[PATCH libinput] evdev: implement support for the MOUSE_WHEEL_CLICK_COUNT property

Hans de Goede hdegoede at redhat.com
Fri Oct 28 10:57:38 UTC 2016


Hi,

On 28-10-16 07:08, Peter Hutterer wrote:
> Not all mice have a click angle with integer degrees. The new
> MOUSE_WHEEL_CLICK_COUNT property specifies how many clicks per full rotation,
> the angle can be calculated from that.
>
> See https://github.com/systemd/systemd/pull/4440 for more information
>
> CLICK_COUNT overrides CLICK_ANGLE, so we check for the former first and then
> fall back to the angle if need be. No changes to the user-facing API.
>
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> ---
>  src/evdev.c                                  | 51 +++++++++++++++---
>  src/libinput-private.h                       |  2 +-
>  src/libinput-util.c                          | 32 ++++++++++++
>  src/libinput-util.h                          |  1 +
>  test/Makefile.am                             |  1 +
>  test/litest-device-mouse-wheel-click-count.c | 77 ++++++++++++++++++++++++++++
>  test/litest.c                                |  2 +
>  test/litest.h                                |  1 +
>  test/misc.c                                  | 30 +++++++++++
>  test/pointer.c                               | 49 +++++++++++++++---
>  10 files changed, 229 insertions(+), 17 deletions(-)
>  create mode 100644 test/litest-device-mouse-wheel-click-count.c
>
> diff --git a/src/evdev.c b/src/evdev.c
> index d49b391..1c46534 100644
> --- a/src/evdev.c
> +++ b/src/evdev.c
> @@ -2008,7 +2008,7 @@ evdev_device_init_pointer_acceleration(struct evdev_device *device,
>  static inline bool
>  evdev_read_wheel_click_prop(struct evdev_device *device,
>  			    const char *prop,
> -			    int *angle)
> +			    double *angle)
>  {
>  	int val;
>
> @@ -2032,18 +2032,53 @@ evdev_read_wheel_click_prop(struct evdev_device *device,
>  	return false;
>  }
>
> +static inline bool
> +evdev_read_wheel_click_count_prop(struct evdev_device *device,
> +				  const char *prop,
> +				  double *angle)
> +{
> +	int val;
> +
> +	prop = udev_device_get_property_value(device->udev_device, prop);
> +	if (!prop)
> +		return false;
> +
> +	val = parse_mouse_wheel_click_angle_property(prop);
> +	if (val) {
> +		*angle = 360.0/val;
> +		return true;
> +	}
> +
> +	log_error(evdev_libinput_context(device),
> +		  "Mouse wheel click count '%s' is present but invalid, "
> +		  "using %d degrees for angle instead instead\n",
> +		  device->devname,
> +		  DEFAULT_WHEEL_CLICK_ANGLE);
> +	*angle = DEFAULT_WHEEL_CLICK_ANGLE;
> +
> +	return false;
> +}
> +

This is almost a 100% copy of evdev_read_wheel_click_prop
how about giving evdev_read_wheel_click_prop an extra
"bool count" argument and then doing:

	if (val) {
		if (count)
			*angle = 360.0 / val;
		else
			*angle = val;
		return true;
	}

Then we do not need the almost identical function.

>  static inline struct wheel_angle
>  evdev_read_wheel_click_props(struct evdev_device *device)
>  {
>  	struct wheel_angle angles;
>
> -	evdev_read_wheel_click_prop(device,
> -				    "MOUSE_WHEEL_CLICK_ANGLE",
> -				    &angles.x);
> -	if (!evdev_read_wheel_click_prop(device,
> -					 "MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL",
> -					 &angles.y))
> -		angles.y = angles.x;
> +	/* CLICK_COUNT overrides CLICK_ANGLE */
> +	if (!evdev_read_wheel_click_count_prop(device,
> +					      "MOUSE_WHEEL_CLICK_COUNT",
> +					      &angles.x))
> +		evdev_read_wheel_click_prop(device,
> +					    "MOUSE_WHEEL_CLICK_ANGLE",
> +					    &angles.x);
> +	if (!evdev_read_wheel_click_count_prop(device,
> +					      "MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL",
> +					      &angles.y)) {
> +		if (!evdev_read_wheel_click_prop(device,
> +						 "MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL",
> +						 &angles.y))
> +			angles.y = angles.x;
> +	}
>
>  	return angles;
>  }
> diff --git a/src/libinput-private.h b/src/libinput-private.h
> index 28656e0..2044cdd 100644
> --- a/src/libinput-private.h
> +++ b/src/libinput-private.h
> @@ -73,7 +73,7 @@ struct normalized_range_coords {
>
>  /* A pair of angles in degrees */
>  struct wheel_angle {
> -	int x, y;
> +	double x, y;
>  };
>
>  /* A pair of angles in degrees */
> diff --git a/src/libinput-util.c b/src/libinput-util.c
> index 4b90fbb..6c051c3 100644
> --- a/src/libinput-util.c
> +++ b/src/libinput-util.c
> @@ -176,6 +176,38 @@ parse_mouse_dpi_property(const char *prop)
>  }
>
>  /**
> + * Helper function to parse the MOUSE_WHEEL_CLICK_COUNT property from udev.
> + * Property is of the form:
> + * MOUSE_WHEEL_CLICK_COUNT=<integer>
> + * Where the number indicates the number of wheel clicks per 360 deg
> + * rotation.
> + *
> + * We skip preceding whitespaces and parse the first number seen. If
> + * multiple numbers are specified, we ignore those.
> + *
> + * @param prop The value of the udev property (without the MOUSE_WHEEL_CLICK_COUNT=)
> + * @return The click count of the wheel (may be negative) or 0 on error.
> + */
> +int
> +parse_mouse_wheel_click_count_property(const char *prop)
> +{
> +	int count = 0,
> +	    nread = 0;
> +
> +	while(*prop != 0 && *prop == ' ')
> +		prop++;
> +
> +	sscanf(prop, "%d%n", &count, &nread);
> +	if (nread == 0 || count == 0 || abs(count) > 360)
> +		return 0;
> +	if (prop[nread] != ' ' && prop[nread] != '\0')
> +		return 0;
> +
> +        return count;
> +}
> +
> +/**
> + *

This is a 1:1 copy off parse_mouse_wheel_click_angle_property (with
s/angle/count/ but that does not change the functionality), please drop.

>   * Helper function to parse the MOUSE_WHEEL_CLICK_ANGLE property from udev.
>   * Property is of the form:
>   * MOUSE_WHEEL_CLICK_ANGLE=<integer>
> diff --git a/src/libinput-util.h b/src/libinput-util.h
> index e31860d..b7bef80 100644
> --- a/src/libinput-util.h
> +++ b/src/libinput-util.h
> @@ -370,6 +370,7 @@ enum ratelimit_state ratelimit_test(struct ratelimit *r);
>
>  int parse_mouse_dpi_property(const char *prop);
>  int parse_mouse_wheel_click_angle_property(const char *prop);
> +int parse_mouse_wheel_click_count_property(const char *prop);
>  double parse_trackpoint_accel_property(const char *prop);
>  bool parse_dimension_property(const char *prop, size_t *width, size_t *height);
>
> diff --git a/test/Makefile.am b/test/Makefile.am
> index 7ff12e4..f4a9252 100644
> --- a/test/Makefile.am
> +++ b/test/Makefile.am
> @@ -35,6 +35,7 @@ liblitest_la_SOURCES = \
>  	litest-device-mouse-roccat.c \
>  	litest-device-mouse-low-dpi.c \
>  	litest-device-mouse-wheel-click-angle.c \
> +	litest-device-mouse-wheel-click-count.c \
>  	litest-device-ms-surface-cover.c \
>  	litest-device-protocol-a-touch-screen.c \
>  	litest-device-qemu-usb-tablet.c \
> diff --git a/test/litest-device-mouse-wheel-click-count.c b/test/litest-device-mouse-wheel-click-count.c
> new file mode 100644
> index 0000000..419b702
> --- /dev/null
> +++ b/test/litest-device-mouse-wheel-click-count.c
> @@ -0,0 +1,77 @@
> +/*
> + * Copyright © 2016 Red Hat, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> + * DEALINGS IN THE SOFTWARE.
> + */
> +
> +#if HAVE_CONFIG_H
> +#include "config.h"
> +#endif
> +
> +#include "litest.h"
> +#include "litest-int.h"
> +
> +static void litest_mouse_setup(void)
> +{
> +	struct litest_device *d = litest_create_device(LITEST_MOUSE_WHEEL_CLICK_COUNT);
> +	litest_set_current_device(d);
> +}
> +
> +static struct input_id input_id = {
> +	.bustype = 0x3,
> +	.vendor = 0x1234,
> +	.product = 0x5678,
> +};
> +
> +static int events[] = {
> +	EV_KEY, BTN_LEFT,
> +	EV_KEY, BTN_RIGHT,
> +	EV_KEY, BTN_MIDDLE,
> +	EV_REL, REL_X,
> +	EV_REL, REL_Y,
> +	EV_REL, REL_WHEEL,
> +	-1 , -1,
> +};
> +
> +static const char udev_rule[] =
> +"ACTION==\"remove\", GOTO=\"wheel_click_count_end\"\n"
> +"KERNEL!=\"event*\", GOTO=\"wheel_click_count_end\"\n"
> +"\n"
> +"ATTRS{name}==\"litest Wheel Click Count Mouse*\",\\\n"
> +"    ENV{MOUSE_WHEEL_CLICK_ANGLE}=\"-15\",\n"
> +"    ENV{MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL}=\"13\",\n\\"
> +"    ENV{MOUSE_WHEEL_CLICK_COUNT}=\"-14\",\n"
> +"    ENV{MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL}=\"27\"\\\n"
> +"\n"
> +"LABEL=\"wheel_click_count_end\"";
> +
> +struct litest_test_device litest_mouse_wheel_click_count_device = {
> +	.type = LITEST_MOUSE_WHEEL_CLICK_COUNT,
> +	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
> +	.shortname = "mouse-wheelclickcount",
> +	.setup = litest_mouse_setup,
> +	.interface = NULL,
> +
> +	.name = "Wheel Click Count Mouse",
> +	.id = &input_id,
> +	.absinfo = NULL,
> +	.events = events,
> +	.udev_rule = udev_rule,
> +};
> diff --git a/test/litest.c b/test/litest.c
> index fd62d21..515eb18 100644
> --- a/test/litest.c
> +++ b/test/litest.c
> @@ -402,6 +402,7 @@ extern struct litest_test_device litest_wacom_cintiq_13hdt_finger_device;
>  extern struct litest_test_device litest_wacom_cintiq_13hdt_pen_device;
>  extern struct litest_test_device litest_wacom_cintiq_13hdt_pad_device;
>  extern struct litest_test_device litest_wacom_hid4800_tablet_device;
> +extern struct litest_test_device litest_mouse_wheel_click_count_device;
>
>  struct litest_test_device* devices[] = {
>  	&litest_synaptics_clickpad_device,
> @@ -458,6 +459,7 @@ struct litest_test_device* devices[] = {
>  	&litest_wacom_cintiq_13hdt_pen_device,
>  	&litest_wacom_cintiq_13hdt_pad_device,
>  	&litest_wacom_hid4800_tablet_device,
> +	&litest_mouse_wheel_click_count_device,
>  	NULL,
>  };
>
> diff --git a/test/litest.h b/test/litest.h
> index 4602355..d52ebd2 100644
> --- a/test/litest.h
> +++ b/test/litest.h
> @@ -224,6 +224,7 @@ enum litest_device_type {
>  	LITEST_WACOM_CINTIQ_13HDT_PAD,
>  	LITEST_WACOM_CINTIQ_13HDT_FINGER,
>  	LITEST_WACOM_HID4800_PEN,
> +	LITEST_MOUSE_WHEEL_CLICK_COUNT,
>  };
>
>  enum litest_device_feature {
> diff --git a/test/misc.c b/test/misc.c
> index 582d4fc..4afd4d0 100644
> --- a/test/misc.c
> +++ b/test/misc.c
> @@ -750,6 +750,35 @@ START_TEST(wheel_click_parser)
>  }
>  END_TEST
>
> +START_TEST(wheel_click_count_parser)
> +{
> +	struct parser_test tests[] = {
> +		{ "1", 1 },
> +		{ "10", 10 },
> +		{ "-12", -12 },
> +		{ "360", 360 },
> +		{ "66 ", 66 },
> +		{ "   100 ", 100 },
> +
> +		{ "0", 0 },
> +		{ "-0", 0 },
> +		{ "a", 0 },
> +		{ "10a", 0 },
> +		{ "10-", 0 },
> +		{ "sadfasfd", 0 },
> +		{ "361", 0 },
> +		{ NULL, 0 }
> +	};
> +
> +	int i, angle;
> +
> +	for (i = 0; tests[i].tag != NULL; i++) {
> +		angle = parse_mouse_wheel_click_count_property(tests[i].tag);
> +		ck_assert_int_eq(angle, tests[i].expected_value);
> +	}
> +}
> +END_TEST
> +
>  struct parser_test_float {
>  	char *tag;
>  	double expected_value;
> @@ -956,6 +985,7 @@ litest_setup_tests_misc(void)
>  	litest_add_no_device("misc:ratelimit", ratelimit_helpers);
>  	litest_add_no_device("misc:parser", dpi_parser);
>  	litest_add_no_device("misc:parser", wheel_click_parser);
> +	litest_add_no_device("misc:parser", wheel_click_count_parser);
>  	litest_add_no_device("misc:parser", trackpoint_accel_parser);
>  	litest_add_no_device("misc:parser", dimension_prop_parser);
>  	litest_add_no_device("misc:time", time_conversion);
> diff --git a/test/pointer.c b/test/pointer.c
> index 175cb3b..4f33de5 100644
> --- a/test/pointer.c
> +++ b/test/pointer.c
> @@ -473,14 +473,45 @@ START_TEST(pointer_button_auto_release)
>  }
>  END_TEST
>
> -static inline int
> +static inline double
> +wheel_click_count(struct litest_device *dev, int which)
> +{
> +	struct udev_device *d;
> +	const char *prop = NULL;
> +	int count;
> +	double angle = 0.0;
> +
> +	d = libinput_device_get_udev_device(dev->libinput_device);
> +	litest_assert_ptr_notnull(d);
> +
> +	if (which == REL_HWHEEL)
> +		prop = udev_device_get_property_value(d, "MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL");
> +	if(!prop)
> +		prop = udev_device_get_property_value(d, "MOUSE_WHEEL_CLICK_COUNT");
> +	if (!prop)
> +		goto out;
> +
> +	count = parse_mouse_wheel_click_count_property(prop);
> +	angle = 360.0/count;
> +
> +out:
> +	udev_device_unref(d);
> +	return angle;
> +}
> +
> +static inline double
>  wheel_click_angle(struct litest_device *dev, int which)
>  {
>  	struct udev_device *d;
>  	const char *prop = NULL;
>  	const int default_angle = 15;
> -	int angle = default_angle;
> +	double angle;
>
> +	angle = wheel_click_count(dev, which);
> +	if (angle != 0.0)
> +		return angle;
> +
> +	angle = default_angle;
>  	d = libinput_device_get_udev_device(dev->libinput_device);
>  	litest_assert_ptr_notnull(d);
>
> @@ -492,7 +523,7 @@ wheel_click_angle(struct litest_device *dev, int which)
>  		goto out;
>
>  	angle = parse_mouse_wheel_click_angle_property(prop);
> -	if (angle == 0)
> +	if (angle == 0.0)
>  		angle = default_angle;
>
>  out:
> @@ -508,7 +539,7 @@ test_wheel_event(struct litest_device *dev, int which, int amount)
>  	struct libinput_event_pointer *ptrev;
>  	enum libinput_pointer_axis axis;
>
> -	int scroll_step, expected, discrete;;
> +	double scroll_step, expected, discrete;
>
>  	scroll_step = wheel_click_angle(dev, which);
>  	expected = amount * scroll_step;
> @@ -535,10 +566,12 @@ test_wheel_event(struct litest_device *dev, int which, int amount)
>  				     axis,
>  				     LIBINPUT_POINTER_AXIS_SOURCE_WHEEL);
>
> -	litest_assert_int_eq(libinput_event_pointer_get_axis_value(ptrev, axis),
> -			 expected);
> -	litest_assert_int_eq(libinput_event_pointer_get_axis_value_discrete(ptrev, axis),
> -			     discrete);
> +	litest_assert_double_eq(
> +			libinput_event_pointer_get_axis_value(ptrev, axis),
> +			expected);
> +	litest_assert_double_eq(
> +			libinput_event_pointer_get_axis_value_discrete(ptrev, axis),
> +			discrete);
>  	libinput_event_destroy(event);
>  }
>
>


Regards,

Hans


More information about the wayland-devel mailing list