[PATCH v2 libinput 4/5] pad: implement wacom pad support

Jason Gerecke killertofu at gmail.com
Thu Apr 14 22:33:53 UTC 2016


On 04/10/2016 09:15 PM, Peter Hutterer wrote:
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> ---
> Adjustments for the new API in 2/5
> 
>  src/Makefile.am        |   1 +
>  src/evdev-tablet-pad.c | 614 +++++++++++++++++++++++++++++++++++++++++++++++++
>  src/evdev-tablet-pad.h |  69 ++++++
>  src/evdev.c            |  28 ++-
>  src/evdev.h            |  13 ++
>  src/libinput.c         |   6 +-
>  6 files changed, 716 insertions(+), 15 deletions(-)
>  create mode 100644 src/evdev-tablet-pad.c
>  create mode 100644 src/evdev-tablet-pad.h
> 
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 343e75c..a3df6c8 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -20,6 +20,7 @@ libinput_la_SOURCES =			\
>  	evdev-mt-touchpad-gestures.c	\
>  	evdev-tablet.c			\
>  	evdev-tablet.h			\
> +	evdev-tablet-pad.c		\
>  	filter.c			\
>  	filter.h			\
>  	filter-private.h		\
> diff --git a/src/evdev-tablet-pad.c b/src/evdev-tablet-pad.c
> new file mode 100644
> index 0000000..8c64830
> --- /dev/null
> +++ b/src/evdev-tablet-pad.c
> @@ -0,0 +1,614 @@
> +/*
> + * Copyright © 2016 Red Hat, Inc.
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and
> + * its documentation for any purpose is hereby granted without fee, provided
> + * that the above copyright notice appear in all copies and that both that
> + * copyright notice and this permission notice appear in supporting
> + * documentation, and that the name of the copyright holders not be used in
> + * advertising or publicity pertaining to distribution of the software
> + * without specific, written prior permission.  The copyright holders make
> + * no representations about the suitability of this software for any
> + * purpose.  It is provided "as is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
> + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
> + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
> + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
> + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
> + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include "config.h"
> +#include "evdev-tablet-pad.h"
> +
> +#include <assert.h>
> +#include <stdbool.h>
> +#include <string.h>
> +
> +#define pad_set_status(pad_,s_) (pad_)->status |= (s_)
> +#define pad_unset_status(pad_,s_) (pad_)->status &= ~(s_)
> +#define pad_has_status(pad_,s_) (!!((pad_)->status & (s_)))
> +
> +static void
> +pad_get_buttons_pressed(struct pad_dispatch *pad,
> +			struct button_state *buttons)
> +{
> +	struct button_state *state = &pad->button_state;
> +	struct button_state *prev_state = &pad->prev_button_state;
> +	unsigned int i;
> +
> +	for (i = 0; i < sizeof(buttons->bits); i++)
> +		buttons->bits[i] = state->bits[i] & ~(prev_state->bits[i]);
> +}
> +
> +static void
> +pad_get_buttons_released(struct pad_dispatch *pad,
> +			 struct button_state *buttons)
> +{
> +	struct button_state *state = &pad->button_state;
> +	struct button_state *prev_state = &pad->prev_button_state;
> +	unsigned int i;
> +
> +	for (i = 0; i < sizeof(buttons->bits); i++)
> +		buttons->bits[i] = prev_state->bits[i] & ~(state->bits[i]);
> +}
> +
> +static inline bool
> +pad_button_is_down(const struct pad_dispatch *pad,
> +		   uint32_t button)
> +{
> +	return bit_is_set(pad->button_state.bits, button);
> +}
> +
> +static inline 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;
> +
> +	for (code = BTN_0; code < BTN_MOUSE; code++) {
> +		if (libevdev_has_event_code(device->evdev, EV_KEY, code))
> +			pad->button_map[code] = map++;
> +	}
> +
> +	for (code = BTN_A; code < BTN_DIGI; code++) {
> +		if (libevdev_has_event_code(device->evdev, EV_KEY, code))
> +			pad->button_map[code] = map++;
> +	}
> +
> +	pad->nbuttons = map;
> +}
> +

For the sake of the compatibility with future devices, I'd recommend
more strictly following the button ranges used by the kernel:
http://lxr.free-electrons.com/source/drivers/hid/wacom_wac.c#L2712

Also, note that you're missing coverage of BTN_BASE and BTN_BASE2.

Jason
---
Now instead of four in the eights place /
you’ve got three, ‘Cause you added one /
(That is to say, eight) to the two, /
But you can’t take seven from three, /
So you look at the sixty-fours....

> +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 0000000..828ded8
> --- /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.c b/src/evdev.c
> index a5511c5..0ec3823 100644
> --- a/src/evdev.c
> +++ b/src/evdev.c
> @@ -65,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),
>  };
>  
> @@ -82,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},
> @@ -2031,7 +2031,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,
> @@ -2049,14 +2049,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",
> @@ -2092,7 +2084,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,
> @@ -2521,6 +2523,8 @@ evdev_device_has_capability(struct evdev_device *device,
>  		return !!(device->seat_caps & EVDEV_DEVICE_GESTURE);
>  	case LIBINPUT_DEVICE_CAP_TABLET_TOOL:
>  		return !!(device->seat_caps & EVDEV_DEVICE_TABLET);
> +	case LIBINPUT_DEVICE_CAP_TABLET_PAD:
> +		return !!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD);
>  	default:
>  		return 0;
>  	}
> diff --git a/src/evdev.h b/src/evdev.h
> index f5e7bed..d76c3dd 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),
>  };
>  
> @@ -317,6 +318,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);
> @@ -367,6 +371,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,
> diff --git a/src/libinput.c b/src/libinput.c
> index 53da963..22fef0a 100644
> --- a/src/libinput.c
> +++ b/src/libinput.c
> @@ -2794,19 +2794,19 @@ libinput_device_keyboard_has_key(struct libinput_device *device, uint32_t code)
>  LIBINPUT_EXPORT int
>  libinput_device_tablet_pad_get_num_buttons(struct libinput_device *device)
>  {
> -	return 0;
> +	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 0;
> +	return evdev_device_tablet_pad_get_num_rings((struct evdev_device *)device);
>  }
>  
>  LIBINPUT_EXPORT int
>  libinput_device_tablet_pad_get_num_strips(struct libinput_device *device)
>  {
> -	return 0;
> +	return evdev_device_tablet_pad_get_num_strips((struct evdev_device *)device);
>  }
>  
>  LIBINPUT_EXPORT struct libinput_event *
> 


More information about the wayland-devel mailing list