From: Benjamin Tissoires <[email protected]>

Same approach as evdev-tablet (started as copy/paste), with axis and buttons
adjusted. Wacom's handling of pad devices requires a lot of non-obvious
handling, e.g.  ABS_THROTTLE is the second ring, ABS_RX is the strip, etc.

This is not generic buttonset code, if we start supporting other devices for
buttonsets we'll factor out a couple of the functions.

The wheel and strip events are a bit of a problem: Wacom sends a 0 event on the
axis when the finger is released. We can detect this if there is an ABS_MISC 0
present in the same event and suppress it.  Won't work if any finger is down
on any other wheel, strip or button but that's a kernel bug we'll fix once we
figure out how.

Signed-off-by: Benjamin Tissoires <[email protected]>
Signed-off-by: Peter Hutterer <[email protected]>
---
 src/Makefile.am             |   2 +
 src/evdev-buttonset-wacom.c | 477 ++++++++++++++++++++++++++++++++++++++++++++
 src/evdev-buttonset-wacom.h | 111 +++++++++++
 src/evdev.c                 |  17 +-
 src/evdev.h                 |   3 +
 src/libinput-private.h      |  16 ++
 src/libinput.c              |  65 ++++++
 7 files changed, 688 insertions(+), 3 deletions(-)
 create mode 100644 src/evdev-buttonset-wacom.c
 create mode 100644 src/evdev-buttonset-wacom.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 2442794..b592d79 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -10,6 +10,8 @@ libinput_la_SOURCES =                 \
        libinput-private.h              \
        evdev.c                         \
        evdev.h                         \
+       evdev-buttonset-wacom.c         \
+       evdev-buttonset-wacom.h         \
        evdev-tablet.c                  \
        evdev-tablet.h                  \
        evdev-mt-touchpad.c             \
diff --git a/src/evdev-buttonset-wacom.c b/src/evdev-buttonset-wacom.c
new file mode 100644
index 0000000..9015c44
--- /dev/null
+++ b/src/evdev-buttonset-wacom.c
@@ -0,0 +1,477 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "evdev-buttonset-wacom.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+
+#define buttonset_set_status(buttonset_,s_) (buttonset_)->status |= (s_)
+#define buttonset_unset_status(buttonset_,s_) (buttonset_)->status &= ~(s_)
+#define buttonset_has_status(buttonset_,s_) (!!((buttonset_)->status & (s_)))
+
+static unsigned long *
+buttonset_get_pressed_buttons(struct buttonset_dispatch *buttonset)
+{
+       struct button_state *state = &buttonset->button_state;
+       struct button_state *prev_state = &buttonset->prev_button_state;
+       unsigned int i;
+
+       for (i = 0; i < NLONGS(KEY_CNT); i++)
+               state->buttons_pressed[i] = state->buttons[i]
+                                               & ~(prev_state->buttons[i]);
+
+       return state->buttons_pressed;
+}
+
+static unsigned long *
+buttonset_get_released_buttons(struct buttonset_dispatch *buttonset)
+{
+       struct button_state *state = &buttonset->button_state;
+       struct button_state *prev_state = &buttonset->prev_button_state;
+       unsigned int i;
+
+       for (i = 0; i < NLONGS(KEY_CNT); i++)
+               state->buttons_pressed[i] = prev_state->buttons[i]
+                                               & ~(state->buttons[i]);
+
+       return state->buttons_pressed;
+}
+
+static void
+buttonset_process_absolute(struct buttonset_dispatch *buttonset,
+                          struct evdev_device *device,
+                          struct input_event *e,
+                          uint32_t time)
+{
+       enum libinput_buttonset_axis axis;
+
+       switch (e->code) {
+       case ABS_WHEEL:
+       case ABS_THROTTLE:
+       case ABS_RX:
+       case ABS_RY:
+               axis = evcode_to_axis(e->code);
+               if (axis == LIBINPUT_BUTTONSET_AXIS_NONE) {
+                       log_bug_libinput(device->base.seat->libinput,
+                                        "Invalid ABS event code %#x\n",
+                                        e->code);
+                       break;
+               }
+
+               set_bit(buttonset->changed_axes, axis);
+               buttonset_set_status(buttonset, BUTTONSET_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.
+                */
+               buttonset->have_abs_misc_terminator = true;
+               break;
+       default:
+               log_info(device->base.seat->libinput,
+                        "Unhandled ABS event code %#x\n", e->code);
+               break;
+       }
+}
+
+static void
+buttonset_mark_all_axes_changed(struct buttonset_dispatch *buttonset,
+                               struct evdev_device *device)
+{
+       enum libinput_buttonset_axis a;
+
+       for (a = LIBINPUT_BUTTONSET_AXIS_RING; a <= 
LIBINPUT_BUTTONSET_AXIS_MAX; a++) {
+               if (libevdev_has_event_code(device->evdev,
+                                           EV_ABS,
+                                           axis_to_evcode(a)))
+                       set_bit(buttonset->changed_axes, a);
+       }
+
+       buttonset_set_status(buttonset, BUTTONSET_AXES_UPDATED);
+}
+
+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
+unnormalize_ring_value(const struct input_absinfo *absinfo,
+                      double value)
+{
+       double range = absinfo->maximum - absinfo->minimum + 1;
+
+       return value * range;
+}
+
+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 = 1,
+              max = log2(absinfo->maximum);
+       double range = max - min;
+       double value = (log2(absinfo->value) - min) / range;
+
+       return value;
+}
+
+/* Detect ring wraparound, current and old are normalized to [0, 1[ */
+static inline double
+guess_ring_delta(double current, double old)
+{
+       double d1, d2, d3;
+
+       d1 = current - old;
+       d2 = (current + 1) - old;
+       d3 = current - (old + 1);
+
+       if (fabs(d2) < fabs(d1))
+               d1 = d2;
+
+       if (fabs(d3) < fabs(d1))
+               d1 = d3;
+
+       return d1;
+}
+
+static void
+buttonset_check_notify_axes(struct buttonset_dispatch *buttonset,
+                           struct evdev_device *device,
+                           uint32_t time)
+{
+       struct libinput_device *base = &device->base;
+       bool axis_update_needed = false;
+       double deltas[LIBINPUT_BUTTONSET_AXIS_MAX + 1]  = {0};
+       double deltas_discrete[LIBINPUT_BUTTONSET_AXIS_MAX + 1]  = {0};
+       int a;
+
+       for (a = LIBINPUT_BUTTONSET_AXIS_RING;
+            a <= LIBINPUT_BUTTONSET_AXIS_MAX;
+            a++) {
+               const struct input_absinfo *absinfo;
+
+               if (!bit_is_set(buttonset->changed_axes, a))
+                       continue;
+
+               absinfo = libevdev_get_abs_info(device->evdev,
+                                               axis_to_evcode(a));
+
+               switch (a) {
+               case LIBINPUT_BUTTONSET_AXIS_RING:
+               case LIBINPUT_BUTTONSET_AXIS_RING2:
+                       buttonset->axes[a] = normalize_ring(absinfo);
+                       deltas[a] = guess_ring_delta(buttonset->axes[a],
+                                                    buttonset->axes_prev[a]);
+                       deltas_discrete[a] = unnormalize_ring_value(absinfo,
+                                                                   deltas[a]);
+                       break;
+               case LIBINPUT_BUTTONSET_AXIS_STRIP:
+               case LIBINPUT_BUTTONSET_AXIS_STRIP2:
+                       buttonset->axes[a] = normalize_strip(absinfo);
+                       deltas[a] = buttonset->axes[a] - 
buttonset->axes_prev[a];
+                       break;
+               default:
+                       log_bug_libinput(device->base.seat->libinput,
+                                        "Invalid axis update: %d\n", a);
+                       break;
+               }
+
+               if (buttonset->have_abs_misc_terminator) {
+                       /* Suppress the reset to 0 on finger up. See the
+                          comment in buttonset_process_absolute */
+                       if (libevdev_get_event_value(device->evdev,
+                                                    EV_ABS,
+                                                    ABS_MISC) == 0) {
+                               clear_bit(buttonset->changed_axes, a);
+                               buttonset->axes[a] = buttonset->axes_prev[a];
+                               continue;
+                       /* on finger down, reset the delta to 0 */
+                       } else {
+                               deltas[a] = 0;
+                               deltas_discrete[a] = 0;
+                       }
+               }
+
+               axis_update_needed = true;
+       }
+
+       if (axis_update_needed)
+               buttonset_notify_axis(base,
+                                     time,
+                                     LIBINPUT_BUTTONSET_AXIS_SOURCE_UNKNOWN,
+                                     buttonset->changed_axes,
+                                     buttonset->axes,
+                                     deltas,
+                                     deltas_discrete);
+
+       memset(buttonset->changed_axes, 0, sizeof(buttonset->changed_axes));
+       memcpy(buttonset->axes_prev,
+              buttonset->axes,
+              sizeof(buttonset->axes_prev));
+       buttonset->have_abs_misc_terminator = false;
+}
+
+static void
+buttonset_process_key(struct buttonset_dispatch *buttonset,
+                     struct evdev_device *device,
+                     struct input_event *e,
+                     uint32_t time)
+{
+       uint32_t button = e->code;
+       uint32_t enable = e->value;
+
+       if (enable) {
+               long_set_bit(buttonset->button_state.buttons, button);
+               buttonset_set_status(buttonset, BUTTONSET_BUTTONS_PRESSED);
+       } else {
+               long_clear_bit(buttonset->button_state.buttons, button);
+               buttonset_set_status(buttonset, BUTTONSET_BUTTONS_RELEASED);
+       }
+}
+
+static void
+buttonset_notify_button_mask(struct buttonset_dispatch *buttonset,
+                            struct evdev_device *device,
+                            uint32_t time,
+                            unsigned long *buttons,
+                            enum libinput_button_state state)
+{
+       struct libinput_device *base = &device->base;
+       int32_t num_button;
+       unsigned int i;
+
+       for (i = 0; i < NLONGS(KEY_CNT); i++) {
+               unsigned long buttons_slice = buttons[i];
+
+               num_button = i * LONG_BITS;
+               while (buttons_slice) {
+                       int enabled;
+
+                       num_button++;
+                       enabled = (buttons_slice & 1);
+                       buttons_slice >>= 1;
+
+                       if (!enabled)
+                               continue;
+
+                       buttonset_notify_button(base,
+                                               time,
+                                               buttonset->axes,
+                                               num_button - 1,
+                                               state);
+               }
+       }
+}
+
+static void
+buttonset_notify_buttons(struct buttonset_dispatch *buttonset,
+                        struct evdev_device *device,
+                        uint32_t time,
+                        enum libinput_button_state state)
+{
+       unsigned long *buttons;
+
+       if (state == LIBINPUT_BUTTON_STATE_PRESSED)
+               buttons = buttonset_get_pressed_buttons(buttonset);
+       else
+               buttons = buttonset_get_released_buttons(buttonset);
+
+       buttonset_notify_button_mask(buttonset,
+                                    device,
+                                    time,
+                                    buttons,
+                                    state);
+}
+
+static void
+sanitize_buttonset_axes(struct buttonset_dispatch *buttonset)
+{
+}
+
+static void
+buttonset_flush(struct buttonset_dispatch *buttonset,
+               struct evdev_device *device,
+               uint32_t time)
+{
+       if (buttonset_has_status(buttonset, BUTTONSET_AXES_UPDATED)) {
+               sanitize_buttonset_axes(buttonset);
+               buttonset_check_notify_axes(buttonset, device, time);
+               buttonset_unset_status(buttonset, BUTTONSET_AXES_UPDATED);
+       }
+
+       if (buttonset_has_status(buttonset, BUTTONSET_BUTTONS_RELEASED)) {
+               buttonset_notify_buttons(buttonset,
+                                        device,
+                                        time,
+                                        LIBINPUT_BUTTON_STATE_RELEASED);
+               buttonset_unset_status(buttonset, BUTTONSET_BUTTONS_RELEASED);
+       }
+
+       if (buttonset_has_status(buttonset, BUTTONSET_BUTTONS_PRESSED)) {
+               buttonset_notify_buttons(buttonset,
+                                        device,
+                                        time,
+                                        LIBINPUT_BUTTON_STATE_PRESSED);
+               buttonset_unset_status(buttonset, BUTTONSET_BUTTONS_PRESSED);
+       }
+
+       /* Update state */
+       memcpy(&buttonset->prev_button_state,
+              &buttonset->button_state,
+              sizeof(buttonset->button_state));
+}
+
+static void
+buttonset_process(struct evdev_dispatch *dispatch,
+                 struct evdev_device *device,
+                 struct input_event *e,
+                 uint64_t time)
+{
+       struct buttonset_dispatch *buttonset =
+               (struct buttonset_dispatch *)dispatch;
+
+       switch (e->type) {
+       case EV_ABS:
+               buttonset_process_absolute(buttonset, device, e, time);
+               break;
+       case EV_KEY:
+               buttonset_process_key(buttonset, device, e, time);
+               break;
+       case EV_SYN:
+               buttonset_flush(buttonset, 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
+buttonset_destroy(struct evdev_dispatch *dispatch)
+{
+       struct buttonset_dispatch *buttonset =
+               (struct buttonset_dispatch*)dispatch;
+
+       free(buttonset);
+}
+
+static double
+buttonset_transform_to_phys(struct evdev_device *device,
+                           enum libinput_buttonset_axis axis,
+                           double value)
+{
+       switch(axis) {
+       case LIBINPUT_BUTTONSET_AXIS_RING:
+       case LIBINPUT_BUTTONSET_AXIS_RING2:
+               value *= 360;
+               break;
+       case LIBINPUT_BUTTONSET_AXIS_STRIP:
+       case LIBINPUT_BUTTONSET_AXIS_STRIP2:
+               value *= 52; /* FIXME: correct for Intuos3 and 21UX */
+               break;
+       }
+
+       return value;
+}
+
+static struct evdev_dispatch_interface buttonset_interface = {
+       buttonset_process,
+       NULL, /* remove */
+       buttonset_destroy,
+       NULL, /* device_added */
+       NULL, /* device_removed */
+       NULL, /* device_suspended */
+       NULL, /* device_resumed */
+       NULL, /* tag_device */
+       buttonset_transform_to_phys,
+};
+
+static int
+buttonset_init(struct buttonset_dispatch *buttonset,
+              struct evdev_device *device)
+{
+       enum libinput_buttonset_axis axis;
+
+       buttonset->base.interface = &buttonset_interface;
+       buttonset->device = device;
+       buttonset->status = BUTTONSET_NONE;
+
+       for (axis = LIBINPUT_BUTTONSET_AXIS_RING;
+            axis <= LIBINPUT_BUTTONSET_AXIS_MAX;
+            axis++) {
+               if (libevdev_has_event_code(device->evdev,
+                                           EV_ABS,
+                                           axis_to_evcode(axis)))
+                       set_bit(buttonset->axis_caps, axis);
+       }
+
+       buttonset_mark_all_axes_changed(buttonset, device);
+
+       return 0;
+}
+
+struct evdev_dispatch *
+evdev_buttonset_create(struct evdev_device *device)
+{
+       struct buttonset_dispatch *buttonset;
+
+       buttonset = zalloc(sizeof *buttonset);
+       if (!buttonset)
+               return NULL;
+
+       if (buttonset_init(buttonset, device) != 0) {
+               buttonset_destroy(&buttonset->base);
+               return NULL;
+       }
+
+       return &buttonset->base;
+}
diff --git a/src/evdev-buttonset-wacom.h b/src/evdev-buttonset-wacom.h
new file mode 100644
index 0000000..60b1c81
--- /dev/null
+++ b/src/evdev-buttonset-wacom.h
@@ -0,0 +1,111 @@
+/*
+ * 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_H
+#define EVDEV_BUTTONSET_H
+
+#include "evdev.h"
+
+#define LIBINPUT_BUTTONSET_AXIS_NONE 0
+
+enum buttonset_status {
+       BUTTONSET_NONE = 0,
+       BUTTONSET_AXES_UPDATED = 1 << 0,
+       BUTTONSET_BUTTONS_PRESSED = 1 << 1,
+       BUTTONSET_BUTTONS_RELEASED = 1 << 2,
+};
+
+struct button_state {
+       /* Bitmask of pressed buttons. */
+       unsigned long buttons[NLONGS(KEY_CNT)];
+       unsigned long buttons_pressed[NLONGS(KEY_CNT)];
+       unsigned long buttons_released[NLONGS(KEY_CNT)];
+};
+
+struct buttonset_dispatch {
+       struct evdev_dispatch base;
+       struct evdev_device *device;
+       unsigned char status;
+       unsigned char changed_axes[NCHARS(LIBINPUT_BUTTONSET_AXIS_MAX + 1)];
+       double axes[LIBINPUT_BUTTONSET_AXIS_MAX + 1];
+       double axes_prev[LIBINPUT_BUTTONSET_AXIS_MAX + 1];
+       unsigned char axis_caps[NCHARS(LIBINPUT_BUTTONSET_AXIS_MAX + 1)];
+
+       struct button_state button_state;
+       struct button_state prev_button_state;
+
+       bool have_abs_misc_terminator;
+};
+
+static inline enum libinput_buttonset_axis
+evcode_to_axis(const uint32_t evcode)
+{
+       enum libinput_buttonset_axis axis;
+
+       switch (evcode) {
+       case ABS_WHEEL:
+               axis = LIBINPUT_BUTTONSET_AXIS_RING;
+               break;
+       case ABS_THROTTLE:
+               axis = LIBINPUT_BUTTONSET_AXIS_RING2;
+               break;
+       case ABS_RX:
+               axis = LIBINPUT_BUTTONSET_AXIS_STRIP;
+               break;
+       case ABS_RY:
+               axis = LIBINPUT_BUTTONSET_AXIS_STRIP2;
+               break;
+       default:
+               axis = LIBINPUT_BUTTONSET_AXIS_NONE;
+               break;
+       }
+
+       return axis;
+}
+
+static inline uint32_t
+axis_to_evcode(const enum libinput_buttonset_axis axis)
+{
+       uint32_t evcode;
+
+       switch (axis) {
+       case LIBINPUT_BUTTONSET_AXIS_RING:
+               evcode = ABS_WHEEL;
+               break;
+       case LIBINPUT_BUTTONSET_AXIS_RING2:
+               evcode = ABS_THROTTLE;
+               break;
+       case LIBINPUT_BUTTONSET_AXIS_STRIP:
+               evcode = ABS_RX;
+               break;
+       case LIBINPUT_BUTTONSET_AXIS_STRIP2:
+               evcode = ABS_RY;
+               break;
+       default:
+               abort();
+       }
+
+       return evcode;
+}
+
+#endif
diff --git a/src/evdev.c b/src/evdev.c
index f7fccdf..f8e99f3 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -58,6 +58,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),
 };
 
 struct evdev_udev_tag_match {
@@ -73,6 +74,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_JOYSTICK",           EVDEV_UDEV_TAG_JOYSTICK},
        {"ID_INPUT_ACCELEROMETER",      EVDEV_UDEV_TAG_ACCELEROMETER},
 
@@ -1406,7 +1408,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%s\n",
+                "input device '%s', %s is tagged by udev 
as:%s%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" : "",
@@ -1414,7 +1416,8 @@ evdev_configure_device(struct evdev_device *device)
                 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" : 
"");
+                udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : 
"",
+                udev_tags & EVDEV_UDEV_TAG_BUTTONSET ? " Buttonset" : "");
 
        /* libwacom *adds* TABLET, TOUCHPAD but leaves JOYSTICK in place, so
           make sure we only ignore real joystick devices */
@@ -1498,10 +1501,18 @@ evdev_configure_device(struct evdev_device *device)
                }
        }
 
+       /* libwacom assigns tablet _and_ tablet_pad to the pad devices */
+       if (udev_tags & EVDEV_UDEV_TAG_BUTTONSET) {
+               device->dispatch = evdev_buttonset_create(device);
+               device->seat_caps |= EVDEV_DEVICE_BUTTONSET;
+               log_info(libinput,
+                        "input device '%s', %s is a buttonset\n",
+                        device->devname, devnode);
+               return device->dispatch == NULL ? -1 : 0;
        /* 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)) ==
+       } else 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;
diff --git a/src/evdev.h b/src/evdev.h
index 918ee2f..8c35c10 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -229,6 +229,9 @@ evdev_mt_touchpad_create(struct evdev_device *device);
 struct evdev_dispatch *
 evdev_tablet_create(struct evdev_device *device);
 
+struct evdev_dispatch *
+evdev_buttonset_create(struct evdev_device *device);
+
 void
 evdev_device_led_update(struct evdev_device *device, enum libinput_led leds);
 
diff --git a/src/libinput-private.h b/src/libinput-private.h
index b876779..3f4198d 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -373,6 +373,22 @@ void
 touch_notify_frame(struct libinput_device *device,
                   uint64_t time);
 
+void
+buttonset_notify_axis(struct libinput_device *device,
+                     uint32_t time,
+                     enum libinput_buttonset_axis_source source,
+                     unsigned char *changed_axes,
+                     double *axes,
+                     double *deltas,
+                     double *deltas_discrete);
+
+void
+buttonset_notify_button(struct libinput_device *device,
+                       uint32_t time,
+                       double *axes,
+                       int32_t button,
+                       enum libinput_button_state state);
+
 static inline uint64_t
 libinput_now(struct libinput *libinput)
 {
diff --git a/src/libinput.c b/src/libinput.c
index 732f9fe..1b1dadc 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -1651,6 +1651,71 @@ tablet_notify_button(struct libinput_device *device,
                          &button_event->base);
 }
 
+void
+buttonset_notify_axis(struct libinput_device *device,
+                  uint32_t time,
+                  enum libinput_buttonset_axis_source source,
+                  unsigned char *changed_axes,
+                  double *axes,
+                  double *deltas,
+                  double *deltas_discrete)
+{
+       struct libinput_event_buttonset *axis_event;
+
+       axis_event = zalloc(sizeof *axis_event);
+       if (!axis_event)
+               return;
+
+       *axis_event = (struct libinput_event_buttonset) {
+               .time = time,
+               .source = source,
+       };
+
+       memcpy(axis_event->changed_axes,
+              changed_axes,
+              sizeof(axis_event->changed_axes));
+       memcpy(axis_event->axes, axes, sizeof(axis_event->axes));
+       memcpy(axis_event->deltas, deltas, sizeof(axis_event->deltas));
+       memcpy(axis_event->deltas_discrete, deltas_discrete, 
sizeof(axis_event->deltas_discrete));
+
+       post_device_event(device,
+                         time,
+                         LIBINPUT_EVENT_BUTTONSET_AXIS,
+                         &axis_event->base);
+}
+
+void
+buttonset_notify_button(struct libinput_device *device,
+                       uint32_t time,
+                       double *axes,
+                       int32_t button,
+                       enum libinput_button_state state)
+{
+       struct libinput_event_buttonset *button_event;
+       int32_t seat_button_count;
+
+       button_event = zalloc(sizeof *button_event);
+       if (!button_event)
+               return;
+
+       seat_button_count = update_seat_button_count(device->seat,
+                                                    button,
+                                                    state);
+
+       *button_event = (struct libinput_event_buttonset) {
+               .time = time,
+               .button = button,
+               .state = state,
+               .seat_button_count = seat_button_count,
+       };
+       memcpy(button_event->axes, axes, sizeof(button_event->axes));
+
+       post_device_event(device,
+                         time,
+                         LIBINPUT_EVENT_BUTTONSET_BUTTON,
+                         &button_event->base);
+}
+
 static void
 libinput_post_event(struct libinput *libinput,
                    struct libinput_event *event)
-- 
2.1.0

_______________________________________________
wayland-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to