Compare commits

...

11 commits
main ... 1.5.3

Author SHA1 Message Date
Peter Hutterer
4e48547047 libinput 1.5.3
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-12-07 14:23:46 +10:00
Peter Hutterer
9835acf7c7 touchpad: sync BTN_TOOL_FINGER state on init
The Elantech touchpad on my Asus Vivobook doesn't release BTN_TOOL_FINGER on
up. If the touchpad was used before libinput initializes, the kernel filters
the event because its state is already set. We never receive it and keep
ignoring all events until the first switch to BTN_TOOL_DOUBLETAP and back.

On touchpad init sync the BTN_TOOL_FINGER state and set it accordingly. This
is the only event that can be legitimately down on init. We don't care about
BTN_TOUCH because ignoring an ongoing touch on init is generally a good idea
and we can ignore any multifinger gesture as well.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
(cherry picked from commit 5552a6f145)
2016-12-06 17:39:04 +10:00
Peter Hutterer
2d2b38aec4 touchpad: add a quirk for the HP Pavilion dm4
This touchpad has cursor jumps for 2-finger scrolling that also affects the
single-finger emulation. So disable any multitouch bits on this device and
disallow the 2-finger scroll method. This still allows for 2-finger
tapping/clicking.

https://bugs.freedesktop.org/show_bug.cgi?id=91135

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
(cherry picked from commit 996b845d68)
2016-12-06 17:39:04 +10:00
Peter Hutterer
8e6d3301b8 evdev: use safe_atod to convert the matrix values
Avoids parsing issues when we're in different locales

https://bugs.freedesktop.org/show_bug.cgi?id=98828

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
(cherry picked from commit 98793f6b43)
2016-12-06 17:39:04 +10:00
Peter Hutterer
5181be6cbc util: add a helper function to split a string into substrings
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
(cherry picked from commit 33100fe88d)
2016-12-06 17:39:04 +10:00
Peter Hutterer
ed4d674ca2 util: add safe_atod for locale-independent conversion
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
(cherry picked from commit d6020d7ab2)
2016-12-06 17:39:04 +10:00
Peter Hutterer
bfff1ab3ef path: read the calibration prop on startup
We were reading this property in the udev backend, but not in the path
backend.

Reported-by: Thomas Olszak <olszak.tomasz@gmail.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
(cherry picked from commit 21f0e2e3b7)
2016-12-06 17:39:03 +10:00
Peter Hutterer
84c12a8a63 evdev: move reading the calibration prop into a helper function
No functional changes.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
(cherry picked from commit 50e4a1fada)
2016-12-06 17:39:03 +10:00
Peter Hutterer
690dd44121 util: if errno is nonzero, exit early from safe_atoi
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
(cherry picked from commit 13428f5d82)
2016-12-06 17:39:03 +10:00
Peter Hutterer
59d1ed9466 util: fix include order
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
(cherry picked from commit 4a5dcbf69b)
2016-12-06 17:39:03 +10:00
Peter Hutterer
67a875d752 util: fix indentation for safe_atoi
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
(cherry picked from commit 3168d86223)
2016-12-06 17:39:03 +10:00
11 changed files with 344 additions and 53 deletions

View file

@ -2,7 +2,7 @@ AC_PREREQ([2.64])
m4_define([libinput_major_version], [1])
m4_define([libinput_minor_version], [5])
m4_define([libinput_micro_version], [2])
m4_define([libinput_micro_version], [3])
m4_define([libinput_version],
[libinput_major_version.libinput_minor_version.libinput_micro_version])
@ -35,7 +35,7 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
# b) If interfaces have been changed or added, but binary compatibility has
# been preserved, change to C+1:0:A+1
# c) If the interface is the same as the previous version, change to C:R+1:A
LIBINPUT_LT_VERSION=20:3:10
LIBINPUT_LT_VERSION=20:4:10
AC_SUBST(LIBINPUT_LT_VERSION)
AM_SILENT_RULES([yes])

View file

@ -1795,8 +1795,12 @@ tp_init_slots(struct tp_dispatch *tp,
* If three fingers are set down in the same frame, one slot has the
* coordinates 0/0 and may not get updated for several frames.
* See https://bugzilla.redhat.com/show_bug.cgi?id=1295073
*
* The HP Pavilion DM4 touchpad has random jumps in slots, including
* for single-finger movement. See fdo bug 91135
*/
if (tp->semi_mt) {
if (tp->semi_mt ||
device->model_flags & EVDEV_MODEL_HP_PAVILION_DM4_TOUCHPAD) {
tp->num_slots = 1;
tp->slot = 0;
tp->has_mt = false;
@ -1825,6 +1829,14 @@ tp_init_slots(struct tp_dispatch *tp,
for (i = 1; i < tp->num_slots; i++)
tp_sync_touch(tp, device, &tp->touches[i], i);
/* Some touchpads don't reset BTN_TOOL_FINGER on touch up and only
* change to/from it when BTN_TOOL_DOUBLETAP is set. This causes us
* to ignore the first touches events until a two-finger gesture is
* performed.
*/
if (libevdev_get_event_value(device->evdev, EV_KEY, BTN_TOOL_FINGER))
tp_fake_finger_set(tp, BTN_TOOL_FINGER, 1);
return true;
}
@ -1899,6 +1911,12 @@ tp_scroll_get_methods(struct tp_dispatch *tp)
{
uint32_t methods = LIBINPUT_CONFIG_SCROLL_EDGE;
/* Any movement with more than one finger has random cursor
* jumps. Don't allow for 2fg scrolling on this device, see
* fdo bug 91135 */
if (tp->device->model_flags & EVDEV_MODEL_HP_PAVILION_DM4_TOUCHPAD)
return LIBINPUT_CONFIG_SCROLL_EDGE;
if (tp->ntouches >= 2)
methods |= LIBINPUT_CONFIG_SCROLL_2FG;

View file

@ -2180,6 +2180,7 @@ evdev_read_model_flags(struct evdev_device *device)
MODEL(HP8510_TOUCHPAD),
MODEL(HP6910_TOUCHPAD),
MODEL(HP_ZBOOK_STUDIO_G3),
MODEL(HP_PAVILION_DM4_TOUCHPAD),
#undef MODEL
{ "ID_INPUT_TRACKBALL", EVDEV_MODEL_TRACKBALL },
{ NULL, EVDEV_MODEL_DEFAULT },
@ -2975,6 +2976,51 @@ evdev_device_calibrate(struct evdev_device *device,
matrix_mult(&device->abs.calibration, &transform, &scale);
}
void
evdev_read_calibration_prop(struct evdev_device *device)
{
const char *calibration_values;
float calibration[6];
int idx;
char **strv;
calibration_values =
udev_device_get_property_value(device->udev_device,
"LIBINPUT_CALIBRATION_MATRIX");
if (calibration_values == NULL)
return;
if (!device->abs.absinfo_x || !device->abs.absinfo_y)
return;
strv = strv_from_string(calibration_values, " ");
if (!strv)
return;
for (idx = 0; idx < 6; idx++) {
double v;
if (strv[idx] == NULL || !safe_atod(strv[idx], &v)) {
strv_free(strv);
return;
}
calibration[idx] = v;
}
strv_free(strv);
evdev_device_set_default_calibration(device, calibration);
log_info(evdev_libinput_context(device),
"Applying calibration: %f %f %f %f %f %f\n",
calibration[0],
calibration[1],
calibration[2],
calibration[3],
calibration[4],
calibration[5]);
}
bool
evdev_device_has_capability(struct evdev_device *device,
enum libinput_device_capability capability)

View file

@ -122,6 +122,7 @@ enum evdev_device_model {
EVDEV_MODEL_HP8510_TOUCHPAD = (1 << 21),
EVDEV_MODEL_HP6910_TOUCHPAD = (1 << 22),
EVDEV_MODEL_HP_ZBOOK_STUDIO_G3 = (1 << 23),
EVDEV_MODEL_HP_PAVILION_DM4_TOUCHPAD = (1 << 24),
};
struct mt_slot {
@ -337,6 +338,9 @@ void
evdev_init_calibration(struct evdev_device *device,
struct libinput_device_config_calibration *calibration);
void
evdev_read_calibration_prop(struct evdev_device *device);
void
evdev_device_init_pointer_acceleration(struct evdev_device *device,
struct motion_filter *filter);

View file

@ -248,21 +248,10 @@ parse_mouse_wheel_click_angle_property(const char *prop)
double
parse_trackpoint_accel_property(const char *prop)
{
locale_t c_locale;
double accel;
char *endp;
/* Create a "C" locale to force strtod to use '.' as separator */
c_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
if (c_locale == (locale_t)0)
return 0.0;
accel = strtod_l(prop, &endp, c_locale);
freelocale(c_locale);
if (*endp != '\0')
return 0.0;
if (!safe_atod(prop, &accel))
accel = 0.0;
return accel;
}
@ -296,3 +285,85 @@ parse_dimension_property(const char *prop, size_t *w, size_t *h)
*h = (size_t)y;
return true;
}
/**
* Return the next word in a string pointed to by state before the first
* separator character. Call repeatedly to tokenize a whole string.
*
* @param state Current state
* @param len String length of the word returned
* @param separators List of separator characters
*
* @return The first word in *state, NOT null-terminated
*/
static const char *
next_word(const char **state, size_t *len, const char *separators)
{
const char *next = *state;
size_t l;
if (!*next)
return NULL;
next += strspn(next, separators);
if (!*next) {
*state = next;
return NULL;
}
l = strcspn(next, separators);
*state = next + l;
*len = l;
return next;
}
/**
* Return a null-terminated string array with the tokens in the input
* string, e.g. "one two\tthree" with a separator list of " \t" will return
* an array [ "one", "two", "three", NULL ].
*
* Use strv_free() to free the array.
*
* @param in Input string
* @param separators List of separator characters
*
* @return A null-terminated string array or NULL on errors
*/
char **
strv_from_string(const char *in, const char *separators)
{
const char *s, *word;
char **strv = NULL;
int nelems = 0, idx;
size_t l;
assert(in != NULL);
s = in;
while ((word = next_word(&s, &l, separators)) != NULL)
nelems++;
if (nelems == 0)
return NULL;
nelems++; /* NULL-terminated */
strv = zalloc(nelems * sizeof *strv);
if (!strv)
return NULL;
idx = 0;
s = in;
while ((word = next_word(&s, &l, separators)) != NULL) {
char *copy = strndup(word, l);
if (!copy) {
strv_free(strv);
return NULL;
}
strv[idx++] = copy;
}
return strv;
}

View file

@ -28,14 +28,16 @@
#include "config.h"
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "libinput.h"
@ -407,20 +409,69 @@ us2ms(uint64_t us)
static inline bool
safe_atoi(const char *str, int *val)
{
char *endptr;
long v;
char *endptr;
long v;
v = strtol(str, &endptr, 10);
if (str == endptr)
return false;
if (*str != '\0' && *endptr != '\0')
return false;
errno = 0;
v = strtol(str, &endptr, 10);
if (errno > 0)
return false;
if (str == endptr)
return false;
if (*str != '\0' && *endptr != '\0')
return false;
if (v > INT_MAX || v < INT_MIN)
return false;
if (v > INT_MAX || v < INT_MIN)
return false;
*val = v;
return true;
*val = v;
return true;
}
static inline bool
safe_atod(const char *str, double *val)
{
char *endptr;
double v;
locale_t c_locale;
/* Create a "C" locale to force strtod to use '.' as separator */
c_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
if (c_locale == (locale_t)0)
return false;
errno = 0;
v = strtod_l(str, &endptr, c_locale);
freelocale(c_locale);
if (errno > 0)
return false;
if (str == endptr)
return false;
if (*str != '\0' && *endptr != '\0')
return false;
if (isnan(v) || isinf(v))
return false;
*val = v;
return true;
}
char **strv_from_string(const char *string, const char *separator);
static inline void
strv_free(char **strv) {
char **s = strv;
if (!strv)
return;
while (*s != NULL) {
free(*s);
*s = (char*)0x1; /* detect use-after-free */
s++;
}
free (strv);
}
#endif /* LIBINPUT_UTIL_H */

View file

@ -171,6 +171,8 @@ path_device_enable(struct path_input *input,
goto out;
}
evdev_read_calibration_prop(device);
out:
free(seat_name);
free(seat_logical_name);

View file

@ -51,8 +51,6 @@ device_added(struct udev_device *udev_device,
struct evdev_device *device;
const char *devnode;
const char *device_seat, *output_name;
const char *calibration_values;
float calibration[6];
struct udev_seat *seat;
device_seat = udev_device_get_property_value(udev_device, "ID_SEAT");
@ -94,29 +92,7 @@ device_added(struct udev_device *udev_device,
return 0;
}
calibration_values =
udev_device_get_property_value(udev_device,
"LIBINPUT_CALIBRATION_MATRIX");
if (device->abs.absinfo_x && device->abs.absinfo_y &&
calibration_values && sscanf(calibration_values,
"%f %f %f %f %f %f",
&calibration[0],
&calibration[1],
&calibration[2],
&calibration[3],
&calibration[4],
&calibration[5]) == 6) {
evdev_device_set_default_calibration(device, calibration);
log_info(&input->base,
"Applying calibration: %f %f %f %f %f %f\n",
calibration[0],
calibration[1],
calibration[2],
calibration[3],
calibration[4],
calibration[5]);
}
evdev_read_calibration_prop(device);
output_name = udev_device_get_property_value(udev_device, "WL_OUTPUT");
if (output_name)

View file

@ -861,6 +861,99 @@ START_TEST(time_conversion)
}
END_TEST
struct atod_test {
char *str;
bool success;
double val;
};
START_TEST(safe_atod_test)
{
struct atod_test tests[] = {
{ "10", true, 10 },
{ "20", true, 20 },
{ "-1", true, -1 },
{ "2147483647", true, 2147483647 },
{ "-2147483648", true, -2147483648 },
{ "4294967295", true, 4294967295 },
{ "0x0", true, 0 },
{ "0x10", true, 0x10 },
{ "0xaf", true, 0xaf },
{ "x80", false, 0 },
{ "0.0", true, 0.0 },
{ "0.1", true, 0.1 },
{ "1.2", true, 1.2 },
{ "-324.9", true, -324.9 },
{ "9324.9", true, 9324.9 },
{ "NAN", false, 0 },
{ "INFINITY", false, 0 },
{ "-10x10", false, 0 },
{ "1x-99", false, 0 },
{ "", false, 0 },
{ "abd", false, 0 },
{ "xabd", false, 0 },
{ "0x0x", false, 0 },
{ NULL, false, 0 }
};
double v;
bool success;
for (int i = 0; tests[i].str != NULL; i++) {
v = 0xad;
success = safe_atod(tests[i].str, &v);
ck_assert(success == tests[i].success);
if (success)
ck_assert_int_eq(v, tests[i].val);
else
ck_assert_int_eq(v, 0xad);
}
}
END_TEST
struct strsplit_test {
const char *string;
const char *delim;
const char *results[10];
};
START_TEST(strsplit_test)
{
struct strsplit_test tests[] = {
{ "one two three", " ", { "one", "two", "three", NULL } },
{ "one", " ", { "one", NULL } },
{ "one two ", " ", { "one", "two", NULL } },
{ "one two", " ", { "one", "two", NULL } },
{ " one two", " ", { "one", "two", NULL } },
{ "one", "\t \r", { "one", NULL } },
{ "one two three", " t", { "one", "wo", "hree", NULL } },
{ " one two three", "te", { " on", " ", "wo ", "hr", NULL } },
{ "one", "ne", { "o", NULL } },
{ "onene", "ne", { "o", NULL } },
{ NULL, NULL, { NULL }}
};
struct strsplit_test *t = tests;
while (t->string) {
char **strv;
int idx = 0;
strv = strv_from_string(t->string, t->delim);
while (t->results[idx]) {
ck_assert_str_eq(t->results[idx], strv[idx]);
idx++;
}
ck_assert_ptr_eq(strv[idx], NULL);
strv_free(strv);
t++;
}
/* Special cases */
ck_assert_ptr_eq(strv_from_string("", " "), NULL);
ck_assert_ptr_eq(strv_from_string(" ", " "), NULL);
ck_assert_ptr_eq(strv_from_string(" ", " "), NULL);
ck_assert_ptr_eq(strv_from_string("oneoneone", "one"), NULL);
}
END_TEST
static int open_restricted_leak(const char *path, int flags, void *data)
{
return *(int*)data;
@ -988,6 +1081,8 @@ litest_setup_tests_misc(void)
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:parser", safe_atod_test);
litest_add_no_device("misc:parser", strsplit_test);
litest_add_no_device("misc:time", time_conversion);
litest_add_no_device("misc:fd", fd_no_event_leak);

View file

@ -4365,6 +4365,29 @@ START_TEST(touchpad_slot_swap)
}
END_TEST
START_TEST(touchpad_finger_always_down)
{
struct litest_device *dev = litest_current_device();
struct libinput *li;
/* Set BTN_TOOL_FINGER before a new context is initialized */
litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
li = litest_create_context();
libinput_path_add_device(li,
libevdev_uinput_get_devnode(dev->uinput));
litest_drain_events(li);
litest_touch_down(dev, 0, 50, 50);
litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
libinput_unref(li);
}
END_TEST
START_TEST(touchpad_time_usec)
{
struct litest_device *dev = litest_current_device();
@ -4742,6 +4765,7 @@ litest_setup_tests_touchpad(void)
litest_add_for_device("touchpad:bugs", touchpad_tool_tripletap_touch_count, LITEST_SYNAPTICS_TOPBUTTONPAD);
litest_add_for_device("touchpad:bugs", touchpad_slot_swap, LITEST_SYNAPTICS_TOPBUTTONPAD);
litest_add_for_device("touchpad:bugs", touchpad_finger_always_down, LITEST_SYNAPTICS_TOPBUTTONPAD);
litest_add("touchpad:time", touchpad_time_usec, LITEST_TOUCHPAD, LITEST_ANY);

View file

@ -99,6 +99,10 @@ libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnHewlett-Packard:*pnHPCompaq6910
libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnHewlett-Packard:*pnHPCompaq8510w*
LIBINPUT_MODEL_HP8510_TOUCHPAD=1
# HP Pavillion dm4
libinput:name:SynPS/2 Synaptics TouchPad:dmi:*svnHewlett-Packard:*pnHPPaviliondm4NotebookPC*
LIBINPUT_MODEL_HP_PAVILION_DM4_TOUCHPAD=1
# HP Stream 11
libinput:name:SYN1EDE:00 06CB:7442:dmi:*svnHewlett-Packard:pnHPStreamNotebookPC11*
LIBINPUT_MODEL_HP_STREAM11_TOUCHPAD=1