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