mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-03-24 07:50:35 +01:00
Compare commits
11 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e48547047 | ||
|
|
9835acf7c7 | ||
|
|
2d2b38aec4 | ||
|
|
8e6d3301b8 | ||
|
|
5181be6cbc | ||
|
|
ed4d674ca2 | ||
|
|
bfff1ab3ef | ||
|
|
84c12a8a63 | ||
|
|
690dd44121 | ||
|
|
59d1ed9466 | ||
|
|
67a875d752 |
11 changed files with 344 additions and 53 deletions
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
46
src/evdev.c
46
src/evdev.c
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
95
test/misc.c
95
test/misc.c
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue