[RFC v2 libinput 2/2] buttonset: implement buttonset handling for Wacom tablet pads
Peter Hutterer
peter.hutterer at who-t.net
Tue Mar 17 23:58:05 PDT 2015
From: Benjamin Tissoires <benjamin.tissoires at gmail.com>
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 <benjamin.tissoires at redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
src/Makefile.am | 2 +
src/evdev-buttonset-wacom.c | 533 ++++++++++++++++++++++++++++++++++++++++++++
src/evdev-buttonset-wacom.h | 63 ++++++
src/evdev.c | 18 +-
src/evdev.h | 3 +
src/libinput-private.h | 16 ++
src/libinput.c | 65 ++++++
7 files changed, 691 insertions(+), 9 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 d5cd4f4..3e36f63 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..402c6f5
--- /dev/null
+++ b/src/evdev-buttonset-wacom.c
@@ -0,0 +1,533 @@
+/*
+ * 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)
+{
+ unsigned int axis;
+
+ switch (e->code) {
+ case ABS_WHEEL:
+ case ABS_THROTTLE:
+ case ABS_RX:
+ case ABS_RY:
+ axis = buttonset->evcode_map[e->code];
+ if (axis == (unsigned int)-1) {
+ log_bug_libinput(device->base.seat->libinput,
+ "Unhandled EV_ABS mapping for %#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 EV_ABS event code %#x\n", e->code);
+ break;
+ }
+}
+
+static void
+buttonset_mark_all_axes_changed(struct buttonset_dispatch *buttonset,
+ struct evdev_device *device)
+{
+ unsigned int a;
+
+ for (a = 0; a < buttonset->naxes; 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_MAX_NUM_AXES] = {0};
+ double deltas_discrete[LIBINPUT_BUTTONSET_MAX_NUM_AXES] = {0};
+ unsigned int a;
+ unsigned int code;
+
+ for (a = 0; a <= buttonset->naxes; a++) {
+ const struct input_absinfo *absinfo;
+
+ if (!bit_is_set(buttonset->changed_axes, a))
+ continue;
+
+ code = buttonset->axis_map[a];
+ assert(code != 0);
+ absinfo = libevdev_get_abs_info(device->evdev, code);
+ assert(absinfo);
+
+ switch (buttonset->types[a]) {
+ case LIBINPUT_BUTTONSET_AXIS_RING:
+ 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:
+ 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: %u\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_to_phys(struct evdev_device *device, unsigned int axis, double value)
+{
+ struct buttonset_dispatch *buttonset =
+ (struct buttonset_dispatch*)device->dispatch;
+
+ switch(buttonset->types[axis]) {
+ case LIBINPUT_BUTTONSET_AXIS_NONE:
+ case LIBINPUT_BUTTONSET_AXIS_X:
+ case LIBINPUT_BUTTONSET_AXIS_Y:
+ case LIBINPUT_BUTTONSET_AXIS_Z:
+ case LIBINPUT_BUTTONSET_AXIS_REL_X:
+ case LIBINPUT_BUTTONSET_AXIS_REL_Y:
+ case LIBINPUT_BUTTONSET_AXIS_REL_Z:
+ case LIBINPUT_BUTTONSET_AXIS_ROTATION_X:
+ case LIBINPUT_BUTTONSET_AXIS_ROTATION_Y:
+ case LIBINPUT_BUTTONSET_AXIS_ROTATION_Z:
+ abort();
+ case LIBINPUT_BUTTONSET_AXIS_RING:
+ value *= 360;
+ break;
+ case LIBINPUT_BUTTONSET_AXIS_STRIP:
+ value *= 52; /* FIXME: correct for Intuos3 and 21UX */
+ break;
+ }
+
+ return value;
+}
+
+static unsigned int
+buttonset_get_num_axes(struct evdev_device *device)
+{
+ struct buttonset_dispatch *buttonset =
+ (struct buttonset_dispatch*)device->dispatch;
+
+ return buttonset->naxes;
+}
+
+static enum libinput_buttonset_axis_type
+buttonset_get_axis_type(struct evdev_device *device, unsigned int axis)
+{
+ struct buttonset_dispatch *buttonset =
+ (struct buttonset_dispatch*)device->dispatch;
+ if (axis < buttonset->naxes)
+ return buttonset->types[axis];
+ else
+ return LIBINPUT_BUTTONSET_AXIS_NONE;
+}
+
+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 */
+ NULL, /* post_added */
+ buttonset_to_phys,
+ buttonset_get_num_axes,
+ buttonset_get_axis_type,
+};
+
+static enum libinput_buttonset_axis_type
+buttonset_guess_axis_type(struct evdev_device *device,
+ unsigned int evcode)
+{
+ switch (evcode) {
+ case ABS_WHEEL:
+ case ABS_THROTTLE:
+ return LIBINPUT_BUTTONSET_AXIS_RING;
+ case ABS_RX:
+ case ABS_RY:
+ return LIBINPUT_BUTTONSET_AXIS_STRIP;
+ default:
+ return LIBINPUT_BUTTONSET_AXIS_NONE;
+ }
+}
+
+static int
+buttonset_init(struct buttonset_dispatch *buttonset,
+ struct evdev_device *device)
+{
+ unsigned int naxes = 0;
+ int code;
+ enum libinput_buttonset_axis_type type;
+
+ buttonset->base.interface = &buttonset_interface;
+ buttonset->device = device;
+ buttonset->status = BUTTONSET_NONE;
+
+ /* We intentionally skip X/Y, they're dead on wacom pads */
+ for (code = ABS_Z; code <= ABS_MAX; code++) {
+ buttonset->evcode_map[code] = -1;
+
+ if (!libevdev_has_event_code(device->evdev, EV_ABS, code))
+ continue;
+
+ /* Ignore axes we don't know about */
+ type = buttonset_guess_axis_type(device, code);
+ if (type == LIBINPUT_BUTTONSET_AXIS_NONE)
+ continue;
+
+ buttonset->axis_map[naxes] = code;
+ buttonset->evcode_map[code] = naxes;
+ buttonset->types[naxes] = type;
+ naxes++;
+ }
+
+ buttonset->naxes = naxes;
+ 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..b3fa440
--- /dev/null
+++ b/src/evdev-buttonset-wacom.h
@@ -0,0 +1,63 @@
+/*
+ * 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 int naxes;
+ unsigned int axis_map[LIBINPUT_BUTTONSET_MAX_NUM_AXES]; /* number to evcode */
+ unsigned int evcode_map[ABS_CNT]; /* evcode to number */
+ unsigned char changed_axes[NCHARS(LIBINPUT_BUTTONSET_MAX_NUM_AXES)];
+ enum libinput_buttonset_axis_type types[LIBINPUT_BUTTONSET_MAX_NUM_AXES];
+
+ double axes[LIBINPUT_BUTTONSET_MAX_NUM_AXES];
+ double axes_prev[LIBINPUT_BUTTONSET_MAX_NUM_AXES];
+
+ struct button_state button_state;
+ struct button_state prev_button_state;
+
+ bool have_abs_misc_terminator;
+};
+
+#endif
diff --git a/src/evdev.c b/src/evdev.c
index 4beacaa..9ab027d 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -1583,14 +1583,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",
@@ -1622,10 +1614,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 a8d2d38..0221ea8 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -254,6 +254,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 5ce8fdd..feeb7c1 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -382,6 +382,22 @@ tablet_notify_button(struct libinput_device *device,
int32_t button,
enum libinput_button_state state);
+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 d52ca0d..30c0599 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -1799,6 +1799,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.3.2
More information about the wayland-devel
mailing list