evdev: implement support for the MOUSE_WHEEL_CLICK_COUNT property

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@who-t.net>
This commit is contained in:
Peter Hutterer 2016-10-28 15:08:32 +10:00
parent e0ccfc87f0
commit 58c7a9cbf0
10 changed files with 229 additions and 17 deletions

View file

@ -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;
}
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;
}

View file

@ -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 */

View file

@ -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;
}
/**
*
* Helper function to parse the MOUSE_WHEEL_CLICK_ANGLE property from udev.
* Property is of the form:
* MOUSE_WHEEL_CLICK_ANGLE=<integer>

View file

@ -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);

View file

@ -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 \

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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 {

View file

@ -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);

View file

@ -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);
}