[PATCH libinput 3/6] pad: implement wacom pad support

Peter Hutterer peter.hutterer at who-t.net
Wed Feb 10 06:17:31 UTC 2016


Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 src/Makefile.am        |   1 +
 src/evdev-tablet-pad.c | 536 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/evdev-tablet-pad.h |  66 ++++++
 src/evdev.c            |  28 +--
 src/evdev.h            |  14 ++
 src/libinput.c         |   7 +-
 6 files changed, 637 insertions(+), 15 deletions(-)
 create mode 100644 src/evdev-tablet-pad.c
 create mode 100644 src/evdev-tablet-pad.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 343e75c..a3df6c8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -20,6 +20,7 @@ libinput_la_SOURCES =			\
 	evdev-mt-touchpad-gestures.c	\
 	evdev-tablet.c			\
 	evdev-tablet.h			\
+	evdev-tablet-pad.c		\
 	filter.c			\
 	filter.h			\
 	filter-private.h		\
diff --git a/src/evdev-tablet-pad.c b/src/evdev-tablet-pad.c
new file mode 100644
index 0000000..810b241
--- /dev/null
+++ b/src/evdev-tablet-pad.c
@@ -0,0 +1,536 @@
+/*
+ * Copyright © 2016 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+#include "evdev-tablet-pad.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+
+#define pad_set_status(pad_,s_) (pad_)->status |= (s_)
+#define pad_unset_status(pad_,s_) (pad_)->status &= ~(s_)
+#define pad_has_status(pad_,s_) (!!((pad_)->status & (s_)))
+
+static void
+pad_get_buttons_pressed(struct pad_dispatch *pad,
+			struct button_state *buttons)
+{
+	struct button_state *state = &pad->button_state;
+	struct button_state *prev_state = &pad->prev_button_state;
+	unsigned int i;
+
+	for (i = 0; i < sizeof(buttons->bits); i++)
+		buttons->bits[i] = state->bits[i] & ~(prev_state->bits[i]);
+}
+
+static void
+pad_get_buttons_released(struct pad_dispatch *pad,
+			 struct button_state *buttons)
+{
+	struct button_state *state = &pad->button_state;
+	struct button_state *prev_state = &pad->prev_button_state;
+	unsigned int i;
+
+	for (i = 0; i < sizeof(buttons->bits); i++)
+		buttons->bits[i] = prev_state->bits[i] & ~(state->bits[i]);
+}
+
+static inline bool
+pad_button_is_down(const struct pad_dispatch *pad,
+		   uint32_t button)
+{
+	return bit_is_set(pad->button_state.bits, button);
+}
+
+static inline void
+pad_button_set_down(struct pad_dispatch *pad,
+		    uint32_t button,
+		    bool is_down)
+{
+	struct button_state *state = &pad->button_state;
+
+	if (is_down) {
+		set_bit(state->bits, button);
+		pad_set_status(pad, PAD_BUTTONS_PRESSED);
+	} else {
+		clear_bit(state->bits, button);
+		pad_set_status(pad, PAD_BUTTONS_RELEASED);
+	}
+}
+
+static void
+pad_process_absolute(struct pad_dispatch *pad,
+		     struct evdev_device *device,
+		     struct input_event *e,
+		     uint64_t time)
+{
+	switch (e->code) {
+	case ABS_WHEEL:
+		pad->changed_axes |= PAD_AXIS_RING1;
+		pad_set_status(pad, PAD_AXES_UPDATED);
+		break;
+	case ABS_THROTTLE:
+		pad->changed_axes |= PAD_AXIS_RING2;
+		pad_set_status(pad, PAD_AXES_UPDATED);
+		break;
+	case ABS_RX:
+		pad->changed_axes |= PAD_AXIS_STRIP1;
+		pad_set_status(pad, PAD_AXES_UPDATED);
+		break;
+	case ABS_RY:
+		pad->changed_axes |= PAD_AXIS_STRIP2;
+		pad_set_status(pad, PAD_AXES_UPDATED);
+		break;
+	case ABS_MISC:
+		/* The wacom driver always sends a 0 axis event on finger
+		   up, but we also get an ABS_MISC 15 on touch down and
+		   ABS_MISC 0 on touch up, on top of the actual event. This
+		   is kernel behavior for xf86-input-wacom backwards
+		   compatibility after the 3.17 wacom HID move.
+
+		   We use that event to tell when we truly went a full
+		   rotation around the wheel vs. a finger release.
+
+		   FIXME: On the Intuos5 and later the kernel merges all
+		   states into that event, so if any finger is down on any
+		   button, the wheel release won't trigger the ABS_MISC 0
+		   but still send a 0 event. We can't currently detect this.
+		 */
+		pad->have_abs_misc_terminator = true;
+		break;
+	default:
+		log_info(device->base.seat->libinput,
+			 "Unhandled EV_ABS event code %#x\n", e->code);
+		break;
+	}
+}
+
+static inline double
+normalize_ring(const struct input_absinfo *absinfo)
+{
+	/* libinput has 0 as the ring's northernmost point in the device's
+	   current logical rotation, increasing clockwise to 1. Wacom has
+	   0 on the left-most wheel position.
+	 */
+	double range = absinfo->maximum - absinfo->minimum + 1;
+	double value = (absinfo->value - absinfo->minimum) / range - 0.25;
+
+	if (value < 0.0)
+		value += 1.0;
+
+	return value;
+}
+
+static inline double
+normalize_strip(const struct input_absinfo *absinfo)
+{
+	/* strip axes don't use a proper value, they just shift the bit left
+	 * for each position. 0 isn't a real value either, it's only sent on
+	 * finger release */
+	double min = 0,
+	       max = log2(absinfo->maximum);
+	double range = max - min;
+	double value = (log2(absinfo->value) - min) / range;
+
+	return value;
+}
+
+static inline double
+pad_handle_ring(struct pad_dispatch *pad,
+		struct evdev_device *device,
+		unsigned int code)
+{
+	const struct input_absinfo *absinfo;
+
+	absinfo = libevdev_get_abs_info(device->evdev, code);
+	assert(absinfo);
+
+	return normalize_ring(absinfo) * 360;
+}
+
+static inline double
+pad_handle_strip(struct pad_dispatch *pad,
+		 struct evdev_device *device,
+		 unsigned int code)
+{
+	const struct input_absinfo *absinfo;
+
+	absinfo = libevdev_get_abs_info(device->evdev, code);
+	assert(absinfo);
+
+	if (absinfo->value == 0)
+		return 0.0;
+
+	return normalize_strip(absinfo);
+}
+
+static void
+pad_check_notify_axes(struct pad_dispatch *pad,
+		      struct evdev_device *device,
+		      uint64_t time)
+{
+	struct libinput_device *base = &device->base;
+	double value;
+	bool send_finger_up = false;
+
+	/* Suppress the reset to 0 on finger up. See the
+	   comment in pad_process_absolute */
+	if (pad->have_abs_misc_terminator &&
+	    libevdev_get_event_value(device->evdev, EV_ABS, ABS_MISC) == 0)
+		send_finger_up = true;
+
+	if (pad->changed_axes & PAD_AXIS_RING1) {
+		value = pad_handle_ring(pad, device, ABS_WHEEL);
+		if (send_finger_up)
+			value = -1.0;
+
+		tablet_pad_notify_ring(base,
+				       time,
+				       0,
+				       value,
+				       LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
+	}
+
+	if (pad->changed_axes & PAD_AXIS_RING2) {
+		value = pad_handle_ring(pad, device, ABS_THROTTLE);
+		if (send_finger_up)
+			value = -1.0;
+
+		tablet_pad_notify_ring(base,
+				       time,
+				       1,
+				       value,
+				       LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
+	}
+
+	if (pad->changed_axes & PAD_AXIS_STRIP1) {
+		value = pad_handle_strip(pad, device, ABS_RX);
+		if (send_finger_up)
+			value = -1.0;
+
+		tablet_pad_notify_strip(base,
+					time,
+					0,
+					value,
+					LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
+	}
+
+	if (pad->changed_axes & PAD_AXIS_STRIP2) {
+		value = pad_handle_strip(pad, device, ABS_RY);
+		if (send_finger_up)
+			value = -1.0;
+
+		tablet_pad_notify_strip(base,
+					time,
+					1,
+					value,
+					LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
+	}
+
+	pad->changed_axes = PAD_AXIS_NONE;
+	pad->have_abs_misc_terminator = false;
+}
+
+static void
+pad_process_key(struct pad_dispatch *pad,
+		struct evdev_device *device,
+		struct input_event *e,
+		uint64_t time)
+{
+	uint32_t button = e->code;
+	uint32_t is_press = e->value != 0;
+
+	pad_button_set_down(pad, button, is_press);
+}
+
+static void
+pad_notify_button_mask(struct pad_dispatch *pad,
+		       struct evdev_device *device,
+		       uint64_t time,
+		       const struct button_state *buttons,
+		       enum libinput_button_state state)
+{
+	struct libinput_device *base = &device->base;
+	int32_t num_button;
+	unsigned int i;
+
+	for (i = 0; i < sizeof(buttons->bits); i++) {
+		unsigned char buttons_slice = buttons->bits[i];
+
+		num_button = i * 8;
+		while (buttons_slice) {
+			int enabled;
+
+			num_button++;
+			enabled = (buttons_slice & 1);
+			buttons_slice >>= 1;
+
+			if (!enabled)
+				continue;
+
+			tablet_pad_notify_button(base,
+						 time,
+						 num_button - 1,
+						 state);
+		}
+	}
+}
+
+static void
+pad_notify_buttons(struct pad_dispatch *pad,
+		   struct evdev_device *device,
+		   uint64_t time,
+		   enum libinput_button_state state)
+{
+	struct button_state buttons;
+
+	if (state == LIBINPUT_BUTTON_STATE_PRESSED)
+		pad_get_buttons_pressed(pad, &buttons);
+	else
+		pad_get_buttons_released(pad, &buttons);
+
+	pad_notify_button_mask(pad, device, time, &buttons, state);
+}
+
+static void
+pad_flush(struct pad_dispatch *pad,
+	  struct evdev_device *device,
+	  uint64_t time)
+{
+	if (pad_has_status(pad, PAD_AXES_UPDATED)) {
+		pad_check_notify_axes(pad, device, time);
+		pad_unset_status(pad, PAD_AXES_UPDATED);
+	}
+
+	if (pad_has_status(pad, PAD_BUTTONS_RELEASED)) {
+		pad_notify_buttons(pad,
+				   device,
+				   time,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+		pad_unset_status(pad, PAD_BUTTONS_RELEASED);
+	}
+
+	if (pad_has_status(pad, PAD_BUTTONS_PRESSED)) {
+		pad_notify_buttons(pad,
+				   device,
+				   time,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+		pad_unset_status(pad, PAD_BUTTONS_PRESSED);
+	}
+
+	/* Update state */
+	memcpy(&pad->prev_button_state,
+	       &pad->button_state,
+	       sizeof(pad->button_state));
+}
+
+static void
+pad_process(struct evdev_dispatch *dispatch,
+	    struct evdev_device *device,
+	    struct input_event *e,
+	    uint64_t time)
+{
+	struct pad_dispatch *pad = (struct pad_dispatch *)dispatch;
+
+	switch (e->type) {
+	case EV_ABS:
+		pad_process_absolute(pad, device, e, time);
+		break;
+	case EV_KEY:
+		pad_process_key(pad, device, e, time);
+		break;
+	case EV_SYN:
+		pad_flush(pad, device, time);
+		break;
+	default:
+		log_error(device->base.seat->libinput,
+			  "Unexpected event type %s (%#x)\n",
+			  libevdev_event_type_get_name(e->type),
+			  e->type);
+		break;
+	}
+}
+
+static void
+pad_suspend(struct evdev_dispatch *dispatch,
+	    struct evdev_device *device)
+{
+	struct pad_dispatch *pad = (struct pad_dispatch *)dispatch;
+	struct libinput *libinput = device->base.seat->libinput;
+	unsigned int code;
+
+	for (code = KEY_ESC; code < KEY_CNT; code++) {
+		if (pad_button_is_down(pad, code))
+			pad_button_set_down(pad, code, false);
+	}
+
+	pad_flush(pad, device, libinput_now(libinput));
+}
+
+static void
+pad_destroy(struct evdev_dispatch *dispatch)
+{
+	struct pad_dispatch *pad = (struct pad_dispatch*)dispatch;
+
+	free(pad);
+}
+
+static struct evdev_dispatch_interface pad_interface = {
+	pad_process,
+	pad_suspend, /* suspend */
+	NULL, /* remove */
+	pad_destroy,
+	NULL, /* device_added */
+	NULL, /* device_removed */
+	NULL, /* device_suspended */
+	NULL, /* device_resumed */
+	NULL, /* post_added */
+};
+
+static int
+pad_init(struct pad_dispatch *pad, struct evdev_device *device)
+{
+	pad->base.interface = &pad_interface;
+	pad->device = device;
+	pad->status = PAD_NONE;
+	pad->changed_axes = PAD_AXIS_NONE;
+
+	return 0;
+}
+
+static uint32_t
+pad_sendevents_get_modes(struct libinput_device *device)
+{
+	return LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
+}
+
+static enum libinput_config_status
+pad_sendevents_set_mode(struct libinput_device *device,
+			enum libinput_config_send_events_mode mode)
+{
+	struct evdev_device *evdev = (struct evdev_device*)device;
+	struct pad_dispatch *pad = (struct pad_dispatch*)evdev->dispatch;
+
+	if (mode == pad->sendevents.current_mode)
+		return LIBINPUT_CONFIG_STATUS_SUCCESS;
+
+	switch(mode) {
+	case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
+		break;
+	case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
+		pad_suspend(evdev->dispatch, evdev);
+		break;
+	default:
+		return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
+	}
+
+	pad->sendevents.current_mode = mode;
+
+	return LIBINPUT_CONFIG_STATUS_SUCCESS;
+}
+
+static enum libinput_config_send_events_mode
+pad_sendevents_get_mode(struct libinput_device *device)
+{
+	struct evdev_device *evdev = (struct evdev_device*)device;
+	struct pad_dispatch *dispatch = (struct pad_dispatch*)evdev->dispatch;
+
+	return dispatch->sendevents.current_mode;
+}
+
+static enum libinput_config_send_events_mode
+pad_sendevents_get_default_mode(struct libinput_device *device)
+{
+	return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
+}
+
+struct evdev_dispatch *
+evdev_tablet_pad_create(struct evdev_device *device)
+{
+	struct pad_dispatch *pad;
+
+	pad = zalloc(sizeof *pad);
+	if (!pad)
+		return NULL;
+
+	if (pad_init(pad, device) != 0) {
+		pad_destroy(&pad->base);
+		return NULL;
+	}
+
+	device->base.config.sendevents = &pad->sendevents.config;
+	pad->sendevents.current_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
+	pad->sendevents.config.get_modes = pad_sendevents_get_modes;
+	pad->sendevents.config.set_mode = pad_sendevents_set_mode;
+	pad->sendevents.config.get_mode = pad_sendevents_get_mode;
+	pad->sendevents.config.get_default_mode = pad_sendevents_get_default_mode;
+
+	return &pad->base;
+}
+
+int
+evdev_device_tablet_pad_has_button(struct evdev_device *device, uint32_t code)
+{
+	if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
+		return -1;
+
+	return libevdev_has_event_code(device->evdev, EV_KEY, code);
+}
+
+int
+evdev_device_tablet_pad_get_num_rings(struct evdev_device *device)
+{
+	int nrings = 0;
+
+	if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
+		return -1;
+
+	if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_WHEEL)) {
+		nrings++;
+		if (libevdev_has_event_code(device->evdev,
+					    EV_ABS,
+					    ABS_THROTTLE))
+			nrings++;
+	}
+
+	return nrings;
+}
+
+int
+evdev_device_tablet_pad_get_num_strips(struct evdev_device *device)
+{
+	int nstrips = 0;
+
+	if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
+		return -1;
+
+	if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_RX)) {
+		nstrips++;
+		if (libevdev_has_event_code(device->evdev,
+					    EV_ABS,
+					    ABS_RY))
+			nstrips++;
+	}
+
+	return nstrips;
+}
diff --git a/src/evdev-tablet-pad.h b/src/evdev-tablet-pad.h
new file mode 100644
index 0000000..a769b47
--- /dev/null
+++ b/src/evdev-tablet-pad.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef EVDEV_BUTTONSET_WACOM_H
+#define EVDEV_BUTTONSET_WACOM_H
+
+#include "evdev.h"
+
+#define LIBINPUT_BUTTONSET_AXIS_NONE 0
+
+enum pad_status {
+	PAD_NONE = 0,
+	PAD_AXES_UPDATED = 1 << 0,
+	PAD_BUTTONS_PRESSED = 1 << 1,
+	PAD_BUTTONS_RELEASED = 1 << 2,
+};
+
+enum pad_axes {
+	PAD_AXIS_NONE = 0,
+	PAD_AXIS_RING1 = 1 << 0,
+	PAD_AXIS_RING2 = 1 << 1,
+	PAD_AXIS_STRIP1 = 1 << 2,
+	PAD_AXIS_STRIP2 = 1 << 3,
+};
+
+struct button_state {
+	unsigned char bits[NCHARS(KEY_CNT)];
+};
+
+struct pad_dispatch {
+	struct evdev_dispatch base;
+	struct evdev_device *device;
+	unsigned char status;
+	uint32_t changed_axes;
+
+	struct button_state button_state;
+	struct button_state prev_button_state;
+
+	bool have_abs_misc_terminator;
+
+	struct {
+		struct libinput_device_config_send_events config;
+		enum libinput_config_send_events_mode current_mode;
+	} sendevents;
+};
+
+#endif
diff --git a/src/evdev.c b/src/evdev.c
index 51768fe..a50b0a3 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -61,7 +61,7 @@ enum evdev_device_udev_tags {
         EVDEV_UDEV_TAG_TABLET = (1 << 5),
         EVDEV_UDEV_TAG_JOYSTICK = (1 << 6),
         EVDEV_UDEV_TAG_ACCELEROMETER = (1 << 7),
-        EVDEV_UDEV_TAG_BUTTONSET = (1 << 8),
+        EVDEV_UDEV_TAG_TABLET_PAD = (1 << 8),
         EVDEV_UDEV_TAG_POINTINGSTICK = (1 << 9),
 };
 
@@ -78,7 +78,7 @@ static const struct evdev_udev_tag_match evdev_udev_tag_matches[] = {
 	{"ID_INPUT_TOUCHPAD",		EVDEV_UDEV_TAG_TOUCHPAD},
 	{"ID_INPUT_TOUCHSCREEN",	EVDEV_UDEV_TAG_TOUCHSCREEN},
 	{"ID_INPUT_TABLET",		EVDEV_UDEV_TAG_TABLET},
-	{"ID_INPUT_TABLET_PAD",		EVDEV_UDEV_TAG_BUTTONSET},
+	{"ID_INPUT_TABLET_PAD",		EVDEV_UDEV_TAG_TABLET_PAD},
 	{"ID_INPUT_JOYSTICK",		EVDEV_UDEV_TAG_JOYSTICK},
 	{"ID_INPUT_ACCELEROMETER",	EVDEV_UDEV_TAG_ACCELEROMETER},
 	{"ID_INPUT_POINTINGSTICK",	EVDEV_UDEV_TAG_POINTINGSTICK},
@@ -2026,7 +2026,7 @@ evdev_configure_device(struct evdev_device *device)
 		 udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK ? " Pointingstick" : "",
 		 udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "",
 		 udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : "",
-		 udev_tags & EVDEV_UDEV_TAG_BUTTONSET ? " Buttonset" : "");
+		 udev_tags & EVDEV_UDEV_TAG_TABLET_PAD ? " TabletPad" : "");
 
 	if (udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER) {
 		log_info(libinput,
@@ -2044,14 +2044,6 @@ evdev_configure_device(struct evdev_device *device)
 		return -1;
 	}
 
-	/* libwacom assigns tablet _and_ tablet_pad to the pad devices */
-	if (udev_tags & EVDEV_UDEV_TAG_BUTTONSET) {
-		log_info(libinput,
-			 "input device '%s', %s is a buttonset, ignoring\n",
-			 device->devname, devnode);
-		return -1;
-	}
-
 	if (evdev_reject_device(device) == -1) {
 		log_info(libinput,
 			 "input device '%s', %s was rejected.\n",
@@ -2087,7 +2079,17 @@ evdev_configure_device(struct evdev_device *device)
 	tablet_tags = EVDEV_UDEV_TAG_TABLET |
 		      EVDEV_UDEV_TAG_TOUCHPAD |
 		      EVDEV_UDEV_TAG_TOUCHSCREEN;
-	if ((udev_tags & tablet_tags) == EVDEV_UDEV_TAG_TABLET) {
+
+	/* libwacom assigns tablet _and_ tablet_pad to the pad devices */
+	if (udev_tags & EVDEV_UDEV_TAG_TABLET_PAD) {
+		device->dispatch = evdev_tablet_pad_create(device);
+		device->seat_caps |= EVDEV_DEVICE_TABLET_PAD;
+		log_info(libinput,
+			 "input device '%s', %s is a tablet pad\n",
+			 device->devname, devnode);
+		return device->dispatch == NULL ? -1 : 0;
+
+	} else if ((udev_tags & tablet_tags) == EVDEV_UDEV_TAG_TABLET) {
 		device->dispatch = evdev_tablet_create(device);
 		device->seat_caps |= EVDEV_DEVICE_TABLET;
 		log_info(libinput,
@@ -2516,6 +2518,8 @@ evdev_device_has_capability(struct evdev_device *device,
 		return !!(device->seat_caps & EVDEV_DEVICE_GESTURE);
 	case LIBINPUT_DEVICE_CAP_TABLET_TOOL:
 		return !!(device->seat_caps & EVDEV_DEVICE_TABLET);
+	case LIBINPUT_DEVICE_CAP_TABLET_PAD:
+		return !!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD);
 	default:
 		return 0;
 	}
diff --git a/src/evdev.h b/src/evdev.h
index 482712b..79afc20 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -61,6 +61,7 @@ enum evdev_device_seat_capability {
 	EVDEV_DEVICE_KEYBOARD = (1 << 1),
 	EVDEV_DEVICE_TOUCH = (1 << 2),
 	EVDEV_DEVICE_TABLET = (1 << 3),
+	EVDEV_DEVICE_TABLET_PAD = (1 << 4),
 	EVDEV_DEVICE_GESTURE = (1 << 5),
 };
 
@@ -316,6 +317,9 @@ evdev_mt_touchpad_create(struct evdev_device *device);
 struct evdev_dispatch *
 evdev_tablet_create(struct evdev_device *device);
 
+struct evdev_dispatch *
+evdev_tablet_pad_create(struct evdev_device *device);
+
 void
 evdev_tag_touchpad(struct evdev_device *device,
 		   struct udev_device *udev_device);
@@ -366,6 +370,16 @@ evdev_device_has_button(struct evdev_device *device, uint32_t code);
 int
 evdev_device_has_key(struct evdev_device *device, uint32_t code);
 
+int
+evdev_device_tablet_pad_has_button(struct evdev_device *device,
+				   uint32_t code);
+
+int
+evdev_device_tablet_pad_get_num_rings(struct evdev_device *device);
+
+int
+evdev_device_tablet_pad_get_num_strips(struct evdev_device *device);
+
 double
 evdev_device_transform_x(struct evdev_device *device,
 			 double x,
diff --git a/src/libinput.c b/src/libinput.c
index 5f1cc1a..6a209ec 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -2799,19 +2799,20 @@ LIBINPUT_EXPORT int
 libinput_device_tablet_pad_has_button(struct libinput_device *device,
 				      uint32_t code)
 {
-	return 0;
+	return evdev_device_tablet_pad_has_button((struct evdev_device *)device,
+						  code);
 }
 
 LIBINPUT_EXPORT int
 libinput_device_tablet_pad_get_num_rings(struct libinput_device *device)
 {
-	return 0;
+	return evdev_device_tablet_pad_get_num_rings((struct evdev_device *)device);
 }
 
 LIBINPUT_EXPORT int
 libinput_device_tablet_pad_get_num_strips(struct libinput_device *device)
 {
-	return 0;
+	return evdev_device_tablet_pad_get_num_strips((struct evdev_device *)device);
 }
 
 LIBINPUT_EXPORT struct libinput_event *
-- 
2.5.0



More information about the wayland-devel mailing list