diff --git a/.gitignore b/.gitignore index 3ea8576b..2253d454 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,5 @@ doc/libinput.doxygen doc/html tags test/test-* +test/symbols-leak-test* test-driver diff --git a/Makefile.am b/Makefile.am index 05698ba5..fc6e6b79 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = src doc test tools +SUBDIRS = src doc test tools udev ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} diff --git a/configure.ac b/configure.ac index f47c5a49..2ccd30d7 100644 --- a/configure.ac +++ b/configure.ac @@ -69,6 +69,19 @@ fi AC_SUBST(GCC_CFLAGS) AC_SUBST(GCC_CXXFLAGS) +udev_dir_default="$libdir/udev" +AC_ARG_WITH(udev-dir, + AS_HELP_STRING([--with-udev-dir=DIR], + [udev base directory [[default=$udev_dir_default]]]), + [], + [with_udev_dir="yes"]) +AS_CASE($with_udev_dir, + [no|""], [AC_MSG_ERROR([You must define a udev base directory])], + [yes], [udevdir="$udev_dir_default"], + [udevdir="$with_udev_dir"]) +UDEV_DIR=${udevdir} +AC_SUBST(UDEV_DIR) + AC_ARG_ENABLE([documentation], [AC_HELP_STRING([--enable-documentation], [Enable building the documentation (default=auto)])], @@ -163,11 +176,15 @@ AC_CONFIG_FILES([Makefile src/libinput.pc src/libinput-version.h test/Makefile - tools/Makefile]) + tools/Makefile + udev/Makefile]) +AC_CONFIG_FILES([test/symbols-leak-test], + [chmod +x test/symbols-leak-test]) AC_OUTPUT AC_MSG_RESULT([ Prefix ${prefix} + udev base dir ${UDEV_DIR} Build documentation ${build_documentation} Build tests ${build_tests} diff --git a/doc/device-configuration-via-udev.dox b/doc/device-configuration-via-udev.dox index bee36598..68a45af0 100644 --- a/doc/device-configuration-via-udev.dox +++ b/doc/device-configuration-via-udev.dox @@ -12,6 +12,11 @@ The following udev properties are supported:
Sets the calibration matrix, see libinput_device_config_calibration_get_default_matrix(). If unset, defaults to the identity matrix.
+
LIBINPUT_DEVICE_GROUP
+
A string identifying the @ref libinput_device_group for this device. Two +devices with the same property value are grouped into the same device group, +the value itself is irrelevant otherwise. +
ID_SEAT
Assigns the physical seat for this device. See libinput_seat_get_physical_name(). Defaults to "seat0".
diff --git a/src/evdev.c b/src/evdev.c index eb963ab1..bbc3dce5 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1356,16 +1356,23 @@ evdev_device_get_udev_tags(struct evdev_device *device, { const char *prop; enum evdev_device_udev_tags tags = 0; - const struct evdev_udev_tag_match *match = evdev_udev_tag_matches; + const struct evdev_udev_tag_match *match; + int i; - while (match->name) { - prop = udev_device_get_property_value(device->udev_device, + for (i = 0; i < 2 && udev_device; i++) { + match = evdev_udev_tag_matches; + while (match->name) { + prop = udev_device_get_property_value( + udev_device, match->name); - if (prop) - tags |= match->tag; + if (prop) + tags |= match->tag; - match++; + match++; + } + udev_device = udev_device_get_parent(udev_device); } + return tags; } @@ -1393,7 +1400,7 @@ evdev_configure_device(struct evdev_device *device) } log_info(libinput, - "input device '%s', %s is tagged by udev as:%s%s%s%s%s%s\n", + "input device '%s', %s is tagged by udev as:%s%s%s%s%s%s%s\n", device->devname, devnode, udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "", udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "", @@ -1592,6 +1599,42 @@ out: return rc; } +static int +evdev_set_device_group(struct evdev_device *device, + struct udev_device *udev_device) +{ + struct libinput_device_group *group = NULL; + const char *udev_group; + + udev_group = udev_device_get_property_value(udev_device, + "LIBINPUT_DEVICE_GROUP"); + if (udev_group) { + struct libinput_device *d; + + list_for_each(d, &device->base.seat->devices_list, link) { + const char *identifier = d->group->identifier; + + if (identifier && + strcmp(identifier, udev_group) == 0) { + group = d->group; + break; + } + } + } + + if (!group) { + group = libinput_device_group_create(udev_group); + if (!group) + return 1; + libinput_device_set_device_group(&device->base, group); + libinput_device_group_unref(group); + } else { + libinput_device_set_device_group(&device->base, group); + } + + return 0; +} + struct evdev_device * evdev_device_create(struct libinput_seat *seat, struct udev_device *udev_device) @@ -1602,7 +1645,6 @@ 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 @@ -1673,11 +1715,8 @@ evdev_device_create(struct libinput_seat *seat, if (!device->source) goto err; - group = libinput_device_group_create(); - if (!group) + if (evdev_set_device_group(device, udev_device)) goto err; - libinput_device_set_device_group(&device->base, group); - libinput_device_group_unref(group); list_insert(seat->devices_list.prev, &device->base.link); diff --git a/src/libinput-private.h b/src/libinput-private.h index bc1e0c10..ac8f9fbf 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -172,6 +172,7 @@ struct libinput_device_config { struct libinput_device_group { int refcount; void *user_data; + char *identifier; /* unique identifier or NULL for singletons */ }; struct libinput_device { @@ -260,7 +261,7 @@ libinput_device_init(struct libinput_device *device, struct libinput_seat *seat); struct libinput_device_group * -libinput_device_group_create(void); +libinput_device_group_create(const char *identifier); void libinput_device_set_device_group(struct libinput_device *device, @@ -295,8 +296,8 @@ pointer_notify_motion(struct libinput_device *device, uint64_t time, double dx, double dy, - double dx_noaccel, - double dy_noaccel); + double dx_unaccel, + double dy_unaccel); void pointer_notify_motion_absolute(struct libinput_device *device, diff --git a/src/libinput.c b/src/libinput.c index b300d74e..3aa684a9 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -1673,11 +1673,17 @@ libinput_device_get_size(struct libinput_device *device, } LIBINPUT_EXPORT int -libinput_device_has_button(struct libinput_device *device, uint32_t code) +libinput_device_pointer_has_button(struct libinput_device *device, uint32_t code) { return evdev_device_has_button((struct evdev_device *)device, code); } +LIBINPUT_EXPORT int +libinput_device_has_button(struct libinput_device *device, uint32_t code) +{ + return libinput_device_pointer_has_button(device, code); +} + LIBINPUT_EXPORT struct libinput_event * libinput_event_device_notify_get_base_event(struct libinput_event_device_notify *event) { @@ -1710,13 +1716,23 @@ libinput_device_group_ref(struct libinput_device_group *group) } struct libinput_device_group * -libinput_device_group_create(void) +libinput_device_group_create(const char *identifier) { struct libinput_device_group *group; group = zalloc(sizeof *group); - if (group) - group->refcount = 1; + if (!group) + return NULL; + + group->refcount = 1; + if (identifier) { + group->identifier = strdup(identifier); + if (!group->identifier) { + free(group); + group = NULL; + } + } + return group; } @@ -1731,6 +1747,7 @@ libinput_device_set_device_group(struct libinput_device *device, static void libinput_device_group_destroy(struct libinput_device_group *group) { + free(group->identifier); free(group); } @@ -2114,7 +2131,7 @@ LIBINPUT_EXPORT enum libinput_config_status libinput_device_config_scroll_set_button(struct libinput_device *device, uint32_t button) { - if (button && !libinput_device_has_button(device, button)) + if (button && !libinput_device_pointer_has_button(device, button)) return LIBINPUT_CONFIG_STATUS_INVALID; if ((libinput_device_config_scroll_get_methods(device) & diff --git a/src/libinput.h b/src/libinput.h index a4413b67..3cc041e2 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -834,11 +834,11 @@ libinput_event_pointer_get_axis_value(struct libinput_event_pointer *event, * The coordinate system is identical to the cursor movement, i.e. a * scroll value of 1 represents the equivalent relative motion of 1. * - * For pointer events that are not of type LIBINPUT_EVENT_POINTER_AXIS, + * For pointer events that are not of type @ref LIBINPUT_EVENT_POINTER_AXIS, * this function returns 0. * * @note It is an application bug to call this function for events other than - * LIBINPUT_EVENT_POINTER_AXIS. + * @ref LIBINPUT_EVENT_POINTER_AXIS. * * @return the source for this axis event */ @@ -1837,6 +1837,9 @@ libinput_device_get_context(struct libinput_device *device); * libinput_device_group_unref() to continue using the handle outside of the * immediate scope. * + * Device groups are assigned based on the LIBINPUT_DEVICE_GROUP udev + * property, see @ref udev_config. + * * @return The device group this device belongs to */ struct libinput_device_group * @@ -2027,7 +2030,15 @@ libinput_device_get_size(struct libinput_device *device, * on error. */ int -libinput_device_has_button(struct libinput_device *device, uint32_t code); +libinput_device_pointer_has_button(struct libinput_device *device, uint32_t code); + +/** + * @ingroup device + * + * @deprecated Use libinput_device_pointer_has_button() instead. + */ +int +libinput_device_has_button(struct libinput_device *device, uint32_t code) LIBINPUT_ATTRIBUTE_DEPRECATED; /** * @ingroup device diff --git a/src/libinput.sym b/src/libinput.sym index 307dff3b..65ffe3d6 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -135,6 +135,8 @@ LIBINPUT_0.11.0 { libinput_device_group_set_user_data; libinput_device_group_unref; + libinput_device_pointer_has_button; + /* tablet APIs, they are not part of any stable API promise yet. * keep them separate */ libinput_event_get_tablet_event; diff --git a/test/Makefile.am b/test/Makefile.am index d1502c10..a0c93414 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -55,7 +55,8 @@ build_tests = \ test-build-std-gnuc90 noinst_PROGRAMS = $(build_tests) $(run_tests) -TESTS = $(run_tests) +noinst_SCRIPTS = symbols-leak-test +TESTS = $(run_tests) symbols-leak-test .NOTPARALLEL: diff --git a/test/symbols-leak-test.in b/test/symbols-leak-test.in new file mode 100755 index 00000000..448ef2f8 --- /dev/null +++ b/test/symbols-leak-test.in @@ -0,0 +1,17 @@ +#!/bin/bash + +### simple check for exported symbols + +# make bash exit if any command will exit with non-0 return value +set -e + +# make sure the paths are alright +cd `dirname $0` + +diff -a -u \ + <(cat @top_srcdir@/src/libinput.sym | \ + grep '^\s\+libinput_.*' | \ + sed -e 's/^\s\+\(.*\);/\1/' | sort) \ + <(cat @top_srcdir@/src/*.c | \ + grep LIBINPUT_EXPORT -A 1 | grep '^libinput_.*' | \ + sed -e 's/(.*//' | sort) diff --git a/tools/event-debug.c b/tools/event-debug.c index 9f44cf61..f74344ef 100644 --- a/tools/event-debug.c +++ b/tools/event-debug.c @@ -131,13 +131,24 @@ print_device_notify(struct libinput_event *ev) { struct libinput_device *dev = libinput_event_get_device(ev); struct libinput_seat *seat = libinput_device_get_seat(dev); + struct libinput_device_group *group; double w, h; uint32_t scroll_methods; + static int next_group_id = 0; + intptr_t group_id; - printf("%-33s %5s %7s", + group = libinput_device_get_device_group(dev); + group_id = (intptr_t)libinput_device_group_get_user_data(group); + if (!group_id) { + group_id = ++next_group_id; + libinput_device_group_set_user_data(group, (void*)group_id); + } + + printf("%-33s %5s %7s group%d", libinput_device_get_name(dev), libinput_seat_get_physical_name(seat), - libinput_seat_get_logical_name(seat)); + libinput_seat_get_logical_name(seat), + (int)group_id); printf(" cap:"); if (libinput_device_has_capability(dev, diff --git a/udev/.gitignore b/udev/.gitignore new file mode 100644 index 00000000..d8e1456b --- /dev/null +++ b/udev/.gitignore @@ -0,0 +1 @@ +libinput-device-group diff --git a/udev/80-libinput-device-groups.rules b/udev/80-libinput-device-groups.rules new file mode 100644 index 00000000..f826bec6 --- /dev/null +++ b/udev/80-libinput-device-groups.rules @@ -0,0 +1,8 @@ +ACTION!="add|change", GOTO="libinput_device_group_end" +KERNEL!="event[0-9]*", GOTO="libinput_device_group_end" + +ATTRS{phys}=="?*", \ + PROGRAM="libinput-device-group %S%p", \ + ENV{LIBINPUT_DEVICE_GROUP}="%c" + +LABEL="libinput_device_group_end" diff --git a/udev/Makefile.am b/udev/Makefile.am new file mode 100644 index 00000000..3691172c --- /dev/null +++ b/udev/Makefile.am @@ -0,0 +1,9 @@ +udevdir=$(UDEV_DIR) +udev_PROGRAMS = libinput-device-group + +libinput_device_group_SOURCES = libinput-device-group.c +libinput_device_group_CFLAGS = $(LIBUDEV_CFLAGS) $(GCC_CFLAGS) +libinput_device_group_LDADD = $(LIBUDEV_LIBS) + +udev_rulesdir=$(UDEV_DIR)/rules.d +dist_udev_rules_DATA = 80-libinput-device-groups.rules diff --git a/udev/libinput-device-group.c b/udev/libinput-device-group.c new file mode 100644 index 00000000..50bfbe02 --- /dev/null +++ b/udev/libinput-device-group.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + int rc = 1; + struct udev *udev = NULL; + struct udev_device *device = NULL; + const char *syspath, + *phys = NULL; + char *group, + *str; + + if (argc != 2) + return 1; + + syspath = argv[1]; + + udev = udev_new(); + if (!udev) + goto out; + + device = udev_device_new_from_syspath(udev, syspath); + if (!device) + goto out; + + /* Find the first parent with ATTRS{phys} set. For tablets that + * value looks like usb-0000:00:14.0-1/input1. Drop the /input1 + * bit and use the remainder as device group identifier */ + while (device != NULL) { + struct udev_device *parent; + + phys = udev_device_get_sysattr_value(device, "phys"); + if (phys) + break; + + parent = udev_device_get_parent(device); + udev_device_ref(parent); + udev_device_unref(device); + device = parent; + } + + if (!phys) + goto out; + + group = strdup(phys); + if (!group) + goto out; + + str = strstr(group, "/input"); + if (str) + *str = '\0'; + + /* Cintiq 22HD Touch has + usb-0000:00:14.0-6.3.1/input0 for the touch + usb-0000:00:14.0-6.3.0/input0 for the pen + Check if there's a . after the last -, if so, cut off the string + there. + */ + str = strrchr(group, '.'); + if (str && str > strrchr(group, '-')) + *str = '\0'; + + printf("%s\n", group); + free(group); + + rc = 0; +out: + if (device) + udev_device_unref(device); + if (udev) + udev_unref(udev); + + return rc; +}