diff --git a/doc/svg/tablet-interfaces.svg b/doc/svg/tablet-interfaces.svg
new file mode 100644
index 00000000..d64fe9f3
--- /dev/null
+++ b/doc/svg/tablet-interfaces.svg
@@ -0,0 +1,325 @@
+
+
+
+
diff --git a/doc/tablet-support.dox b/doc/tablet-support.dox
index 35b92303..0a44010b 100644
--- a/doc/tablet-support.dox
+++ b/doc/tablet-support.dox
@@ -8,15 +8,23 @@ Apple iPad.
@image html tablet.svg "Illustration of a graphics tablet"
-@section tablet-tools Tablet buttons vs. tablet tools
+@section tablet-tools Pad buttons vs. tablet tools
-Most tablets provide two types of devices. The pysical tablet often provides
-a number of buttons and a touch ring or strip. Interaction on the drawing
-surface of the tablet requires a tool, usually in the shape of a stylus.
-The libinput interface exposed by devices with the @ref
+Most tablets provide two types of devices. The physical tablet often
+provides a number of buttons and a touch ring or strip. Interaction on the
+drawing surface of the tablet requires a tool, usually in the shape of a
+stylus. The libinput interface exposed by devices with the @ref
LIBINPUT_DEVICE_CAP_TABLET_TOOL capability applies only to events generated
by tools.
+Buttons, rings or strips on the physical tablet hardware (the "pad") are
+exposed by devices with the @ref LIBINPUT_DEVICE_CAP_TABLET_PAD capability.
+Pad events do not require a tool to be in proximity. Note that both
+capabilities may exist on the same device though usually they are split
+across multiple kernel devices.
+
+@image html tablet-interfaces.svg "Difference between Pad and Tool buttons"
+
Touch events on the tablet integrated into a screen itself are exposed
through the @ref LIBINPUT_DEVICE_CAP_TOUCH capability. Touch events on a
standalone tablet are exposed through the @ref LIBINPUT_DEVICE_CAP_POINTER
@@ -25,7 +33,7 @@ node for the touch device, resulting in a separate libinput device.
See libinput_device_get_device_group() for information on how to associate
the touch part with other devices exposed by the same physical hardware.
-@section tablet-tip Tool tip events vs. button events
+@section tablet-tip Tool tip events vs. tool button events
The primary use of a tablet tool is to draw on the surface of the tablet.
When the tool tip comes into contact with the surface, libinput sends an
@@ -113,6 +121,8 @@ tablets however extends further than the user may lift the mouse, i.e. the
tool may not be lifted out of physical proximity. For such tools, libinput
provides software-emulated proximity.
+Events from the pad do not require proximity, they may be sent any time.
+
@section tablet-pressure-offset Pressure offset on worn-out tools
When a tool is used for an extended period it can wear down physically. A
@@ -209,4 +219,30 @@ libinput_event_tablet_tool_get_y_transformed() the resulting value may be
less than 0 or larger than the upper range provided. It is up to the caller
to test for this and handle or ignore these events accordingly.
+@section tablet-pad-buttons Tablet pad button numbers
+
+Tablet Pad buttons are numbered sequentially, starting with button 0. Thus
+button numbers returned by libinput_event_tablet_pad_get_button_number()
+have no semantic meaning, a notable difference to the button codes returned
+by other libinput interfaces (e.g. libinput_event_tablet_tool_get_button()).
+
+The Linux kernel requires all input events to have semantic event codes,
+but generic buttons like those on a pad cannot easily be assigned semantic
+codes. The kernel supports generic codes in the form of BTN_0 through to
+BTN_9 and additional unnamed space up until code 0x10f. Additional generic
+buttons are available as BTN_A in the range dedicated for gamepads and
+joysticks. Thus, tablet with a large number of buttons have to map across
+two semantic ranges, have to use unnamed kernel button codes or risk leaking
+into an unrelated range. libinput transparently maps the kernel event codes
+into a sequential button range on the pad. Callers should use external
+sources like libwacom to associate button numbers to their position on the
+tablet.
+
+Some buttons may have expected default behaviors. For example, on Wacom
+Intuos Pro series tablets, the button inside the touch ring is expected to
+switch between a mode switch. Mode switching is a feature implemented in the
+caller and libinput does not provide specific handling. Callers should use
+external sources like libwacom to identify which buttons have semantic
+behaviors.
+
*/
diff --git a/src/Makefile.am b/src/Makefile.am
index 343e75c7..a3df6c8f 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 00000000..ad6ead84
--- /dev/null
+++ b/src/evdev-tablet-pad.c
@@ -0,0 +1,620 @@
+/*
+ * 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
+#include
+#include
+
+#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 bool
+pad_any_button_down(const struct pad_dispatch *pad)
+{
+ const struct button_state *state = &pad->button_state;
+ unsigned int i;
+
+ for (i = 0; i < sizeof(state->bits); i++)
+ if (state->bits[i] != 0)
+ return true;
+
+ return false;
+}
+
+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;
+ double degrees;
+
+ absinfo = libevdev_get_abs_info(device->evdev, code);
+ assert(absinfo);
+
+ degrees = normalize_ring(absinfo) * 360;
+
+ if (device->left_handed.enabled)
+ degrees = fmod(degrees + 180, 360);
+
+ return degrees;
+}
+
+static inline double
+pad_handle_strip(struct pad_dispatch *pad,
+ struct evdev_device *device,
+ unsigned int code)
+{
+ const struct input_absinfo *absinfo;
+ double pos;
+
+ absinfo = libevdev_get_abs_info(device->evdev, code);
+ assert(absinfo);
+
+ if (absinfo->value == 0)
+ return 0.0;
+
+ pos = normalize_strip(absinfo);
+
+ if (device->left_handed.enabled)
+ pos = 1.0 - pos;
+
+ return pos;
+}
+
+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 code;
+ unsigned int i;
+
+ for (i = 0; i < sizeof(buttons->bits); i++) {
+ unsigned char buttons_slice = buttons->bits[i];
+
+ code = i * 8;
+ while (buttons_slice) {
+ int enabled;
+ char map;
+
+ code++;
+ enabled = (buttons_slice & 1);
+ buttons_slice >>= 1;
+
+ if (!enabled)
+ continue;
+
+ map = pad->button_map[code - 1];
+ if (map != -1)
+ tablet_pad_notify_button(base, time, map, 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_change_to_left_handed(struct evdev_device *device)
+{
+ struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch;
+
+ if (device->left_handed.enabled == device->left_handed.want_enabled)
+ return;
+
+ if (pad_any_button_down(pad))
+ return;
+
+ device->left_handed.enabled = device->left_handed.want_enabled;
+}
+
+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);
+
+ pad_change_to_left_handed(device);
+ }
+
+ 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 void
+pad_init_buttons(struct pad_dispatch *pad,
+ struct evdev_device *device)
+{
+ unsigned int code;
+ size_t i;
+ int map = 0;
+
+ for (i = 0; i < ARRAY_LENGTH(pad->button_map); i++)
+ pad->button_map[i] = -1;
+
+ /* we match wacom_report_numbered_buttons() from the kernel */
+ for (code = BTN_0; code < BTN_0 + 10; code++) {
+ if (libevdev_has_event_code(device->evdev, EV_KEY, code))
+ pad->button_map[code] = map++;
+ }
+
+ for (code = BTN_A; code < BTN_A + 6; code++) {
+ if (libevdev_has_event_code(device->evdev, EV_KEY, code))
+ pad->button_map[code] = map++;
+ }
+
+ for (code = BTN_BASE; code < BTN_BASE + 2; code++) {
+ if (libevdev_has_event_code(device->evdev, EV_KEY, code))
+ pad->button_map[code] = map++;
+ }
+
+ pad->nbuttons = map;
+}
+
+static void
+pad_init_left_handed(struct evdev_device *device)
+{
+ if (evdev_tablet_has_left_handed(device))
+ evdev_init_left_handed(device,
+ pad_change_to_left_handed);
+}
+
+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;
+
+ pad_init_buttons(pad, device);
+ pad_init_left_handed(device);
+
+ 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_get_num_buttons(struct evdev_device *device)
+{
+ struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch;
+
+ if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
+ return -1;
+
+ return pad->nbuttons;
+}
+
+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 00000000..828ded8f
--- /dev/null
+++ b/src/evdev-tablet-pad.h
@@ -0,0 +1,69 @@
+/*
+ * 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;
+
+ char button_map[KEY_CNT];
+ unsigned int nbuttons;
+
+ 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-tablet.c b/src/evdev-tablet.c
index be828d96..5262230c 100644
--- a/src/evdev-tablet.c
+++ b/src/evdev-tablet.c
@@ -1606,45 +1606,9 @@ tablet_init_accel(struct tablet_dispatch *tablet, struct evdev_device *device)
static void
tablet_init_left_handed(struct evdev_device *device)
{
-#if HAVE_LIBWACOM
- struct libinput *libinput = device->base.seat->libinput;
- WacomDeviceDatabase *db;
- WacomDevice *d = NULL;
- WacomError *error;
- const char *devnode;
-
- db = libwacom_database_new();
- if (!db) {
- log_info(libinput,
- "Failed to initialize libwacom context.\n");
- return;
- }
- error = libwacom_error_new();
- devnode = udev_device_get_devnode(device->udev_device);
-
- d = libwacom_new_from_path(db,
- devnode,
- WFALLBACK_NONE,
- error);
-
- if (d) {
- if (libwacom_is_reversible(d))
+ if (evdev_tablet_has_left_handed(device))
evdev_init_left_handed(device,
tablet_change_to_left_handed);
- } else if (libwacom_error_get_code(error) == WERROR_UNKNOWN_MODEL) {
- log_info(libinput, "Tablet unknown to libwacom\n");
- } else {
- log_error(libinput,
- "libwacom error: %s\n",
- libwacom_error_get_message(error));
- }
-
- if (error)
- libwacom_error_free(&error);
- if (d)
- libwacom_destroy(d);
- libwacom_database_destroy(db);
-#endif
}
static int
diff --git a/src/evdev.c b/src/evdev.c
index bfa61806..afc4710c 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -43,6 +43,10 @@
#include "filter.h"
#include "libinput-private.h"
+#if HAVE_LIBWACOM
+#include
+#endif
+
#define DEFAULT_WHEEL_CLICK_ANGLE 15
#define DEFAULT_MIDDLE_BUTTON_SCROLL_TIMEOUT ms2us(200)
@@ -61,7 +65,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 +82,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},
@@ -2082,7 +2086,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,
@@ -2100,14 +2104,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",
@@ -2143,7 +2139,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,
@@ -2572,6 +2578,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;
}
@@ -2913,3 +2921,51 @@ evdev_device_destroy(struct evdev_device *device)
free(device->mt.slots);
free(device);
}
+
+bool
+evdev_tablet_has_left_handed(struct evdev_device *device)
+{
+ bool has_left_handed = false;
+#if HAVE_LIBWACOM
+ struct libinput *libinput = device->base.seat->libinput;
+ WacomDeviceDatabase *db;
+ WacomDevice *d = NULL;
+ WacomError *error;
+ const char *devnode;
+
+ db = libwacom_database_new();
+ if (!db) {
+ log_info(libinput,
+ "Failed to initialize libwacom context.\n");
+ goto out;
+ }
+
+ error = libwacom_error_new();
+ devnode = udev_device_get_devnode(device->udev_device);
+
+ d = libwacom_new_from_path(db,
+ devnode,
+ WFALLBACK_NONE,
+ error);
+
+ if (d) {
+ if (libwacom_is_reversible(d))
+ has_left_handed = true;
+ } else if (libwacom_error_get_code(error) == WERROR_UNKNOWN_MODEL) {
+ log_info(libinput, "Tablet unknown to libwacom\n");
+ } else {
+ log_error(libinput,
+ "libwacom error: %s\n",
+ libwacom_error_get_message(error));
+ }
+
+ if (error)
+ libwacom_error_free(&error);
+ if (d)
+ libwacom_destroy(d);
+ libwacom_database_destroy(db);
+
+out:
+#endif
+ return has_left_handed;
+}
diff --git a/src/evdev.h b/src/evdev.h
index 1e38b5e6..db6d83fa 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),
};
@@ -322,6 +323,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);
@@ -372,6 +376,15 @@ 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_get_num_buttons(struct evdev_device *device);
+
+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,
@@ -459,6 +472,9 @@ int
evdev_init_left_handed(struct evdev_device *device,
void (*change_to_left_handed)(struct evdev_device *));
+bool
+evdev_tablet_has_left_handed(struct evdev_device *device);
+
static inline uint32_t
evdev_to_left_handed(struct evdev_device *device,
uint32_t button)
diff --git a/src/libinput-private.h b/src/libinput-private.h
index 8d2492aa..539e69a8 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -544,6 +544,24 @@ tablet_notify_button(struct libinput_device *device,
int32_t button,
enum libinput_button_state state);
+void
+tablet_pad_notify_button(struct libinput_device *device,
+ uint64_t time,
+ int32_t button,
+ enum libinput_button_state state);
+void
+tablet_pad_notify_ring(struct libinput_device *device,
+ uint64_t time,
+ unsigned int number,
+ double value,
+ enum libinput_tablet_pad_ring_axis_source source);
+void
+tablet_pad_notify_strip(struct libinput_device *device,
+ uint64_t time,
+ unsigned int number,
+ double value,
+ enum libinput_tablet_pad_strip_axis_source source);
+
static inline uint64_t
libinput_now(struct libinput *libinput)
{
diff --git a/src/libinput.c b/src/libinput.c
index b6e660a1..bcd0dcd0 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -138,6 +138,23 @@ struct libinput_event_tablet_tool {
enum libinput_tablet_tool_tip_state tip_state;
};
+struct libinput_event_tablet_pad {
+ struct libinput_event base;
+ uint32_t button;
+ enum libinput_button_state state;
+ uint64_t time;
+ struct {
+ enum libinput_tablet_pad_ring_axis_source source;
+ double position;
+ int number;
+ } ring;
+ struct {
+ enum libinput_tablet_pad_strip_axis_source source;
+ double position;
+ int number;
+ } strip;
+};
+
static void
libinput_default_log_func(struct libinput *libinput,
enum libinput_log_priority priority,
@@ -318,6 +335,19 @@ libinput_event_get_tablet_tool_event(struct libinput_event *event)
return (struct libinput_event_tablet_tool *) event;
}
+LIBINPUT_EXPORT struct libinput_event_tablet_pad *
+libinput_event_get_tablet_pad_event(struct libinput_event *event)
+{
+ require_event_type(libinput_event_get_context(event),
+ event->type,
+ NULL,
+ LIBINPUT_EVENT_TABLET_PAD_RING,
+ LIBINPUT_EVENT_TABLET_PAD_STRIP,
+ LIBINPUT_EVENT_TABLET_PAD_BUTTON);
+
+ return (struct libinput_event_tablet_pad *) event;
+}
+
LIBINPUT_EXPORT struct libinput_event_device_notify *
libinput_event_get_device_notify_event(struct libinput_event *event)
{
@@ -1956,6 +1986,9 @@ device_has_cap(struct libinput_device *device,
case LIBINPUT_DEVICE_CAP_TABLET_TOOL:
capability = "CAP_TABLET_TOOL";
break;
+ case LIBINPUT_DEVICE_CAP_TABLET_PAD:
+ capability = "CAP_TABLET_PAD";
+ break;
}
log_bug_libinput(device->seat->libinput,
@@ -2343,6 +2376,82 @@ tablet_notify_button(struct libinput_device *device,
&button_event->base);
}
+void
+tablet_pad_notify_button(struct libinput_device *device,
+ uint64_t time,
+ int32_t button,
+ enum libinput_button_state state)
+{
+ struct libinput_event_tablet_pad *button_event;
+
+ button_event = zalloc(sizeof *button_event);
+ if (!button_event)
+ return;
+
+ *button_event = (struct libinput_event_tablet_pad) {
+ .time = time,
+ .button = button,
+ .state = state,
+ };
+
+ post_device_event(device,
+ time,
+ LIBINPUT_EVENT_TABLET_PAD_BUTTON,
+ &button_event->base);
+}
+
+void
+tablet_pad_notify_ring(struct libinput_device *device,
+ uint64_t time,
+ unsigned int number,
+ double value,
+ enum libinput_tablet_pad_ring_axis_source source)
+{
+ struct libinput_event_tablet_pad *ring_event;
+
+ ring_event = zalloc(sizeof *ring_event);
+ if (!ring_event)
+ return;
+
+ *ring_event = (struct libinput_event_tablet_pad) {
+ .time = time,
+ .ring.number = number,
+ .ring.position = value,
+ .ring.source = source,
+ };
+
+ post_device_event(device,
+ time,
+ LIBINPUT_EVENT_TABLET_PAD_RING,
+ &ring_event->base);
+}
+
+void
+tablet_pad_notify_strip(struct libinput_device *device,
+ uint64_t time,
+ unsigned int number,
+ double value,
+ enum libinput_tablet_pad_strip_axis_source source)
+{
+ struct libinput_event_tablet_pad *strip_event;
+
+ strip_event = zalloc(sizeof *strip_event);
+ if (!strip_event)
+ return;
+
+ *strip_event = (struct libinput_event_tablet_pad) {
+ .time = time,
+ .strip.number = number,
+ .strip.position = value,
+ .strip.source = source,
+ };
+
+ post_device_event(device,
+ time,
+ LIBINPUT_EVENT_TABLET_PAD_STRIP,
+ &strip_event->base);
+}
+
static void
gesture_notify(struct libinput_device *device,
uint64_t time,
@@ -2448,6 +2557,9 @@ event_type_to_str(enum libinput_event_type type)
CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_TIP);
CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+ CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_BUTTON);
+ CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_RING);
+ CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_STRIP);
CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN);
CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE);
CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_END);
@@ -2679,6 +2791,24 @@ libinput_device_keyboard_has_key(struct libinput_device *device, uint32_t code)
return evdev_device_has_key((struct evdev_device *)device, code);
}
+LIBINPUT_EXPORT int
+libinput_device_tablet_pad_get_num_buttons(struct libinput_device *device)
+{
+ return evdev_device_tablet_pad_get_num_buttons((struct evdev_device *)device);
+}
+
+LIBINPUT_EXPORT int
+libinput_device_tablet_pad_get_num_rings(struct libinput_device *device)
+{
+ 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 evdev_device_tablet_pad_get_num_strips((struct evdev_device *)device);
+}
+
LIBINPUT_EXPORT struct libinput_event *
libinput_event_device_notify_get_base_event(struct libinput_event_device_notify *event)
{
@@ -2751,6 +2881,133 @@ libinput_event_tablet_tool_get_base_event(struct libinput_event_tablet_tool *eve
return &event->base;
}
+LIBINPUT_EXPORT double
+libinput_event_tablet_pad_get_ring_position(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0.0,
+ LIBINPUT_EVENT_TABLET_PAD_RING);
+
+ return event->ring.position;
+}
+
+LIBINPUT_EXPORT unsigned int
+libinput_event_tablet_pad_get_ring_number(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0,
+ LIBINPUT_EVENT_TABLET_PAD_RING);
+
+ return event->ring.number;
+}
+
+LIBINPUT_EXPORT enum libinput_tablet_pad_ring_axis_source
+libinput_event_tablet_pad_get_ring_source(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ LIBINPUT_TABLET_PAD_RING_SOURCE_UNKNOWN,
+ LIBINPUT_EVENT_TABLET_PAD_RING);
+
+ return event->ring.source;
+}
+
+LIBINPUT_EXPORT double
+libinput_event_tablet_pad_get_strip_position(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0.0,
+ LIBINPUT_EVENT_TABLET_PAD_STRIP);
+
+ return event->strip.position;
+}
+
+LIBINPUT_EXPORT unsigned int
+libinput_event_tablet_pad_get_strip_number(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0,
+ LIBINPUT_EVENT_TABLET_PAD_STRIP);
+
+ return event->strip.number;
+}
+
+LIBINPUT_EXPORT enum libinput_tablet_pad_strip_axis_source
+libinput_event_tablet_pad_get_strip_source(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ LIBINPUT_TABLET_PAD_STRIP_SOURCE_UNKNOWN,
+ LIBINPUT_EVENT_TABLET_PAD_STRIP);
+
+ return event->strip.source;
+}
+
+LIBINPUT_EXPORT uint32_t
+libinput_event_tablet_pad_get_button_number(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0,
+ LIBINPUT_EVENT_TABLET_PAD_BUTTON);
+
+ return event->button;
+}
+
+LIBINPUT_EXPORT enum libinput_button_state
+libinput_event_tablet_pad_get_button_state(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ LIBINPUT_BUTTON_STATE_RELEASED,
+ LIBINPUT_EVENT_TABLET_PAD_BUTTON);
+
+ return event->state;
+}
+
+LIBINPUT_EXPORT uint32_t
+libinput_event_tablet_pad_get_time(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0,
+ LIBINPUT_EVENT_TABLET_PAD_RING,
+ LIBINPUT_EVENT_TABLET_PAD_STRIP,
+ LIBINPUT_EVENT_TABLET_PAD_BUTTON);
+
+ return us2ms(event->time);
+}
+
+LIBINPUT_EXPORT uint64_t
+libinput_event_tablet_pad_get_time_usec(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0,
+ LIBINPUT_EVENT_TABLET_PAD_RING,
+ LIBINPUT_EVENT_TABLET_PAD_STRIP,
+ LIBINPUT_EVENT_TABLET_PAD_BUTTON);
+
+ return event->time;
+}
+
+LIBINPUT_EXPORT struct libinput_event *
+libinput_event_tablet_pad_get_base_event(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ NULL,
+ LIBINPUT_EVENT_TABLET_PAD_RING,
+ LIBINPUT_EVENT_TABLET_PAD_STRIP,
+ LIBINPUT_EVENT_TABLET_PAD_BUTTON);
+
+ return &event->base;
+}
+
LIBINPUT_EXPORT struct libinput_device_group *
libinput_device_group_ref(struct libinput_device_group *group)
{
diff --git a/src/libinput.h b/src/libinput.h
index 34496115..97fd5709 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -59,6 +59,7 @@ enum libinput_device_capability {
LIBINPUT_DEVICE_CAP_POINTER = 1,
LIBINPUT_DEVICE_CAP_TOUCH = 2,
LIBINPUT_DEVICE_CAP_TABLET_TOOL = 3,
+ LIBINPUT_DEVICE_CAP_TABLET_PAD = 4,
LIBINPUT_DEVICE_CAP_GESTURE = 5,
};
@@ -134,6 +135,36 @@ enum libinput_pointer_axis_source {
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
};
+/**
+ * @ingroup event_tablet
+ *
+ * The source for a @ref LIBINPUT_EVENT_TABLET_PAD_RING event. See
+ * libinput_event_tablet_pad_get_ring_source() for details.
+ */
+enum libinput_tablet_pad_ring_axis_source {
+ LIBINPUT_TABLET_PAD_RING_SOURCE_UNKNOWN = 1,
+ /**
+ * The event is caused by the movement of one or more fingers on
+ * the ring.
+ */
+ LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
+};
+
+/**
+ * @ingroup event_tablet
+ *
+ * The source for a @ref LIBINPUT_EVENT_TABLET_PAD_STRIP event. See
+ * libinput_event_tablet_pad_get_strip_source() for details.
+ */
+enum libinput_tablet_pad_strip_axis_source {
+ LIBINPUT_TABLET_PAD_STRIP_SOURCE_UNKNOWN = 1,
+ /**
+ * The event is caused by the movement of one or more fingers on
+ * the strip.
+ */
+ LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
+};
+
/**
* @ingroup device
* @struct libinput_tablet_tool
@@ -336,9 +367,34 @@ enum libinput_event_type {
* same logical hardware event, the order of the @ref
* LIBINPUT_EVENT_TABLET_TOOL_BUTTON and @ref
* LIBINPUT_EVENT_TABLET_TOOL_AXIS event is device-specific.
+ *
+ * This event is not to be confused with the button events emitted
+ * by the tablet pad. See @ref LIBINPUT_EVENT_TABLET_PAD_BUTTON.
+ *
+ * @see LIBINPUT_EVENT_TABLET_BUTTON
*/
LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
+ /**
+ * A button pressed on a device with the @ref
+ * LIBINPUT_DEVICE_CAP_TABLET_PAD capability.
+ *
+ * This event is not to be confused with the button events emitted
+ * by tools on a tablet. See @ref LIBINPUT_EVENT_TABLET_TOOL_BUTTON.
+ */
+ LIBINPUT_EVENT_TABLET_PAD_BUTTON = 700,
+ /**
+ * A status change on a tablet ring with the
+ * LIBINPUT_DEVICE_CAP_TABLET_PAD capability.
+ */
+ LIBINPUT_EVENT_TABLET_PAD_RING,
+
+ /**
+ * A status change on a strip on a device with the @ref
+ * LIBINPUT_DEVICE_CAP_TABLET_PAD capability.
+ */
+ LIBINPUT_EVENT_TABLET_PAD_STRIP,
+
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN = 800,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
LIBINPUT_EVENT_GESTURE_SWIPE_END,
@@ -445,6 +501,17 @@ struct libinput_event_touch;
*/
struct libinput_event_tablet_tool;
+/**
+ * @ingroup event_tablet
+ * @struct libinput_event_tablet_pad
+ *
+ * Tablet pad event representing a button press, or ring/strip update on
+ * the tablet pad itself. Valid event types for this event are @ref
+ * LIBINPUT_EVENT_TABLET_PAD_BUTTON, @ref LIBINPUT_EVENT_TABLET_PAD_RING and
+ * @ref LIBINPUT_EVENT_TABLET_PAD_STRIP.
+ */
+struct libinput_event_tablet_pad;
+
/**
* @defgroup event Accessing and destruction of events
*/
@@ -566,6 +633,19 @@ libinput_event_get_gesture_event(struct libinput_event *event);
struct libinput_event_tablet_tool *
libinput_event_get_tablet_tool_event(struct libinput_event *event);
+/**
+ * @ingroup event
+ *
+ * Return the tablet pad event that is this input event. If the event type does not
+ * match the tablet pad event types, this function returns NULL.
+ *
+ * The inverse of this function is libinput_event_tablet_pad_get_base_event().
+ *
+ * @return A tablet pad event, or NULL for other events
+ */
+struct libinput_event_tablet_pad *
+libinput_event_get_tablet_pad_event(struct libinput_event *event);
+
/**
* @ingroup event
*
@@ -1357,7 +1437,17 @@ libinput_event_gesture_get_angle_delta(struct libinput_event_gesture *event);
/**
* @defgroup event_tablet Tablet events
*
- * Events that come from tools on tablet devices.
+ * Events that come from tools on or the pad of tablet devices.
+ *
+ * Events from tablet devices are exposed by two interfaces, tools and pads.
+ * Tool events originate (usually) from a stylus-like device, pad events
+ * reflect any events originating from the physical tablet itself.
+ *
+ * Note that many tablets support touch events. These are exposed through
+ * the @ref LIBINPUT_DEVICE_CAP_POINTER interface (for external touchpad-like
+ * devices such as the Wacom Intuos series) or @ref
+ * LIBINPUT_DEVICE_CAP_TOUCH interface (for built-in touchscreen-like
+ * devices such as the Wacom Cintiq series).
*/
/**
@@ -2105,6 +2195,182 @@ void
libinput_tablet_tool_set_user_data(struct libinput_tablet_tool *tool,
void *user_data);
+/**
+ * @ingroup event_tablet
+ *
+ * @return The generic libinput_event of this event
+ */
+struct libinput_event *
+libinput_event_tablet_pad_get_base_event(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet
+ *
+ * Returns the current position of the ring, in degrees counterclockwise
+ * from the northern-most point of the ring in the tablet's current logical
+ * orientation.
+ *
+ * If the source is @ref LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
+ * libinput sends a terminating event with a ring value of -1 when the
+ * finger is lifted from the ring. A caller may use this information to e.g.
+ * determine if kinetic scrolling should be triggered.
+ *
+ * @note It is an application bug to call this function for events other than
+ * @ref LIBINPUT_EVENT_TABLET_PAD_RING. For other events, this function
+ * returns 0.
+ *
+ * @param event The libinput tablet pad event
+ * @return The current value of the the axis
+ * @retval -1 The finger was lifted
+ */
+double
+libinput_event_tablet_pad_get_ring_position(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet
+ *
+ * Returns the number of the ring that has changed state, with 0 being the
+ * first ring. On tablets with only one ring, this function always returns
+ * 0.
+ *
+ * @note It is an application bug to call this function for events other than
+ * @ref LIBINPUT_EVENT_TABLET_PAD_RING. For other events, this function
+ * returns 0.
+ *
+ * @param event The libinput tablet pad event
+ * @return The index of the ring that changed state
+ */
+unsigned int
+libinput_event_tablet_pad_get_ring_number(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet
+ *
+ * Returns the source of the interaction with the ring. If the source is
+ * @ref LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER, libinput sends a ring
+ * position value of -1 to terminate the current interaction.
+ *
+ * @note It is an application bug to call this function for events other than
+ * @ref LIBINPUT_EVENT_TABLET_PAD_RING. For other events, this function
+ * returns 0.
+ *
+ * @param event The libinput tablet pad event
+ * @return The source of the ring interaction
+ */
+enum libinput_tablet_pad_ring_axis_source
+libinput_event_tablet_pad_get_ring_source(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet
+ *
+ * Returns the current position of the strip, normalized to the range
+ * [0, 1], with 0 being the top/left-most point in the tablet's current
+ * logical orientation.
+ *
+ * If the source is @ref LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
+ * libinput sends a terminating event with a ring value of -1 when the
+ * finger is lifted from the ring. A caller may use this information to e.g.
+ * determine if kinetic scrolling should be triggered.
+ *
+ * @note It is an application bug to call this function for events other than
+ * @ref LIBINPUT_EVENT_TABLET_PAD_STRIP. For other events, this function
+ * returns 0.
+ *
+ * @param event The libinput tablet pad event
+ * @return The current value of the the axis
+ * @retval -1 The finger was lifted
+ */
+double
+libinput_event_tablet_pad_get_strip_position(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet
+ *
+ * Returns the number of the strip that has changed state, with 0 being the
+ * first strip. On tablets with only one strip, this function always returns
+ * 0.
+ *
+ * @note It is an application bug to call this function for events other than
+ * @ref LIBINPUT_EVENT_TABLET_PAD_STRIP. For other events, this function
+ * returns 0.
+ *
+ * @param event The libinput tablet pad event
+ * @return The index of the strip that changed state
+ */
+unsigned int
+libinput_event_tablet_pad_get_strip_number(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet
+ *
+ * Returns the source of the interaction with the strip. If the source is
+ * @ref LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER, libinput sends a strip
+ * position value of -1 to terminate the current interaction.
+ *
+ * @note It is an application bug to call this function for events other than
+ * @ref LIBINPUT_EVENT_TABLET_PAD_STRIP. For other events, this function
+ * returns 0.
+ *
+ * @param event The libinput tablet pad event
+ * @return The source of the strip interaction
+ */
+enum libinput_tablet_pad_strip_axis_source
+libinput_event_tablet_pad_get_strip_source(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet
+ *
+ * Return the button number that triggered this event, starting at 0.
+ * For events that are not of type @ref LIBINPUT_EVENT_TABLET_PAD_BUTTON,
+ * this function returns 0.
+ *
+ * Note that the number returned is a generic sequential button number and
+ * not a semantic button code as defined in linux/input.h.
+ * See @ref tablet-pad-buttons for more details.
+ *
+ * @note It is an application bug to call this function for events other than
+ * @ref LIBINPUT_EVENT_TABLET_PAD_BUTTON. For other events, this function
+ * returns 0.
+ *
+ * @param event The libinput tablet pad event
+ * @return the button triggering this event
+ */
+uint32_t
+libinput_event_tablet_pad_get_button_number(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet
+ *
+ * Return the button state of the event.
+ *
+ * @note It is an application bug to call this function for events other than
+ * @ref LIBINPUT_EVENT_TABLET_PAD_BUTTON. For other events, this function
+ * returns 0.
+ *
+ * @param event The libinput tablet pad event
+ * @return the button state triggering this event
+ */
+enum libinput_button_state
+libinput_event_tablet_pad_get_button_state(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet
+ *
+ * @param event The libinput tablet pad event
+ * @return The event time for this event
+ */
+uint32_t
+libinput_event_tablet_pad_get_time(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet
+ *
+ * @param event The libinput tablet pad event
+ * @return The event time for this event in microseconds
+ */
+uint64_t
+libinput_event_tablet_pad_get_time_usec(struct libinput_event_tablet_pad *event);
+
/**
* @defgroup base Initialization and manipulation of libinput contexts
*/
@@ -2931,6 +3197,51 @@ int
libinput_device_keyboard_has_key(struct libinput_device *device,
uint32_t code);
+/**
+ * @ingroup device
+ *
+ * Return the number of buttons on a device with the
+ * @ref LIBINPUT_DEVICE_CAP_TABLET_PAD capability.
+ * Buttons on a pad device are numbered sequentially, see @ref
+ * tablet-pad-buttons for details.
+ *
+ * @param device A current input device
+ *
+ * @return The number of buttons supported by the device.
+ */
+int
+libinput_device_tablet_pad_get_num_buttons(struct libinput_device *device);
+
+/**
+ * @ingroup device
+ *
+ * Return the number of rings a device with the @ref
+ * LIBINPUT_DEVICE_CAP_TABLET_PAD capability provides.
+ *
+ * @param device A current input device
+ *
+ * @return The number of rings or 0 if the device has no rings.
+ *
+ * @see libinput_event_tablet_pad_get_ring_number
+ */
+int
+libinput_device_tablet_pad_get_num_rings(struct libinput_device *device);
+
+/**
+ * @ingroup device
+ *
+ * Return the number of strips a device with the @ref
+ * LIBINPUT_DEVICE_CAP_TABLET_PAD capability provides.
+ *
+ * @param device A current input device
+ *
+ * @return The number of strips or 0 if the device has no strips.
+ *
+ * @see libinput_event_tablet_pad_get_strip_number
+ */
+int
+libinput_device_tablet_pad_get_num_strips(struct libinput_device *device);
+
/**
* @ingroup device
*
diff --git a/src/libinput.sym b/src/libinput.sym
index a2113883..ca1baba7 100644
--- a/src/libinput.sym
+++ b/src/libinput.sym
@@ -234,3 +234,21 @@ LIBINPUT_1.2 {
libinput_tablet_tool_set_user_data;
libinput_tablet_tool_unref;
} LIBINPUT_1.1;
+
+LIBINPUT_1.3 {
+ libinput_device_tablet_pad_get_num_buttons;
+ libinput_device_tablet_pad_get_num_rings;
+ libinput_device_tablet_pad_get_num_strips;
+ libinput_event_get_tablet_pad_event;
+ libinput_event_tablet_pad_get_base_event;
+ libinput_event_tablet_pad_get_button_number;
+ libinput_event_tablet_pad_get_button_state;
+ libinput_event_tablet_pad_get_ring_position;
+ libinput_event_tablet_pad_get_ring_number;
+ libinput_event_tablet_pad_get_ring_source;
+ libinput_event_tablet_pad_get_strip_position;
+ libinput_event_tablet_pad_get_strip_number;
+ libinput_event_tablet_pad_get_strip_source;
+ libinput_event_tablet_pad_get_time;
+ libinput_event_tablet_pad_get_time_usec;
+} LIBINPUT_1.2;
diff --git a/test/Makefile.am b/test/Makefile.am
index 6ab36aac..9b67818a 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -49,6 +49,8 @@ liblitest_la_SOURCES = \
litest-device-wacom-cintiq-tablet.c \
litest-device-wacom-cintiq-24hd.c \
litest-device-wacom-intuos-tablet.c \
+ litest-device-wacom-intuos3-pad.c \
+ litest-device-wacom-intuos5-pad.c \
litest-device-wacom-isdv4-tablet.c \
litest-device-wacom-touch.c \
litest-device-wacom-intuos-finger.c \
@@ -72,6 +74,7 @@ run_tests = \
test-touchpad \
test-touchpad-tap \
test-touchpad-buttons \
+ test-pad \
test-tablet \
test-device \
test-gestures \
@@ -121,6 +124,10 @@ test_tablet_SOURCES = tablet.c
test_tablet_LDADD = $(TEST_LIBS)
test_tablet_LDFLAGS = -static
+test_pad_SOURCES = pad.c
+test_pad_LDADD = $(TEST_LIBS)
+test_pad_LDFLAGS = -static
+
test_touchpad_SOURCES = touchpad.c
test_touchpad_LDADD = $(TEST_LIBS)
test_touchpad_LDFLAGS = -no-install
diff --git a/test/litest-device-wacom-intuos3-pad.c b/test/litest-device-wacom-intuos3-pad.c
new file mode 100644
index 00000000..0622ddcc
--- /dev/null
+++ b/test/litest-device-wacom-intuos3-pad.c
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "litest.h"
+#include "litest-int.h"
+
+static void
+litest_wacom_intuos3_pad_setup(void)
+{
+ struct litest_device *d = litest_create_device(LITEST_WACOM_INTUOS3_PAD);
+ litest_set_current_device(d);
+}
+
+static struct input_event down[] = {
+ { .type = -1, .code = -1 },
+};
+
+static struct input_event move[] = {
+ { .type = -1, .code = -1 },
+};
+
+static struct input_event strip_start[] = {
+ { .type = EV_ABS, .code = ABS_RX, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MISC, .value = 15 },
+ { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+ { .type = -1, .code = -1 },
+} ;
+
+static struct input_event strip_change[] = {
+ { .type = EV_ABS, .code = ABS_RX, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+ { .type = -1, .code = -1 },
+} ;
+
+static struct input_event strip_end[] = {
+ { .type = EV_ABS, .code = ABS_RX, .value = 0 },
+ { .type = EV_ABS, .code = ABS_MISC, .value = 0 },
+ { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+ { .type = -1, .code = -1 },
+} ;
+
+static struct litest_device_interface interface = {
+ .touch_down_events = down,
+ .touch_move_events = move,
+ .pad_strip_start_events = strip_start,
+ .pad_strip_change_events = strip_change,
+ .pad_strip_end_events = strip_end,
+};
+
+static struct input_absinfo absinfo[] = {
+ { ABS_X, 0, 1, 0, 0, 0 },
+ { ABS_Y, 0, 1, 0, 0, 0 },
+ { ABS_RX, 0, 4096, 0, 0, 0 },
+ { ABS_MISC, 0, 0, 0, 0, 0 },
+ { .value = -1 },
+};
+
+static struct input_id input_id = {
+ .bustype = 0x3,
+ .vendor = 0x56a,
+ .product = 0xb7,
+};
+
+static int events[] = {
+ EV_KEY, BTN_0,
+ EV_KEY, BTN_1,
+ EV_KEY, BTN_2,
+ EV_KEY, BTN_3,
+ EV_KEY, BTN_STYLUS,
+ -1, -1,
+};
+
+static const char udev_rule[] =
+"ACTION==\"remove\", GOTO=\"pad_end\"\n"
+"KERNEL!=\"event*\", GOTO=\"pad_end\"\n"
+"\n"
+"ATTRS{name}==\"litest Wacom Intuos3 4x6 Pad*\",\\\n"
+" ENV{ID_INPUT_TABLET_PAD}=\"1\"\n"
+"\n"
+"LABEL=\"pad_end\"";
+
+struct litest_test_device litest_wacom_intuos3_pad_device = {
+ .type = LITEST_WACOM_INTUOS3_PAD,
+ .features = LITEST_TABLET_PAD | LITEST_STRIP,
+ .shortname = "wacom-intuos3-pad",
+ .setup = litest_wacom_intuos3_pad_setup,
+ .interface = &interface,
+
+ .name = "Wacom Intuos3 4x6 Pad",
+ .id = &input_id,
+ .events = events,
+ .absinfo = absinfo,
+ .udev_rule = udev_rule,
+};
diff --git a/test/litest-device-wacom-intuos5-pad.c b/test/litest-device-wacom-intuos5-pad.c
new file mode 100644
index 00000000..7cff4120
--- /dev/null
+++ b/test/litest-device-wacom-intuos5-pad.c
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "litest.h"
+#include "litest-int.h"
+
+static void
+litest_wacom_intuos5_pad_setup(void)
+{
+ struct litest_device *d = litest_create_device(LITEST_WACOM_INTUOS5_PAD);
+ litest_set_current_device(d);
+}
+
+static struct input_event down[] = {
+ { .type = -1, .code = -1 },
+};
+
+static struct input_event move[] = {
+ { .type = -1, .code = -1 },
+};
+
+static struct input_event ring_start[] = {
+ { .type = EV_ABS, .code = ABS_WHEEL, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_ABS, .code = ABS_MISC, .value = 15 },
+ { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+ { .type = -1, .code = -1 },
+} ;
+
+static struct input_event ring_change[] = {
+ { .type = EV_ABS, .code = ABS_WHEEL, .value = LITEST_AUTO_ASSIGN },
+ { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+ { .type = -1, .code = -1 },
+} ;
+
+static struct input_event ring_end[] = {
+ { .type = EV_ABS, .code = ABS_WHEEL, .value = 0 },
+ { .type = EV_ABS, .code = ABS_MISC, .value = 0 },
+ { .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+ { .type = -1, .code = -1 },
+} ;
+
+static struct litest_device_interface interface = {
+ .touch_down_events = down,
+ .touch_move_events = move,
+ .pad_ring_start_events = ring_start,
+ .pad_ring_change_events = ring_change,
+ .pad_ring_end_events = ring_end,
+};
+
+static struct input_absinfo absinfo[] = {
+ { ABS_X, 0, 1, 0, 0, 0 },
+ { ABS_Y, 0, 1, 0, 0, 0 },
+ { ABS_WHEEL, 0, 71, 0, 0, 0 },
+ { ABS_MISC, 0, 0, 0, 0, 10 },
+ { .value = -1 },
+};
+
+static struct input_id input_id = {
+ .bustype = 0x3,
+ .vendor = 0x56a,
+ .product = 0x27,
+};
+
+static int events[] = {
+ EV_KEY, BTN_0,
+ EV_KEY, BTN_1,
+ EV_KEY, BTN_2,
+ EV_KEY, BTN_3,
+ EV_KEY, BTN_4,
+ EV_KEY, BTN_5,
+ EV_KEY, BTN_6,
+ EV_KEY, BTN_7,
+ EV_KEY, BTN_8,
+ EV_KEY, BTN_STYLUS,
+ -1, -1,
+};
+
+static const char udev_rule[] =
+"ACTION==\"remove\", GOTO=\"pad_end\"\n"
+"KERNEL!=\"event*\", GOTO=\"pad_end\"\n"
+"\n"
+"ATTRS{name}==\"litest Wacom Intuos5 touch M Pad*\",\\\n"
+" ENV{ID_INPUT_TABLET_PAD}=\"1\"\n"
+"\n"
+"LABEL=\"pad_end\"";
+
+struct litest_test_device litest_wacom_intuos5_pad_device = {
+ .type = LITEST_WACOM_INTUOS5_PAD,
+ .features = LITEST_TABLET_PAD | LITEST_RING,
+ .shortname = "wacom-pad",
+ .setup = litest_wacom_intuos5_pad_setup,
+ .interface = &interface,
+
+ .name = "Wacom Intuos5 touch M Pad",
+ .id = &input_id,
+ .events = events,
+ .absinfo = absinfo,
+ .udev_rule = udev_rule,
+};
diff --git a/test/litest-int.h b/test/litest-int.h
index 8d0308b3..1a9060d9 100644
--- a/test/litest-int.h
+++ b/test/litest-int.h
@@ -105,6 +105,22 @@ struct litest_device_interface {
struct input_event *tablet_proximity_out_events;
struct input_event *tablet_motion_events;
+ /**
+ * Pad events, LITEST_AUTO_ASSIGN is allowed on event values
+ * for ABS_WHEEL
+ */
+ struct input_event *pad_ring_start_events;
+ struct input_event *pad_ring_change_events;
+ struct input_event *pad_ring_end_events;
+
+ /**
+ * Pad events, LITEST_AUTO_ASSIGN is allowed on event values
+ * for ABS_RX
+ */
+ struct input_event *pad_strip_start_events;
+ struct input_event *pad_strip_change_events;
+ struct input_event *pad_strip_end_events;
+
int min[2]; /* x/y axis minimum */
int max[2]; /* x/y axis maximum */
};
diff --git a/test/litest.c b/test/litest.c
index 937e8677..48569d6d 100644
--- a/test/litest.c
+++ b/test/litest.c
@@ -380,6 +380,8 @@ extern struct litest_test_device litest_yubikey_device;
extern struct litest_test_device litest_synaptics_i2c_device;
extern struct litest_test_device litest_wacom_cintiq_24hd_device;
extern struct litest_test_device litest_multitouch_fuzz_screen_device;
+extern struct litest_test_device litest_wacom_intuos3_pad_device;
+extern struct litest_test_device litest_wacom_intuos5_pad_device;
struct litest_test_device* devices[] = {
&litest_synaptics_clickpad_device,
@@ -426,6 +428,8 @@ struct litest_test_device* devices[] = {
&litest_synaptics_i2c_device,
&litest_wacom_cintiq_24hd_device,
&litest_multitouch_fuzz_screen_device,
+ &litest_wacom_intuos3_pad_device,
+ &litest_wacom_intuos5_pad_device,
NULL,
};
@@ -1811,6 +1815,15 @@ litest_scale_axis(const struct litest_device *d,
return (abs->maximum - abs->minimum) * val/100.0 + abs->minimum;
}
+static inline int
+litest_scale_range(int min, int max, double val)
+{
+ litest_assert_int_ge((int)val, 0);
+ litest_assert_int_le((int)val, 100);
+
+ return (max - min) * val/100.0 + min;
+}
+
int
litest_scale(const struct litest_device *d, unsigned int axis, double val)
{
@@ -1821,12 +1834,120 @@ litest_scale(const struct litest_device *d, unsigned int axis, double val)
if (axis <= ABS_Y) {
min = d->interface->min[axis];
max = d->interface->max[axis];
- return (max - min) * val/100.0 + min;
+
+ return litest_scale_range(min, max, val);
} else {
return litest_scale_axis(d, axis, val);
}
}
+static inline int
+auto_assign_pad_value(struct litest_device *dev,
+ struct input_event *ev,
+ double value)
+{
+ const struct input_absinfo *abs;
+
+ if (ev->value != LITEST_AUTO_ASSIGN ||
+ ev->type != EV_ABS)
+ return value;
+
+ abs = libevdev_get_abs_info(dev->evdev, ev->code);
+ litest_assert_notnull(abs);
+
+ if (ev->code == ABS_RX || ev->code == ABS_RY) {
+ double min = abs->minimum != 0 ? log2(abs->minimum) : 0,
+ max = abs->maximum != 0 ? log2(abs->maximum) : 0;
+
+ /* Value 0 is reserved for finger up, so a value of 0% is
+ * actually 1 */
+ if (value == 0.0) {
+ return 1;
+ } else {
+ value = litest_scale_range(min, max, value);
+ return pow(2, value);
+ }
+ } else {
+ return litest_scale_range(abs->minimum, abs->maximum, value);
+ }
+}
+
+void
+litest_pad_ring_start(struct litest_device *d, double value)
+{
+ struct input_event *ev;
+
+ ev = d->interface->pad_ring_start_events;
+ while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
+ value = auto_assign_pad_value(d, ev, value);
+ litest_event(d, ev->type, ev->code, value);
+ ev++;
+ }
+}
+
+void
+litest_pad_ring_change(struct litest_device *d, double value)
+{
+ struct input_event *ev;
+
+ ev = d->interface->pad_ring_change_events;
+ while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
+ value = auto_assign_pad_value(d, ev, value);
+ litest_event(d, ev->type, ev->code, value);
+ ev++;
+ }
+}
+
+void
+litest_pad_ring_end(struct litest_device *d)
+{
+ struct input_event *ev;
+
+ ev = d->interface->pad_ring_end_events;
+ while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
+ litest_event(d, ev->type, ev->code, ev->value);
+ ev++;
+ }
+}
+
+void
+litest_pad_strip_start(struct litest_device *d, double value)
+{
+ struct input_event *ev;
+
+ ev = d->interface->pad_strip_start_events;
+ while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
+ value = auto_assign_pad_value(d, ev, value);
+ litest_event(d, ev->type, ev->code, value);
+ ev++;
+ }
+}
+
+void
+litest_pad_strip_change(struct litest_device *d, double value)
+{
+ struct input_event *ev;
+
+ ev = d->interface->pad_strip_change_events;
+ while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
+ value = auto_assign_pad_value(d, ev, value);
+ litest_event(d, ev->type, ev->code, value);
+ ev++;
+ }
+}
+
+void
+litest_pad_strip_end(struct litest_device *d)
+{
+ struct input_event *ev;
+
+ ev = d->interface->pad_strip_end_events;
+ while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
+ litest_event(d, ev->type, ev->code, ev->value);
+ ev++;
+ }
+}
+
void
litest_wait_for_event(struct libinput *li)
{
@@ -1965,6 +2086,15 @@ litest_event_type_str(struct libinput_event *event)
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
str = "TABLET TOOL BUTTON";
break;
+ case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
+ str = "TABLET PAD BUTTON";
+ break;
+ case LIBINPUT_EVENT_TABLET_PAD_RING:
+ str = "TABLET PAD RING";
+ break;
+ case LIBINPUT_EVENT_TABLET_PAD_STRIP:
+ str = "TABLET PAD STRIP";
+ break;
}
return str;
}
@@ -1974,6 +2104,7 @@ litest_print_event(struct libinput_event *event)
{
struct libinput_event_pointer *p;
struct libinput_event_tablet_tool *t;
+ struct libinput_event_tablet_pad *pad;
struct libinput_device *dev;
enum libinput_event_type type;
double x, y;
@@ -2035,6 +2166,26 @@ litest_print_event(struct libinput_event *event)
libinput_event_tablet_tool_get_button(t),
libinput_event_tablet_tool_get_button_state(t));
break;
+ case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
+ pad = libinput_event_get_tablet_pad_event(event);
+ fprintf(stderr, "button %d state %d",
+ libinput_event_tablet_pad_get_button_number(pad),
+ libinput_event_tablet_pad_get_button_state(pad));
+ break;
+ case LIBINPUT_EVENT_TABLET_PAD_RING:
+ pad = libinput_event_get_tablet_pad_event(event);
+ fprintf(stderr, "ring %d position %.2f source %d",
+ libinput_event_tablet_pad_get_ring_number(pad),
+ libinput_event_tablet_pad_get_ring_position(pad),
+ libinput_event_tablet_pad_get_ring_source(pad));
+ break;
+ case LIBINPUT_EVENT_TABLET_PAD_STRIP:
+ pad = libinput_event_get_tablet_pad_event(event);
+ fprintf(stderr, "strip %d position %.2f source %d",
+ libinput_event_tablet_pad_get_ring_number(pad),
+ libinput_event_tablet_pad_get_ring_position(pad),
+ libinput_event_tablet_pad_get_ring_source(pad));
+ break;
default:
break;
}
@@ -2456,6 +2607,81 @@ void litest_assert_tablet_proximity_event(struct libinput *li,
libinput_event_destroy(event);
}
+struct libinput_event_tablet_pad *
+litest_is_pad_button_event(struct libinput_event *event,
+ unsigned int button,
+ enum libinput_button_state state)
+{
+ struct libinput_event_tablet_pad *p;
+ enum libinput_event_type type = LIBINPUT_EVENT_TABLET_PAD_BUTTON;
+
+ litest_assert(event != NULL);
+ litest_assert_int_eq(libinput_event_get_type(event), type);
+
+ p = libinput_event_get_tablet_pad_event(event);
+ litest_assert(p != NULL);
+
+ litest_assert_int_eq(libinput_event_tablet_pad_get_button_number(p),
+ button);
+
+ return p;
+}
+
+struct libinput_event_tablet_pad *
+litest_is_pad_ring_event(struct libinput_event *event,
+ unsigned int number,
+ enum libinput_tablet_pad_ring_axis_source source)
+{
+ struct libinput_event_tablet_pad *p;
+ enum libinput_event_type type = LIBINPUT_EVENT_TABLET_PAD_RING;
+
+ litest_assert(event != NULL);
+ litest_assert_int_eq(libinput_event_get_type(event), type);
+ p = libinput_event_get_tablet_pad_event(event);
+
+ litest_assert_int_eq(libinput_event_tablet_pad_get_ring_number(p),
+ number);
+ litest_assert_int_eq(libinput_event_tablet_pad_get_ring_source(p),
+ source);
+
+ return p;
+}
+
+struct libinput_event_tablet_pad *
+litest_is_pad_strip_event(struct libinput_event *event,
+ unsigned int number,
+ enum libinput_tablet_pad_strip_axis_source source)
+{
+ struct libinput_event_tablet_pad *p;
+ enum libinput_event_type type = LIBINPUT_EVENT_TABLET_PAD_STRIP;
+
+ litest_assert(event != NULL);
+ litest_assert_int_eq(libinput_event_get_type(event), type);
+ p = libinput_event_get_tablet_pad_event(event);
+
+ litest_assert_int_eq(libinput_event_tablet_pad_get_strip_number(p),
+ number);
+ litest_assert_int_eq(libinput_event_tablet_pad_get_strip_source(p),
+ source);
+
+ return p;
+}
+
+void
+litest_assert_pad_button_event(struct libinput *li,
+ unsigned int button,
+ enum libinput_button_state state)
+{
+ struct libinput_event *event;
+ struct libinput_event_tablet_pad *pev;
+
+ litest_wait_for_event(li);
+ event = libinput_get_event(li);
+
+ pev = litest_is_pad_button_event(event, button, state);
+ libinput_event_destroy(libinput_event_tablet_pad_get_base_event(pev));
+}
+
void
litest_assert_scroll(struct libinput *li,
enum libinput_pointer_axis axis,
diff --git a/test/litest.h b/test/litest.h
index cc06ca51..f77fc733 100644
--- a/test/litest.h
+++ b/test/litest.h
@@ -197,6 +197,8 @@ enum litest_device_type {
LITEST_SYNAPTICS_I2C = -43,
LITEST_WACOM_CINTIQ_24HD = -44,
LITEST_MULTITOUCH_FUZZ_SCREEN = -45,
+ LITEST_WACOM_INTUOS3_PAD = -46,
+ LITEST_WACOM_INTUOS5_PAD = -47,
};
enum litest_device_feature {
@@ -223,6 +225,9 @@ enum litest_device_feature {
LITEST_DISTANCE = 1 << 18,
LITEST_TOOL_SERIAL = 1 << 19,
LITEST_TILT = 1 << 20,
+ LITEST_TABLET_PAD = 1 << 21,
+ LITEST_RING = 1 << 22,
+ LITEST_STRIP = 1 << 23,
};
struct litest_device {
@@ -440,6 +445,24 @@ litest_tablet_motion(struct litest_device *d,
int x, int y,
struct axis_replacement *axes);
+void
+litest_pad_ring_start(struct litest_device *d, double value);
+
+void
+litest_pad_ring_change(struct litest_device *d, double value);
+
+void
+litest_pad_ring_end(struct litest_device *d);
+
+void
+litest_pad_strip_start(struct litest_device *d, double value);
+
+void
+litest_pad_strip_change(struct litest_device *d, double value);
+
+void
+litest_pad_strip_end(struct litest_device *d);
+
void
litest_hover_start(struct litest_device *d,
unsigned int slot,
@@ -526,6 +549,19 @@ struct libinput_event_tablet_tool *
litest_is_tablet_event(struct libinput_event *event,
enum libinput_event_type type);
+struct libinput_event_tablet_pad *
+litest_is_pad_button_event(struct libinput_event *event,
+ unsigned int button,
+ enum libinput_button_state state);
+struct libinput_event_tablet_pad *
+litest_is_pad_ring_event(struct libinput_event *event,
+ unsigned int number,
+ enum libinput_tablet_pad_ring_axis_source source);
+struct libinput_event_tablet_pad *
+litest_is_pad_strip_event(struct libinput_event *event,
+ unsigned int number,
+ enum libinput_tablet_pad_strip_axis_source source);
+
void
litest_assert_button_event(struct libinput *li,
unsigned int button,
@@ -549,6 +585,10 @@ void
litest_assert_tablet_proximity_event(struct libinput *li,
enum libinput_tablet_tool_proximity_state state);
+void
+litest_assert_pad_button_event(struct libinput *li,
+ unsigned int button,
+ enum libinput_button_state state);
struct libevdev_uinput *
litest_create_uinput_device(const char *name,
struct input_id *id,
diff --git a/test/misc.c b/test/misc.c
index e8b41e59..304710ff 100644
--- a/test/misc.c
+++ b/test/misc.c
@@ -134,6 +134,7 @@ START_TEST(event_conversion_device_notify)
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
+ ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
@@ -190,6 +191,7 @@ START_TEST(event_conversion_pointer)
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
+ ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
@@ -240,6 +242,7 @@ START_TEST(event_conversion_pointer_abs)
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
+ ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
@@ -283,6 +286,7 @@ START_TEST(event_conversion_key)
ck_assert(libinput_event_get_touch_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
+ ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
@@ -333,6 +337,7 @@ START_TEST(event_conversion_touch)
ck_assert(libinput_event_get_keyboard_event(event) == NULL);
ck_assert(libinput_event_get_gesture_event(event) == NULL);
ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
+ ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
@@ -381,6 +386,7 @@ START_TEST(event_conversion_gesture)
ck_assert(libinput_event_get_pointer_event(event) == NULL);
ck_assert(libinput_event_get_keyboard_event(event) == NULL);
ck_assert(libinput_event_get_touch_event(event) == NULL);
+ ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
@@ -427,6 +433,50 @@ START_TEST(event_conversion_tablet)
ck_assert(libinput_event_get_pointer_event(event) == NULL);
ck_assert(libinput_event_get_keyboard_event(event) == NULL);
ck_assert(libinput_event_get_touch_event(event) == NULL);
+ ck_assert(libinput_event_get_tablet_pad_event(event) == NULL);
+ litest_restore_log_handler(li);
+ }
+ libinput_event_destroy(event);
+ }
+
+ ck_assert_int_gt(events, 0);
+}
+END_TEST
+
+START_TEST(event_conversion_tablet_pad)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *event;
+ int events = 0;
+
+ litest_button_click(dev, BTN_0, true);
+ litest_pad_ring_start(dev, 10);
+ litest_pad_ring_end(dev);
+
+ libinput_dispatch(li);
+
+ while ((event = libinput_get_event(li))) {
+ enum libinput_event_type type;
+ type = libinput_event_get_type(event);
+
+ if (type >= LIBINPUT_EVENT_TABLET_PAD_BUTTON &&
+ type <= LIBINPUT_EVENT_TABLET_PAD_STRIP) {
+ struct libinput_event_tablet_pad *p;
+ struct libinput_event *base;
+
+ p = libinput_event_get_tablet_pad_event(event);
+ base = libinput_event_tablet_pad_get_base_event(p);
+ ck_assert(event == base);
+
+ events++;
+
+ litest_disable_log_handler(li);
+ ck_assert(libinput_event_get_device_notify_event(event) == NULL);
+ ck_assert(libinput_event_get_pointer_event(event) == NULL);
+ ck_assert(libinput_event_get_keyboard_event(event) == NULL);
+ ck_assert(libinput_event_get_touch_event(event) == NULL);
+ ck_assert(libinput_event_get_tablet_tool_event(event) == NULL);
litest_restore_log_handler(li);
}
libinput_event_destroy(event);
@@ -895,6 +945,7 @@ litest_setup_tests(void)
litest_add_for_device("events:conversion", event_conversion_touch, LITEST_WACOM_TOUCH);
litest_add_for_device("events:conversion", event_conversion_gesture, LITEST_BCM5974);
litest_add_for_device("events:conversion", event_conversion_tablet, LITEST_WACOM_CINTIQ);
+ litest_add_for_device("events:conversion", event_conversion_tablet_pad, LITEST_WACOM_INTUOS5_PAD);
litest_add_no_device("bitfield_helpers", bitfield_helpers);
litest_add_no_device("context:refcount", context_ref_counting);
diff --git a/test/pad.c b/test/pad.c
new file mode 100644
index 00000000..63bd2d7a
--- /dev/null
+++ b/test/pad.c
@@ -0,0 +1,431 @@
+/*
+ * 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
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "libinput-util.h"
+#include "litest.h"
+
+START_TEST(pad_cap)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+
+ ck_assert(libinput_device_has_capability(device,
+ LIBINPUT_DEVICE_CAP_TABLET_PAD));
+
+}
+END_TEST
+
+START_TEST(pad_no_cap)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+
+ ck_assert(!libinput_device_has_capability(device,
+ LIBINPUT_DEVICE_CAP_TABLET_PAD));
+}
+END_TEST
+
+START_TEST(pad_num_buttons)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ unsigned int code;
+ unsigned int nbuttons = 0;
+
+ for (code = BTN_0; code < KEY_MAX; code++) {
+ /* BTN_STYLUS is set for compatibility reasons but not
+ * actually hooked up */
+ if (code == BTN_STYLUS)
+ continue;
+
+ if (libevdev_has_event_code(dev->evdev, EV_KEY, code))
+ nbuttons++;
+ }
+
+ ck_assert_int_eq(libinput_device_tablet_pad_get_num_buttons(device),
+ nbuttons);
+}
+END_TEST
+
+START_TEST(pad_button)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ unsigned int code;
+ unsigned int expected_number = 0;
+ struct libinput_event *ev;
+ struct libinput_event_tablet_pad *pev;
+
+ litest_drain_events(li);
+
+ for (code = BTN_LEFT; code < KEY_MAX; code++) {
+ if (!libevdev_has_event_code(dev->evdev, EV_KEY, code))
+ continue;
+
+ litest_button_click(dev, code, 1);
+ litest_button_click(dev, code, 0);
+ libinput_dispatch(li);
+
+ switch (code) {
+ case BTN_STYLUS:
+ litest_assert_empty_queue(li);
+ continue;
+ default:
+ break;
+ }
+
+ ev = libinput_get_event(li);
+ pev = litest_is_pad_button_event(ev,
+ expected_number,
+ LIBINPUT_BUTTON_STATE_PRESSED);
+ ev = libinput_event_tablet_pad_get_base_event(pev);
+ libinput_event_destroy(ev);
+
+ ev = libinput_get_event(li);
+ pev = litest_is_pad_button_event(ev,
+ expected_number,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ ev = libinput_event_tablet_pad_get_base_event(pev);
+ libinput_event_destroy(ev);
+
+ expected_number++;
+ }
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(pad_has_ring)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ int nrings;
+
+ nrings = libinput_device_tablet_pad_get_num_rings(device);
+ ck_assert_int_ge(nrings, 1);
+}
+END_TEST
+
+START_TEST(pad_ring)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *ev;
+ struct libinput_event_tablet_pad *pev;
+ int val;
+ double degrees, expected;
+
+ litest_pad_ring_start(dev, 10);
+
+ litest_drain_events(li);
+
+ /* Wacom's 0 value is at 275 degrees */
+ expected = 270;
+
+ for (val = 0; val < 100; val += 10) {
+ litest_pad_ring_change(dev, val);
+ libinput_dispatch(li);
+
+ ev = libinput_get_event(li);
+ pev = litest_is_pad_ring_event(ev,
+ 0,
+ LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
+
+ degrees = libinput_event_tablet_pad_get_ring_position(pev);
+ ck_assert_double_ge(degrees, 0.0);
+ ck_assert_double_lt(degrees, 360.0);
+
+ /* rounding errors, mostly caused by small physical range */
+ ck_assert_double_ge(degrees, expected - 2);
+ ck_assert_double_le(degrees, expected + 2);
+
+ libinput_event_destroy(ev);
+
+ expected = fmod(degrees + 36, 360);
+ }
+
+ litest_pad_ring_end(dev);
+}
+END_TEST
+
+START_TEST(pad_ring_finger_up)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *ev;
+ struct libinput_event_tablet_pad *pev;
+ double degrees;
+
+ litest_pad_ring_start(dev, 10);
+
+ litest_drain_events(li);
+
+ litest_pad_ring_end(dev);
+ libinput_dispatch(li);
+
+ ev = libinput_get_event(li);
+ pev = litest_is_pad_ring_event(ev,
+ 0,
+ LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
+
+ degrees = libinput_event_tablet_pad_get_ring_position(pev);
+ ck_assert_double_eq(degrees, -1.0);
+ libinput_event_destroy(ev);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(pad_has_strip)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ int nstrips;
+
+ nstrips = libinput_device_tablet_pad_get_num_strips(device);
+ ck_assert_int_ge(nstrips, 1);
+}
+END_TEST
+
+START_TEST(pad_strip)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *ev;
+ struct libinput_event_tablet_pad *pev;
+ int val;
+ double pos, expected;
+
+ litest_pad_strip_start(dev, 10);
+
+ litest_drain_events(li);
+
+ expected = 0;
+
+ /* 9.5 works with the generic axis scaling without jumping over a
+ * value. */
+ for (val = 0; val < 100; val += 9.5) {
+ litest_pad_strip_change(dev, val);
+ libinput_dispatch(li);
+
+ ev = libinput_get_event(li);
+ pev = litest_is_pad_strip_event(ev,
+ 0,
+ LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
+
+ pos = libinput_event_tablet_pad_get_strip_position(pev);
+ ck_assert_double_ge(pos, 0.0);
+ ck_assert_double_lt(pos, 1.0);
+
+ /* rounding errors, mostly caused by small physical range */
+ ck_assert_double_ge(pos, expected - 0.02);
+ ck_assert_double_le(pos, expected + 0.02);
+
+ libinput_event_destroy(ev);
+
+ expected = pos + 0.08;
+ }
+
+ litest_pad_strip_change(dev, 100);
+ libinput_dispatch(li);
+
+ ev = libinput_get_event(li);
+ pev = litest_is_pad_strip_event(ev,
+ 0,
+ LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
+ pos = libinput_event_tablet_pad_get_strip_position(pev);
+ ck_assert_double_eq(pos, 1.0);
+ libinput_event_destroy(ev);
+
+ litest_pad_strip_end(dev);
+}
+END_TEST
+
+START_TEST(pad_strip_finger_up)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *ev;
+ struct libinput_event_tablet_pad *pev;
+ double pos;
+
+ litest_pad_strip_start(dev, 10);
+ litest_drain_events(li);
+
+ litest_pad_strip_end(dev);
+ libinput_dispatch(li);
+
+ ev = libinput_get_event(li);
+ pev = litest_is_pad_strip_event(ev,
+ 0,
+ LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
+
+ pos = libinput_event_tablet_pad_get_strip_position(pev);
+ ck_assert_double_eq(pos, -1.0);
+ libinput_event_destroy(ev);
+
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(pad_left_handed_default)
+{
+#if HAVE_LIBWACOM
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ enum libinput_config_status status;
+
+ ck_assert(libinput_device_config_left_handed_is_available(device));
+
+ ck_assert_int_eq(libinput_device_config_left_handed_get_default(device),
+ 0);
+ ck_assert_int_eq(libinput_device_config_left_handed_get(device),
+ 0);
+
+ status = libinput_device_config_left_handed_set(dev->libinput_device, 1);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+
+ ck_assert_int_eq(libinput_device_config_left_handed_get(device),
+ 1);
+ ck_assert_int_eq(libinput_device_config_left_handed_get_default(device),
+ 0);
+
+ status = libinput_device_config_left_handed_set(dev->libinput_device, 0);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+
+ ck_assert_int_eq(libinput_device_config_left_handed_get(device),
+ 0);
+ ck_assert_int_eq(libinput_device_config_left_handed_get_default(device),
+ 0);
+
+#endif
+}
+END_TEST
+
+START_TEST(pad_no_left_handed)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ enum libinput_config_status status;
+
+ ck_assert(!libinput_device_config_left_handed_is_available(device));
+
+ ck_assert_int_eq(libinput_device_config_left_handed_get_default(device),
+ 0);
+ ck_assert_int_eq(libinput_device_config_left_handed_get(device),
+ 0);
+
+ status = libinput_device_config_left_handed_set(dev->libinput_device, 1);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+
+ ck_assert_int_eq(libinput_device_config_left_handed_get(device),
+ 0);
+ ck_assert_int_eq(libinput_device_config_left_handed_get_default(device),
+ 0);
+
+ status = libinput_device_config_left_handed_set(dev->libinput_device, 0);
+ ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+
+ ck_assert_int_eq(libinput_device_config_left_handed_get(device),
+ 0);
+ ck_assert_int_eq(libinput_device_config_left_handed_get_default(device),
+ 0);
+}
+END_TEST
+
+START_TEST(pad_left_handed_ring)
+{
+#if HAVE_LIBWACOM
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ struct libinput_event *ev;
+ struct libinput_event_tablet_pad *pev;
+ int val;
+ double degrees, expected;
+
+ libinput_device_config_left_handed_set(dev->libinput_device, 1);
+
+ litest_pad_ring_start(dev, 10);
+
+ litest_drain_events(li);
+
+ /* Wacom's 0 value is at 275 degrees -> 90 in left-handed mode*/
+ expected = 90;
+
+ for (val = 0; val < 100; val += 10) {
+ litest_pad_ring_change(dev, val);
+ libinput_dispatch(li);
+
+ ev = libinput_get_event(li);
+ pev = litest_is_pad_ring_event(ev,
+ 0,
+ LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
+
+ degrees = libinput_event_tablet_pad_get_ring_position(pev);
+ ck_assert_double_ge(degrees, 0.0);
+ ck_assert_double_lt(degrees, 360.0);
+
+ /* rounding errors, mostly caused by small physical range */
+ ck_assert_double_ge(degrees, expected - 2);
+ ck_assert_double_le(degrees, expected + 2);
+
+ libinput_event_destroy(ev);
+
+ expected = fmod(degrees + 36, 360);
+ }
+
+ litest_pad_ring_end(dev);
+#endif
+}
+END_TEST
+
+void
+litest_setup_tests(void)
+{
+ litest_add("pad:cap", pad_cap, LITEST_TABLET_PAD, LITEST_ANY);
+ litest_add("pad:cap", pad_no_cap, LITEST_ANY, LITEST_TABLET_PAD);
+
+ litest_add("pad:button", pad_num_buttons, LITEST_TABLET_PAD, LITEST_ANY);
+ litest_add("pad:button", pad_button, LITEST_TABLET_PAD, LITEST_ANY);
+
+ litest_add("pad:ring", pad_has_ring, LITEST_RING, LITEST_ANY);
+ litest_add("pad:ring", pad_ring, LITEST_RING, LITEST_ANY);
+ litest_add("pad:ring", pad_ring_finger_up, LITEST_RING, LITEST_ANY);
+
+ litest_add("pad:strip", pad_has_strip, LITEST_STRIP, LITEST_ANY);
+ litest_add("pad:strip", pad_strip, LITEST_STRIP, LITEST_ANY);
+ litest_add("pad:strip", pad_strip_finger_up, LITEST_STRIP, LITEST_ANY);
+
+ litest_add_for_device("pad:left_handed", pad_left_handed_default, LITEST_WACOM_INTUOS5_PAD);
+ litest_add_for_device("pad:left_handed", pad_no_left_handed, LITEST_WACOM_INTUOS3_PAD);
+ litest_add_for_device("pad:left_handed", pad_left_handed_ring, LITEST_WACOM_INTUOS5_PAD);
+ /* None of the current strip tablets are left-handed */
+}
diff --git a/tools/event-debug.c b/tools/event-debug.c
index 079029b2..aa64e7d3 100644
--- a/tools/event-debug.c
+++ b/tools/event-debug.c
@@ -121,6 +121,15 @@ print_event_header(struct libinput_event *ev)
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
type = "TABLET_TOOL_BUTTON";
break;
+ case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
+ type = "TABLET_PAD_BUTTON";
+ break;
+ case LIBINPUT_EVENT_TABLET_PAD_RING:
+ type = "TABLET_PAD_RING";
+ break;
+ case LIBINPUT_EVENT_TABLET_PAD_STRIP:
+ type = "TABLET_PAD_STRIP";
+ break;
}
printf("%-7s %-16s ", libinput_device_get_sysname(dev), type);
@@ -172,6 +181,9 @@ print_device_notify(struct libinput_event *ev)
if (libinput_device_has_capability(dev,
LIBINPUT_DEVICE_CAP_TABLET_TOOL))
printf("T");
+ if (libinput_device_has_capability(dev,
+ LIBINPUT_DEVICE_CAP_TABLET_PAD))
+ printf("P");
if (libinput_device_get_size(dev, &w, &h) == 0)
printf("\tsize %.2f/%.2fmm", w, h);
@@ -574,6 +586,66 @@ print_gesture_event_with_coords(struct libinput_event *ev)
}
}
+static void
+print_tablet_pad_button_event(struct libinput_event *ev)
+{
+ struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
+ enum libinput_button_state state;
+
+ print_event_time(libinput_event_tablet_pad_get_time(p));
+
+ state = libinput_event_tablet_pad_get_button_state(p);
+ printf("%3d %s\n",
+ libinput_event_tablet_pad_get_button_number(p),
+ state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released");
+}
+
+static void
+print_tablet_pad_ring_event(struct libinput_event *ev)
+{
+ struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
+ const char *source = "";
+
+ print_event_time(libinput_event_tablet_pad_get_time(p));
+
+ switch (libinput_event_tablet_pad_get_ring_source(p)) {
+ case LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER:
+ source = "finger";
+ break;
+ case LIBINPUT_TABLET_PAD_RING_SOURCE_UNKNOWN:
+ source = "unknown";
+ break;
+ }
+
+ printf("ring %d position %.2f (source %s)\n",
+ libinput_event_tablet_pad_get_ring_number(p),
+ libinput_event_tablet_pad_get_ring_position(p),
+ source);
+}
+
+static void
+print_tablet_pad_strip_event(struct libinput_event *ev)
+{
+ struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
+ const char *source = "";
+
+ print_event_time(libinput_event_tablet_pad_get_time(p));
+
+ switch (libinput_event_tablet_pad_get_strip_source(p)) {
+ case LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER:
+ source = "finger";
+ break;
+ case LIBINPUT_TABLET_PAD_STRIP_SOURCE_UNKNOWN:
+ source = "unknown";
+ break;
+ }
+
+ printf("strip %d position %.2f (source %s)\n",
+ libinput_event_tablet_pad_get_strip_number(p),
+ libinput_event_tablet_pad_get_strip_position(p),
+ source);
+}
+
static int
handle_and_print_events(struct libinput *li)
{
@@ -653,6 +725,15 @@ handle_and_print_events(struct libinput *li)
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
print_tablet_button_event(ev);
break;
+ case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
+ print_tablet_pad_button_event(ev);
+ break;
+ case LIBINPUT_EVENT_TABLET_PAD_RING:
+ print_tablet_pad_ring_event(ev);
+ break;
+ case LIBINPUT_EVENT_TABLET_PAD_STRIP:
+ print_tablet_pad_strip_event(ev);
+ break;
}
libinput_event_destroy(ev);
diff --git a/tools/event-gui.c b/tools/event-gui.c
index 9d541a3d..791a1ea1 100644
--- a/tools/event-gui.c
+++ b/tools/event-gui.c
@@ -798,6 +798,10 @@ handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
handle_event_tablet(ev, w);
break;
+ case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
+ case LIBINPUT_EVENT_TABLET_PAD_RING:
+ case LIBINPUT_EVENT_TABLET_PAD_STRIP:
+ break;
}
libinput_event_destroy(ev);