Signed-off-by: Peter Hutterer <peter.hutte...@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

_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to