Merge branch 'master' into tablet-support

Added: udev-tag detection for the tablet.
libwacom assigns ID_INPUT_TABLET to all known devices but also
ID_INPUT_TOUCHPAD to all known devices with a touch interface. That's a bug
and should be fixed there but we can work around it by checking both and
making sure only one is set.

Conflicts:
	src/evdev.c
	test/misc.c
This commit is contained in:
Peter Hutterer 2015-02-10 15:23:38 +10:00
commit 2365f7d3d1
19 changed files with 669 additions and 190 deletions

View file

@ -22,7 +22,7 @@ For more information, visit:
http://www.freedesktop.org/wiki/Software/libinput/
Bugs can be filed in the libinput component of Wayland:
https://bugs.freedesktop.org/enter_bug.cgi?product=Wayland
https://bugs.freedesktop.org/enter_bug.cgi?product=Wayland&component=libinput
Online API documentation:
http://wayland.freedesktop.org/libinput/doc/latest/modules.html

View file

@ -1,7 +1,7 @@
AC_PREREQ([2.64])
m4_define([libinput_major_version], [0])
m4_define([libinput_minor_version], [9])
m4_define([libinput_minor_version], [10])
m4_define([libinput_micro_version], [0])
m4_define([libinput_version],
[libinput_major_version.libinput_minor_version.libinput_micro_version])
@ -30,10 +30,11 @@ AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
# - If binary compatibility has been broken (eg removed or changed interfaces)
# change to C+1:0:0
# - If the interface is the same as the previous version, change to C:R+1:A
LIBINPUT_LT_VERSION=8:0:1
LIBINPUT_LT_VERSION=8:1:1
AC_SUBST(LIBINPUT_LT_VERSION)
AM_SILENT_RULES([yes])
AC_USE_SYSTEM_EXTENSIONS
# Check for programs
AC_PROG_CC_C99

View file

@ -54,6 +54,13 @@ The movement of a finger can alter the button area behavior:
- once a finger has moved out of the button area, it cannot move back in and
trigger a right or middle button event
On some touchpads, notably the 2015 Lenovo X1 Carbon 3rd series, the very
bottom end of the touchpad is outside of the sensor range but it is possible
to trigger a physical click there. To libinput, the click merely shows up as
a left button click without any positional finger data and it is
impossible to determine whether it is a left or a right click. libinput
ignores such button clicks, this behavior is intentional.
@section clickfinger Clickfinger behavior
This is the default behavior on Apple touchpads.

View file

@ -823,6 +823,7 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time)
break;
case BUTTON_EVENT_IN_TOP_L:
is_top = 1;
/* fallthrough */
case BUTTON_EVENT_IN_BOTTOM_L:
button |= LEFT;
break;
@ -832,6 +833,7 @@ tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time)
break;
case BUTTON_EVENT_IN_TOP_R:
is_top = 1;
/* fallthrough */
case BUTTON_EVENT_IN_BOTTOM_R:
button |= RIGHT;
break;

View file

@ -1107,12 +1107,6 @@ tp_tag_device(struct evdev_device *device,
if (udev_device_get_property_value(udev_device,
"TOUCHPAD_HAS_TRACKPOINT_BUTTONS"))
device->tags |= EVDEV_TAG_TOUCHPAD_TRACKPOINT;
/* Magic version tag: used by the litest device. Should never be set
* in real life but allows us to test for these features without
* requiring custom udev rules during make check */
if (libevdev_get_id_version(device->evdev) == 0xfffa)
device->tags |= EVDEV_TAG_TOUCHPAD_TRACKPOINT;
}
static struct evdev_dispatch_interface tp_interface = {

View file

@ -49,6 +49,37 @@ enum evdev_key_type {
EVDEV_KEY_TYPE_BUTTON,
};
enum evdev_device_udev_tags {
EVDEV_UDEV_TAG_INPUT = (1 << 0),
EVDEV_UDEV_TAG_KEYBOARD = (1 << 1),
EVDEV_UDEV_TAG_MOUSE = (1 << 2),
EVDEV_UDEV_TAG_TOUCHPAD = (1 << 3),
EVDEV_UDEV_TAG_TOUCHSCREEN = (1 << 4),
EVDEV_UDEV_TAG_TABLET = (1 << 5),
EVDEV_UDEV_TAG_JOYSTICK = (1 << 6),
EVDEV_UDEV_TAG_ACCELEROMETER = (1 << 7),
};
struct evdev_udev_tag_match {
const char *name;
enum evdev_device_udev_tags tag;
};
static const struct evdev_udev_tag_match evdev_udev_tag_matches[] = {
{"ID_INPUT", EVDEV_UDEV_TAG_INPUT},
{"ID_INPUT_KEYBOARD", EVDEV_UDEV_TAG_KEYBOARD},
{"ID_INPUT_KEY", EVDEV_UDEV_TAG_KEYBOARD},
{"ID_INPUT_MOUSE", EVDEV_UDEV_TAG_MOUSE},
{"ID_INPUT_TOUCHPAD", EVDEV_UDEV_TAG_TOUCHPAD},
{"ID_INPUT_TOUCHSCREEN", EVDEV_UDEV_TAG_TOUCHSCREEN},
{"ID_INPUT_TABLET", EVDEV_UDEV_TAG_TABLET},
{"ID_INPUT_JOYSTICK", EVDEV_UDEV_TAG_JOYSTICK},
{"ID_INPUT_ACCELEROMETER", EVDEV_UDEV_TAG_ACCELEROMETER},
/* sentinel value */
{ 0 },
};
static void
hw_set_key_down(struct evdev_device *device, int code, int pressed)
{
@ -1229,10 +1260,12 @@ evdev_device_init_pointer_acceleration(struct evdev_device *device)
device->pointer.config.get_default_speed = evdev_accel_config_get_default_speed;
device->base.config.accel = &device->pointer.config;
evdev_accel_config_set_speed(&device->base,
evdev_accel_config_get_default_speed(&device->base));
return 0;
}
static inline int
evdev_need_mtdev(struct evdev_device *device)
{
@ -1317,38 +1350,62 @@ evdev_fix_abs_resolution(struct libevdev *evdev,
}
}
static enum evdev_device_udev_tags
evdev_device_get_udev_tags(struct evdev_device *device,
struct udev_device *udev_device)
{
const char *prop;
enum evdev_device_udev_tags tags = 0;
const struct evdev_udev_tag_match *match = evdev_udev_tag_matches;
while (match->name) {
prop = udev_device_get_property_value(device->udev_device,
match->name);
if (prop)
tags |= match->tag;
match++;
}
return tags;
}
static int
evdev_configure_device(struct evdev_device *device)
{
struct libinput *libinput = device->base.seat->libinput;
struct libevdev *evdev = device->evdev;
const struct input_absinfo *absinfo;
int has_abs, has_rel, has_mt;
int has_button, has_keyboard, has_touch, has_joystick_button;
struct mt_slot *slots;
int num_slots;
int active_slot;
int slot;
unsigned int i;
const char *devnode = udev_device_get_devnode(device->udev_device);
enum evdev_device_udev_tags udev_tags;
has_rel = 0;
has_abs = 0;
has_mt = 0;
has_button = 0;
has_joystick_button = 0;
has_keyboard = 0;
has_touch = 0;
udev_tags = evdev_device_get_udev_tags(device, device->udev_device);
for (i = BTN_JOYSTICK; i <= BTN_PINKIE; i++)
if (libevdev_has_event_code(evdev, EV_KEY, i))
has_joystick_button = 1;
if ((udev_tags & EVDEV_UDEV_TAG_INPUT) == 0 ||
(udev_tags & ~EVDEV_UDEV_TAG_INPUT) == 0) {
log_info(libinput,
"input device '%s', %s not tagged as input device\n",
device->devname, devnode);
return -1;
}
for (i = BTN_GAMEPAD; i <= BTN_TR2; i++)
if (libevdev_has_event_code(evdev, EV_KEY, i))
has_joystick_button = 1;
log_info(libinput,
"input device '%s', %s is tagged by udev as:%s%s%s%s%s%s\n",
device->devname, devnode,
udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "",
udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "",
udev_tags & EVDEV_UDEV_TAG_TOUCHPAD ? " Touchpad" : "",
udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN ? " Touchscreen" : "",
udev_tags & EVDEV_UDEV_TAG_TABLET ? " Tablet" : "",
udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "",
udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : "");
if (has_joystick_button) {
/* libwacom *adds* TABLET, TOUCHPAD but leaves JOYSTICK in place, so
make sure we only ignore real joystick devices */
if ((udev_tags & EVDEV_UDEV_TAG_JOYSTICK) == udev_tags) {
log_info(libinput,
"input device '%s', %s is a joystick, ignoring\n",
device->devname, devnode);
@ -1363,7 +1420,6 @@ evdev_configure_device(struct evdev_device *device)
absinfo))
device->abs.fake_resolution = 1;
device->abs.absinfo_x = absinfo;
has_abs = 1;
}
if ((absinfo = libevdev_get_abs_info(evdev, ABS_Y))) {
if (evdev_fix_abs_resolution(evdev,
@ -1371,7 +1427,6 @@ evdev_configure_device(struct evdev_device *device)
absinfo))
device->abs.fake_resolution = 1;
device->abs.absinfo_y = absinfo;
has_abs = 1;
}
/* Fake MT devices have the ABS_MT_SLOT bit set because of
@ -1379,8 +1434,7 @@ evdev_configure_device(struct evdev_device *device)
just have too many ABS_ axes */
if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT) &&
libevdev_get_num_slots(evdev) == -1) {
has_mt = 0;
has_touch = 0;
udev_tags &= ~EVDEV_UDEV_TAG_TOUCHSCREEN;
} else if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) &&
libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y)) {
absinfo = libevdev_get_abs_info(evdev, ABS_MT_POSITION_X);
@ -1397,8 +1451,6 @@ evdev_configure_device(struct evdev_device *device)
device->abs.fake_resolution = 1;
device->abs.absinfo_y = absinfo;
device->is_mt = 1;
has_touch = 1;
has_mt = 1;
/* We only handle the slotted Protocol B in libinput.
Devices with ABS_MT_POSITION_* but not ABS_MT_SLOT
@ -1433,83 +1485,55 @@ evdev_configure_device(struct evdev_device *device)
}
}
if (libevdev_has_event_code(evdev, EV_REL, REL_X) ||
libevdev_has_event_code(evdev, EV_REL, REL_Y))
has_rel = 1;
if (libevdev_has_event_type(evdev, EV_KEY)) {
if (!libevdev_has_property(evdev, INPUT_PROP_DIRECT) &&
libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_FINGER) &&
!libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_PEN) &&
(has_abs || has_mt)) {
device->dispatch = evdev_mt_touchpad_create(device);
log_info(libinput,
"input device '%s', %s is a touchpad\n",
device->devname, devnode);
return device->dispatch == NULL ? -1 : 0;
} else if (!libevdev_has_event_code(device->evdev, EV_KEY, BTN_TOOL_FINGER) &&
libevdev_has_event_code(device->evdev, EV_KEY, BTN_TOOL_PEN) &&
has_abs) {
device->dispatch = evdev_tablet_create(device);
device->seat_caps |= EVDEV_DEVICE_TABLET;
log_info(libinput,
"input device '%s', %s is a tablet\n",
device->devname, devnode);
return device->dispatch == NULL ? -1 : 0;
}
for (i = 0; i < KEY_MAX; i++) {
if (libevdev_has_event_code(evdev, EV_KEY, i)) {
switch (get_key_type(i)) {
case EVDEV_KEY_TYPE_NONE:
break;
case EVDEV_KEY_TYPE_KEY:
has_keyboard = 1;
break;
case EVDEV_KEY_TYPE_BUTTON:
has_button = 1;
break;
}
}
}
if (libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH))
has_touch = 1;
/* libwacom assigns touchpad _and_ tablet to the tablet touch bits,
so make sure we don't initialize the tablet interface for the
touch device */
if ((udev_tags & (EVDEV_UDEV_TAG_TABLET|EVDEV_UDEV_TAG_TOUCHPAD)) ==
EVDEV_UDEV_TAG_TABLET) {
device->dispatch = evdev_tablet_create(device);
device->seat_caps |= EVDEV_DEVICE_TABLET;
log_info(libinput,
"input device '%s', %s is a tablet\n",
device->devname, devnode);
return device->dispatch == NULL ? -1 : 0;
}
if (libevdev_has_event_type(evdev, EV_LED))
has_keyboard = 1;
if ((has_abs || has_rel) && has_button) {
if (evdev_device_init_pointer_acceleration(device) == -1)
if (udev_tags & EVDEV_UDEV_TAG_TOUCHPAD) {
device->dispatch = evdev_mt_touchpad_create(device);
log_info(libinput,
"input device '%s', %s is a touchpad\n",
device->devname, devnode);
return device->dispatch == NULL ? -1 : 0;
}
if (udev_tags & EVDEV_UDEV_TAG_MOUSE) {
if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X) &&
!libevdev_has_event_code(evdev, EV_ABS, ABS_Y) &&
evdev_device_init_pointer_acceleration(device) == -1)
return -1;
device->seat_caps |= EVDEV_DEVICE_POINTER;
log_info(libinput,
"input device '%s', %s is a pointer caps =%s%s%s\n",
device->devname, devnode,
has_abs ? " absolute-motion" : "",
has_rel ? " relative-motion": "",
has_button ? " button" : "");
"input device '%s', %s is a pointer caps\n",
device->devname, devnode);
/* want left-handed config option */
device->left_handed.want_enabled = true;
/* want natural-scroll config option */
device->scroll.natural_scrolling_enabled = true;
}
if (has_rel && has_button) {
/* want button scrolling config option */
device->scroll.want_button = 1;
}
if (has_keyboard) {
if (udev_tags & EVDEV_UDEV_TAG_KEYBOARD) {
device->seat_caps |= EVDEV_DEVICE_KEYBOARD;
log_info(libinput,
"input device '%s', %s is a keyboard\n",
device->devname, devnode);
}
if (has_touch && !has_button) {
if (udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN) {
device->seat_caps |= EVDEV_DEVICE_TOUCH;
log_info(libinput,
"input device '%s', %s is a touch device\n",
@ -1578,6 +1602,7 @@ evdev_device_create(struct libinput_seat *seat,
int fd;
int unhandled_device = 0;
const char *devnode = udev_device_get_devnode(udev_device);
struct libinput_device_group *group;
/* Use non-blocking mode so that we can loop on read on
* evdev_device_data() until all events on the fd are
@ -1648,6 +1673,12 @@ evdev_device_create(struct libinput_seat *seat,
if (!device->source)
goto err;
group = libinput_device_group_create();
if (!group)
goto err;
libinput_device_set_device_group(&device->base, group);
libinput_device_group_unref(group);
list_insert(seat->devices_list.prev, &device->base.link);
evdev_tag_device(device);
@ -2131,6 +2162,9 @@ evdev_device_destroy(struct evdev_device *device)
if (dispatch)
dispatch->interface->destroy(dispatch);
if (device->base.group)
libinput_device_group_unref(device->base.group);
filter_destroy(device->pointer.filter);
libinput_seat_unref(device->base.seat);
libevdev_free(device->evdev);

View file

@ -286,7 +286,7 @@ create_pointer_accelerator_filter(accel_profile_func_t profile)
{
struct pointer_accelerator *filter;
filter = malloc(sizeof *filter);
filter = zalloc(sizeof *filter);
if (filter == NULL)
return NULL;

View file

@ -169,8 +169,14 @@ struct libinput_device_config {
struct libinput_device_config_click_method *click_method;
};
struct libinput_device_group {
int refcount;
void *user_data;
};
struct libinput_device {
struct libinput_seat *seat;
struct libinput_device_group *group;
struct list link;
struct list event_listeners;
void *user_data;
@ -253,6 +259,13 @@ void
libinput_device_init(struct libinput_device *device,
struct libinput_seat *seat);
struct libinput_device_group *
libinput_device_group_create(void);
void
libinput_device_set_device_group(struct libinput_device *device,
struct libinput_device_group *group);
void
libinput_device_add_event_listener(struct libinput_device *device,
struct libinput_event_listener *listener,

View file

@ -715,7 +715,7 @@ libinput_add_fd(struct libinput *libinput,
struct libinput_source *source;
struct epoll_event ep;
source = malloc(sizeof *source);
source = zalloc(sizeof *source);
if (!source)
return NULL;
@ -1603,6 +1603,12 @@ libinput_device_get_context(struct libinput_device *device)
return libinput_seat_get_context(device->seat);
}
LIBINPUT_EXPORT struct libinput_device_group *
libinput_device_get_device_group(struct libinput_device *device)
{
return device->group;
}
LIBINPUT_EXPORT const char *
libinput_device_get_sysname(struct libinput_device *device)
{
@ -1713,6 +1719,64 @@ libinput_event_touch_get_base_event(struct libinput_event_touch *event)
return &event->base;
}
LIBINPUT_EXPORT struct libinput_device_group *
libinput_device_group_ref(struct libinput_device_group *group)
{
group->refcount++;
return group;
}
struct libinput_device_group *
libinput_device_group_create(void)
{
struct libinput_device_group *group;
group = zalloc(sizeof *group);
if (group)
group->refcount = 1;
return group;
}
void
libinput_device_set_device_group(struct libinput_device *device,
struct libinput_device_group *group)
{
device->group = group;
libinput_device_group_ref(group);
}
static void
libinput_device_group_destroy(struct libinput_device_group *group)
{
free(group);
}
LIBINPUT_EXPORT struct libinput_device_group *
libinput_device_group_unref(struct libinput_device_group *group)
{
assert(group->refcount > 0);
group->refcount--;
if (group->refcount == 0) {
libinput_device_group_destroy(group);
return NULL;
} else {
return group;
}
}
LIBINPUT_EXPORT void
libinput_device_group_set_user_data(struct libinput_device_group *group,
void *user_data)
{
group->user_data = user_data;
}
LIBINPUT_EXPORT void *
libinput_device_group_get_user_data(struct libinput_device_group *group)
{
return group->user_data;
}
LIBINPUT_EXPORT const char *
libinput_config_status_to_str(enum libinput_config_status status)
{
@ -1860,7 +1924,8 @@ LIBINPUT_EXPORT enum libinput_config_status
libinput_device_config_accel_set_speed(struct libinput_device *device,
double speed)
{
if (speed < -1.0 || speed > 1.0)
/* Need the negation in case speed is NaN */
if (!(speed >= -1.0 && speed <= 1.0))
return LIBINPUT_CONFIG_STATUS_INVALID;
if (!libinput_device_config_accel_is_available(device))

View file

@ -255,6 +255,16 @@ struct libinput;
*/
struct libinput_device;
/**
* @ingroup device
* @struct libinput_device_group
*
* A base handle for accessing libinput device groups. This struct is
* refcounted, use libinput_device_group_ref() and
* libinput_device_group_unref().
*/
struct libinput_device_group;
/**
* @ingroup seat
* @struct libinput_seat
@ -1686,7 +1696,7 @@ libinput_device_ref(struct libinput_device *device);
* the device correctly to avoid dangling pointers.
*
* @param device A previously obtained device
* @return NULL if device was destroyed, otherwise the passed device
* @return NULL if the device was destroyed, otherwise the passed device
*/
struct libinput_device *
libinput_device_unref(struct libinput_device *device);
@ -1728,6 +1738,66 @@ libinput_device_get_user_data(struct libinput_device *device);
struct libinput *
libinput_device_get_context(struct libinput_device *device);
/**
* @ingroup device
*
* Get the device group this device is assigned to. Some physical
* devices like graphics tablets are represented by multiple kernel
* devices and thus by multiple struct @ref libinput_device.
*
* libinput assigns these devices to the same @ref libinput_device_group
* allowing the caller to identify such devices and adjust configuration
* settings accordingly. For example, setting a tablet to left-handed often
* means turning it upside down. A touch device on the same tablet would
* need to be turned upside down too to work correctly.
*
* All devices are part of a device group though for most devices the group
* will be a singleton. A device is assigned to a device group on @ref
* LIBINPUT_EVENT_DEVICE_ADDED and removed from that group on @ref
* LIBINPUT_EVENT_DEVICE_REMOVED. It is up to the caller to track how many
* devices are in each device group.
*
* @dot
* digraph groups_libinput {
* rankdir="TB";
* node [
* shape="box";
* ]
*
* mouse [ label="mouse"; URL="\ref libinput_device"];
* kbd [ label="keyboard"; URL="\ref libinput_device"];
*
* pen [ label="tablet pen"; URL="\ref libinput_device"];
* touch [ label="tablet touch"; URL="\ref libinput_device"];
* pad [ label="tablet pad"; URL="\ref libinput_device"];
*
* group1 [ label="group 1"; URL="\ref libinput_device_group"];
* group2 [ label="group 2"; URL="\ref libinput_device_group"];
* group3 [ label="group 3"; URL="\ref libinput_device_group"];
*
* mouse -> group1
* kbd -> group2
*
* pen -> group3;
* touch -> group3;
* pad -> group3;
* }
* @enddot
*
* Device groups do not get re-used once the last device in the group was
* removed, i.e. unplugging and re-plugging a physical device with grouped
* devices will return a different device group after every unplug.
*
* The returned device group is not refcounted and may become invalid after
* the next call to libinput. Use libinput_device_group_ref() and
* libinput_device_group_unref() to continue using the handle outside of the
* immediate scope.
*
* @return The device group this device belongs to
*/
struct libinput_device_group *
libinput_device_get_device_group(struct libinput_device *device);
/**
* @ingroup device
*
@ -1837,6 +1907,8 @@ libinput_device_set_seat_logical_name(struct libinput_device *device,
const char *name);
/**
* @ingroup device
*
* Return a udev handle to the device that is this libinput device, if any.
* The returned handle has a refcount of at least 1, the caller must call
* udev_device_unref() once to release the associated resources.
@ -1913,6 +1985,63 @@ libinput_device_get_size(struct libinput_device *device,
int
libinput_device_has_button(struct libinput_device *device, uint32_t code);
/**
* @ingroup device
*
* Increase the refcount of the device group. A device group will be freed
* whenever the refcount reaches 0. This may happen during dispatch if all
* devices of this group were removed from the system. A caller must ensure
* to reference the device group correctly to avoid dangling pointers.
*
* @param group A previously obtained device group
* @return The passed device group
*/
struct libinput_device_group *
libinput_device_group_ref(struct libinput_device_group *group);
/**
* @ingroup device
*
* Decrease the refcount of the device group. A device group will be freed
* whenever the refcount reaches 0. This may happen during dispatch if all
* devices of this group were removed from the system. A caller must ensure
* to reference the device group correctly to avoid dangling pointers.
*
* @param group A previously obtained device group
* @return NULL if the device group was destroyed, otherwise the passed
* device group
*/
struct libinput_device_group *
libinput_device_group_unref(struct libinput_device_group *group);
/**
* @ingroup device
*
* Set caller-specific data associated with this device group. libinput does
* not manage, look at, or modify this data. The caller must ensure the
* data is valid.
*
* @param group A previously obtained device group
* @param user_data Caller-specific data pointer
* @see libinput_device_group_get_user_data
*/
void
libinput_device_group_set_user_data(struct libinput_device_group *group,
void *user_data);
/**
* @ingroup device
*
* Get the caller-specific data associated with this input device group, if
* any.
*
* @param group A previously obtained group
* @return Caller-specific data pointer or NULL if none was set
* @see libinput_device_group_set_user_data
*/
void *
libinput_device_group_get_user_data(struct libinput_device_group *group);
/**
* @defgroup config Device configuration
*

View file

@ -147,3 +147,11 @@ global:
libinput_tool_set_user_data;
libinput_tool_unref;
} LIBINPUT_0.8.0;
LIBINPUT_0.11.0 {
libinput_device_get_device_group;
libinput_device_group_get_user_data;
libinput_device_group_ref;
libinput_device_group_set_user_data;
libinput_device_group_unref;
} LIBINPUT_0.9.0;

View file

@ -291,14 +291,34 @@ libinput_path_create_context(const struct libinput_interface *interface,
}
static inline struct udev_device *
udev_device_from_devnode(struct udev *udev, const char *devnode)
udev_device_from_devnode(struct libinput *libinput,
struct udev *udev,
const char *devnode)
{
struct udev_device *dev;
struct stat st;
size_t count = 0;
if (stat(devnode, &st) < 0)
return NULL;
return udev_device_new_from_devnum(udev, 'c', st.st_rdev);
dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
while (dev && !udev_device_get_is_initialized(dev)) {
udev_device_unref(dev);
msleep(10);
dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
count++;
if (count > 10) {
log_bug_libinput(libinput,
"udev device never initialized (%s)\n",
devnode);
break;
}
}
return dev;
}
LIBINPUT_EXPORT struct libinput_device *
@ -315,7 +335,7 @@ libinput_path_add_device(struct libinput *libinput,
return NULL;
}
udev_device = udev_device_from_devnode(udev, path);
udev_device = udev_device_from_devnode(libinput, udev, path);
if (!udev_device) {
log_bug_client(libinput, "Invalid path %s\n", path);
return NULL;

View file

@ -661,6 +661,54 @@ START_TEST(device_context)
}
END_TEST
START_TEST(device_group_get)
{
struct litest_device *dev = litest_current_device();
struct libinput_device_group *group;
int userdata = 10;
group = libinput_device_get_device_group(dev->libinput_device);
ck_assert_notnull(group);
libinput_device_group_ref(group);
libinput_device_group_set_user_data(group, &userdata);
ck_assert_ptr_eq(&userdata,
libinput_device_group_get_user_data(group));
libinput_device_group_unref(group);
}
END_TEST
START_TEST(device_group_ref)
{
struct libinput *li = litest_create_context();
struct litest_device *dev = litest_add_device(li,
LITEST_MOUSE);
struct libinput_device *device = dev->libinput_device;
struct libinput_device_group *group;
group = libinput_device_get_device_group(device);
ck_assert_notnull(group);
libinput_device_group_ref(group);
libinput_device_ref(device);
litest_drain_events(li);
litest_delete_device(dev);
litest_drain_events(li);
/* make sure the device is dead but the group is still around */
ck_assert(libinput_device_unref(device) == NULL);
libinput_device_group_ref(group);
ck_assert_notnull(libinput_device_group_unref(group));
ck_assert(libinput_device_group_unref(group) == NULL);
libinput_unref(li);
}
END_TEST
int main (int argc, char **argv)
{
litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_TOUCHPAD|LITEST_TABLET);
@ -687,5 +735,8 @@ int main (int argc, char **argv)
litest_add("device:udev", device_get_udev_handle, LITEST_ANY, LITEST_ANY);
litest_add("device:group", device_group_get, LITEST_ANY, LITEST_ANY);
litest_add_no_device("device:group", device_group_ref);
return litest_run(argc, argv);
}

View file

@ -69,6 +69,8 @@ struct litest_test_device {
*/
struct input_absinfo *absinfo;
struct litest_device_interface *interface;
const char *udev_rule;
};
struct litest_device_interface {

View file

@ -65,7 +65,6 @@ static struct input_id input_id = {
.bustype = 0x11,
.vendor = 0x2,
.product = 0x7,
.version = 0xfffa, /* Magic value, used to detect this test device */
};
static int events[] = {
@ -97,6 +96,16 @@ static struct input_absinfo absinfo[] = {
{ .value = -1 }
};
static const char udev_rule[] =
"ACTION==\"remove\", GOTO=\"touchpad_end\"\n"
"KERNEL!=\"event*\", GOTO=\"touchpad_end\"\n"
"ENV{ID_INPUT_TOUCHPAD}==\"\", GOTO=\"touchpad_end\"\n"
"\n"
"ATTRS{name}==\"litest*X1C3rd*\",\\\n"
" ENV{TOUCHPAD_HAS_TRACKPOINT_BUTTONS}=\"1\"\n"
"\n"
"LABEL=\"touchpad_end\"";
struct litest_test_device litest_synaptics_carbon3rd_device = {
.type = LITEST_SYNAPTICS_TRACKPOINT_BUTTONS,
.features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
@ -104,8 +113,9 @@ struct litest_test_device litest_synaptics_carbon3rd_device = {
.setup = litest_synaptics_carbon3rd_setup,
.interface = &interface,
.name = "SynPS/2 Synaptics TouchPad",
.name = "SynPS/2 Synaptics TouchPad X1C3rd",
.id = &input_id,
.events = events,
.absinfo = absinfo,
.udev_rule = udev_rule,
};

View file

@ -26,6 +26,7 @@
#include <assert.h>
#include <check.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
@ -44,6 +45,9 @@
#include "litest-int.h"
#include "libinput-util.h"
#define UDEV_RULES_D "/run/udev/rules.d"
#define UDEV_RULE_PREFIX "99-litest-"
static int in_debugger = -1;
static int verbose = 0;
@ -122,6 +126,55 @@ struct litest_test_device* devices[] = {
static struct list all_tests;
static void
litest_reload_udev_rules(void)
{
system("udevadm control --reload-rules");
}
static int
litest_udev_rule_filter(const struct dirent *entry)
{
return strncmp(entry->d_name,
UDEV_RULE_PREFIX,
strlen(UDEV_RULE_PREFIX)) == 0;
}
static void
litest_drop_udev_rules(void)
{
int n;
int rc;
struct dirent **entries;
char path[PATH_MAX];
n = scandir(UDEV_RULES_D,
&entries,
litest_udev_rule_filter,
alphasort);
if (n < 0)
return;
while (n--) {
rc = snprintf(path, sizeof(path),
"%s/%s",
UDEV_RULES_D,
entries[n]->d_name);
if (rc > 0 &&
(size_t)rc == strlen(UDEV_RULES_D) +
strlen(entries[n]->d_name) + 1)
unlink(path);
else
fprintf(stderr,
"Failed to delete %s. Remaining tests are unreliable\n",
entries[n]->d_name);
free(entries[n]);
}
free(entries);
litest_reload_udev_rules();
}
static void
litest_add_tcase_for_device(struct suite *suite,
void *func,
@ -142,6 +195,13 @@ litest_add_tcase_for_device(struct suite *suite,
t->name = strdup(test_name);
t->tc = tcase_create(test_name);
list_insert(&suite->tests, &t->node);
/* we can't guarantee that we clean up properly if a test fails, the
udev rules used for a previous test may still be in place. Add an
unchecked fixture to always clean up all rules before/after a
test case completes */
tcase_add_unchecked_fixture(t->tc,
litest_drop_udev_rules,
litest_drop_udev_rules);
tcase_add_checked_fixture(t->tc, dev->setup,
dev->teardown ? dev->teardown : litest_generic_device_teardown);
tcase_add_test(t->tc, func);
@ -472,6 +532,40 @@ merge_events(const int *orig, const int *override)
return events;
}
static char *
litest_init_udev_rules(struct litest_test_device *dev)
{
int rc;
FILE *f;
char *path;
if (!dev->udev_rule)
return NULL;
rc = mkdir(UDEV_RULES_D, 0755);
if (rc == -1 && errno != EEXIST)
ck_abort_msg("Failed to create udev rules directory (%s)\n",
strerror(errno));
rc = asprintf(&path,
"%s/%s%s.rules",
UDEV_RULES_D,
UDEV_RULE_PREFIX,
dev->shortname);
ck_assert_int_eq(rc,
strlen(UDEV_RULES_D) +
strlen(UDEV_RULE_PREFIX) +
strlen(dev->shortname) + 7);
f = fopen(path, "w");
ck_assert_notnull(f);
ck_assert_int_ge(fputs(dev->udev_rule, f), 0);
fclose(f);
litest_reload_udev_rules();
return path;
}
static struct litest_device *
litest_create(enum litest_device_type which,
const char *name_override,
@ -485,6 +579,7 @@ litest_create(enum litest_device_type which,
const struct input_id *id;
struct input_absinfo *abs;
int *events;
char *udev_file;
dev = devices;
while (*dev) {
@ -499,12 +594,17 @@ litest_create(enum litest_device_type which,
d = zalloc(sizeof(*d));
ck_assert(d != NULL);
udev_file = litest_init_udev_rules(*dev);
/* device has custom create method */
if ((*dev)->create) {
(*dev)->create(d);
if (abs_override || events_override)
if (abs_override || events_override) {
if (udev_file)
unlink(udev_file);
ck_abort_msg("Custom create cannot"
"be overridden");
}
return d;
}
@ -519,6 +619,7 @@ litest_create(enum litest_device_type which,
abs,
events);
d->interface = (*dev)->interface;
d->udev_rule_file = udev_file;
free(abs);
free(events);
@ -637,6 +738,12 @@ litest_delete_device(struct litest_device *d)
if (!d)
return;
if (d->udev_rule_file) {
unlink(d->udev_rule_file);
free(d->udev_rule_file);
d->udev_rule_file = NULL;
}
libinput_device_unref(d->libinput_device);
libinput_path_remove_device(d->libinput_device);
if (d->owns_context)

View file

@ -91,6 +91,8 @@ struct litest_device {
bool skip_ev_syn;
void *private; /* device-specific data */
char *udev_rule_file;
};
struct libinput *litest_create_context(void);

View file

@ -145,30 +145,20 @@ END_TEST
START_TEST(event_conversion_pointer)
{
struct libevdev_uinput *uinput;
struct libinput *li;
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
int motion = 0, button = 0;
uinput = create_simple_test_device("litest test device",
EV_REL, REL_X,
EV_REL, REL_Y,
EV_KEY, BTN_LEFT,
EV_KEY, BTN_MIDDLE,
EV_KEY, BTN_LEFT,
-1, -1);
li = libinput_path_create_context(&simple_interface, NULL);
libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
/* Queue at least two relative motion events as the first one may
* be absorbed by the pointer acceleration filter. */
libevdev_uinput_write_event(uinput, EV_REL, REL_X, -1);
libevdev_uinput_write_event(uinput, EV_REL, REL_Y, -1);
libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
libevdev_uinput_write_event(uinput, EV_REL, REL_X, -1);
libevdev_uinput_write_event(uinput, EV_REL, REL_Y, -1);
libevdev_uinput_write_event(uinput, EV_KEY, BTN_LEFT, 1);
libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
litest_event(dev, EV_REL, REL_X, -1);
litest_event(dev, EV_REL, REL_Y, -1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
litest_event(dev, EV_REL, REL_X, -1);
litest_event(dev, EV_REL, REL_Y, -1);
litest_event(dev, EV_KEY, BTN_LEFT, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
@ -196,9 +186,6 @@ START_TEST(event_conversion_pointer)
libinput_event_destroy(event);
}
libinput_unref(li);
libevdev_uinput_destroy(uinput);
ck_assert_int_gt(motion, 0);
ck_assert_int_gt(button, 0);
}
@ -206,29 +193,18 @@ END_TEST
START_TEST(event_conversion_pointer_abs)
{
struct libevdev_uinput *uinput;
struct libinput *li;
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
int motion = 0, button = 0;
uinput = create_simple_test_device("litest test device",
EV_ABS, ABS_X,
EV_ABS, ABS_Y,
EV_KEY, BTN_LEFT,
EV_KEY, BTN_MIDDLE,
EV_KEY, BTN_LEFT,
-1, -1);
li = libinput_path_create_context(&simple_interface, NULL);
libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
libinput_dispatch(li);
libevdev_uinput_write_event(uinput, EV_ABS, ABS_X, 10);
libevdev_uinput_write_event(uinput, EV_ABS, ABS_Y, 50);
libevdev_uinput_write_event(uinput, EV_KEY, BTN_LEFT, 1);
libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
libevdev_uinput_write_event(uinput, EV_ABS, ABS_X, 30);
libevdev_uinput_write_event(uinput, EV_ABS, ABS_Y, 30);
libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
litest_event(dev, EV_ABS, ABS_X, 10);
litest_event(dev, EV_ABS, ABS_Y, 50);
litest_event(dev, EV_KEY, BTN_LEFT, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
litest_event(dev, EV_ABS, ABS_X, 30);
litest_event(dev, EV_ABS, ABS_Y, 30);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
@ -256,9 +232,6 @@ START_TEST(event_conversion_pointer_abs)
libinput_event_destroy(event);
}
libinput_unref(li);
libevdev_uinput_destroy(uinput);
ck_assert_int_gt(motion, 0);
ck_assert_int_gt(button, 0);
}
@ -266,23 +239,15 @@ END_TEST
START_TEST(event_conversion_key)
{
struct libevdev_uinput *uinput;
struct libinput *li;
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
int key = 0;
uinput = create_simple_test_device("litest test device",
EV_KEY, KEY_A,
EV_KEY, KEY_B,
-1, -1);
li = libinput_path_create_context(&simple_interface, NULL);
libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
libinput_dispatch(li);
libevdev_uinput_write_event(uinput, EV_KEY, KEY_A, 1);
libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
libevdev_uinput_write_event(uinput, EV_KEY, KEY_A, 0);
libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
litest_event(dev, EV_KEY, KEY_A, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
litest_event(dev, EV_KEY, KEY_A, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
@ -306,42 +271,28 @@ START_TEST(event_conversion_key)
libinput_event_destroy(event);
}
libinput_unref(li);
libevdev_uinput_destroy(uinput);
ck_assert_int_gt(key, 0);
}
END_TEST
START_TEST(event_conversion_touch)
{
struct libevdev_uinput *uinput;
struct libinput *li;
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
int touch = 0;
uinput = create_simple_test_device("litest test device",
EV_KEY, BTN_TOUCH,
EV_ABS, ABS_X,
EV_ABS, ABS_Y,
EV_ABS, ABS_MT_SLOT,
EV_ABS, ABS_MT_TRACKING_ID,
EV_ABS, ABS_MT_POSITION_X,
EV_ABS, ABS_MT_POSITION_Y,
-1, -1);
li = libinput_path_create_context(&simple_interface, NULL);
libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
libinput_dispatch(li);
libevdev_uinput_write_event(uinput, EV_KEY, BTN_TOOL_FINGER, 1);
libevdev_uinput_write_event(uinput, EV_KEY, BTN_TOUCH, 1);
libevdev_uinput_write_event(uinput, EV_ABS, ABS_X, 10);
libevdev_uinput_write_event(uinput, EV_ABS, ABS_Y, 10);
libevdev_uinput_write_event(uinput, EV_ABS, ABS_MT_SLOT, 0);
libevdev_uinput_write_event(uinput, EV_ABS, ABS_MT_TRACKING_ID, 1);
libevdev_uinput_write_event(uinput, EV_ABS, ABS_MT_POSITION_X, 10);
libevdev_uinput_write_event(uinput, EV_ABS, ABS_MT_POSITION_Y, 10);
libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0);
litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1);
litest_event(dev, EV_KEY, BTN_TOUCH, 1);
litest_event(dev, EV_ABS, ABS_X, 10);
litest_event(dev, EV_ABS, ABS_Y, 10);
litest_event(dev, EV_ABS, ABS_MT_SLOT, 0);
litest_event(dev, EV_ABS, ABS_MT_TRACKING_ID, 1);
litest_event(dev, EV_ABS, ABS_MT_POSITION_X, 10);
litest_event(dev, EV_ABS, ABS_MT_POSITION_Y, 10);
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
@ -366,9 +317,6 @@ START_TEST(event_conversion_touch)
libinput_event_destroy(event);
}
libinput_unref(li);
libevdev_uinput_destroy(uinput);
ck_assert_int_gt(touch, 0);
}
END_TEST
@ -639,11 +587,13 @@ END_TEST
int main (int argc, char **argv) {
litest_add_no_device("events:conversion", event_conversion_device_notify);
litest_add_no_device("events:conversion", event_conversion_pointer);
litest_add_no_device("events:conversion", event_conversion_pointer_abs);
litest_add_no_device("events:conversion", event_conversion_key);
litest_add_no_device("events:conversion", event_conversion_touch);
litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE);
litest_add_for_device("events:conversion", event_conversion_pointer, LITEST_MOUSE);
litest_add_for_device("events:conversion", event_conversion_pointer_abs, LITEST_XEN_VIRTUAL_POINTER);
litest_add_for_device("events:conversion", event_conversion_key, LITEST_KEYBOARD);
litest_add_for_device("events:conversion", event_conversion_touch, LITEST_WACOM_TOUCH);
litest_add_no_device("bitfield_helpers", bitfield_helpers);
litest_add_no_device("context:refcount", context_ref_counting);
litest_add_no_device("config:status string", config_status_string);

View file

@ -732,6 +732,86 @@ START_TEST(pointer_scroll_button)
}
END_TEST
START_TEST(pointer_accel_defaults)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
enum libinput_config_status status;
double speed;
ck_assert(libinput_device_config_accel_is_available(device));
ck_assert(libinput_device_config_accel_get_default_speed(device) == 0.0);
ck_assert(libinput_device_config_accel_get_speed(device) == 0.0);
for (speed = -2.0; speed < -1.0; speed += 0.2) {
status = libinput_device_config_accel_set_speed(device,
speed);
ck_assert_int_eq(status,
LIBINPUT_CONFIG_STATUS_INVALID);
ck_assert(libinput_device_config_accel_get_speed(device) == 0.0);
}
for (speed = -1.0; speed <= 1.0; speed += 0.2) {
status = libinput_device_config_accel_set_speed(device,
speed);
ck_assert_int_eq(status,
LIBINPUT_CONFIG_STATUS_SUCCESS);
ck_assert(libinput_device_config_accel_get_speed(device) == speed);
}
for (speed = 1.2; speed <= -2.0; speed += 0.2) {
status = libinput_device_config_accel_set_speed(device,
speed);
ck_assert_int_eq(status,
LIBINPUT_CONFIG_STATUS_INVALID);
ck_assert(libinput_device_config_accel_get_speed(device) == 1.0);
}
}
END_TEST
START_TEST(pointer_accel_invalid)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
enum libinput_config_status status;
ck_assert(libinput_device_config_accel_is_available(device));
status = libinput_device_config_accel_set_speed(device,
NAN);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
status = libinput_device_config_accel_set_speed(device,
INFINITY);
ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
}
END_TEST
START_TEST(pointer_accel_defaults_absolute)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
enum libinput_config_status status;
double speed;
ck_assert(!libinput_device_config_accel_is_available(device));
ck_assert(libinput_device_config_accel_get_default_speed(device) == 0.0);
ck_assert(libinput_device_config_accel_get_speed(device) == 0.0);
for (speed = -2.0; speed <= 2.0; speed += 0.2) {
status = libinput_device_config_accel_set_speed(device,
speed);
if (speed >= -1.0 && speed <= 1.0)
ck_assert_int_eq(status,
LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
else
ck_assert_int_eq(status,
LIBINPUT_CONFIG_STATUS_INVALID);
ck_assert(libinput_device_config_accel_get_speed(device) == 0.0);
}
}
END_TEST
int main (int argc, char **argv) {
litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_ANY);
@ -754,5 +834,9 @@ int main (int argc, char **argv) {
litest_add("pointer:left-handed", pointer_left_handed_during_click, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY);
litest_add("pointer:left-handed", pointer_left_handed_during_click_multiple_buttons, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_defaults, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_invalid, LITEST_RELATIVE, LITEST_ANY);
litest_add("pointer:accel", pointer_accel_defaults_absolute, LITEST_ABSOLUTE, LITEST_ANY);
return litest_run(argc, argv);
}