Same approach as evdev-tablet (started as copy/paste), with axis and buttons adjusted. Wacom's handling of pad devices requires a lot of non-obvious handling, e.g. ABS_THROTTLE is the second ring, ABS_RX is the strip, etc.
This is not generic buttonset code, if we start supporting other devices for buttonsets we'll factor out a couple of the functions. The wheel and strip events are a bit of a problem: Wacom sends a 0 event on the axis when the finger is released. We can detect this if there is an ABS_MISC 0 present in the same event and suppress it. Won't work if any finger is down on any other wheel, strip or button but that's a kernel bug we'll fix once we figure out how. Signed-off-by: Peter Hutterer <[email protected]> --- Changes to v2: - reworked for the per-axis type API - squashed heaps of fixes piled on after the last version into this patch. Note, this is not a final version yet, it just serves to illustrate the implementation. src/Makefile.am | 2 + src/evdev-buttonset-wacom.c | 602 ++++++++++++++++++++++++++++++++++++++++++++ src/evdev-buttonset-wacom.h | 67 +++++ src/evdev-mt-touchpad.c | 3 + src/evdev-tablet.c | 3 + src/evdev.c | 84 ++++++- src/evdev.h | 32 +++ src/libinput-private.h | 29 +++ src/libinput.c | 105 ++++++-- 9 files changed, 898 insertions(+), 29 deletions(-) create mode 100644 src/evdev-buttonset-wacom.c create mode 100644 src/evdev-buttonset-wacom.h diff --git a/src/Makefile.am b/src/Makefile.am index 343e75c..723f7d6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,6 +11,8 @@ libinput_la_SOURCES = \ libinput-private.h \ evdev.c \ evdev.h \ + evdev-buttonset-wacom.c \ + evdev-buttonset-wacom.h \ evdev-middle-button.c \ evdev-mt-touchpad.c \ evdev-mt-touchpad.h \ diff --git a/src/evdev-buttonset-wacom.c b/src/evdev-buttonset-wacom.c new file mode 100644 index 0000000..7dec3e8 --- /dev/null +++ b/src/evdev-buttonset-wacom.c @@ -0,0 +1,602 @@ +/* + * Copyright © 2015 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" +#include "evdev-buttonset-wacom.h" + +#include <assert.h> +#include <stdbool.h> +#include <string.h> + +#define buttonset_set_status(buttonset_,s_) (buttonset_)->status |= (s_) +#define buttonset_unset_status(buttonset_,s_) (buttonset_)->status &= ~(s_) +#define buttonset_has_status(buttonset_,s_) (!!((buttonset_)->status & (s_))) + +static void +buttonset_get_buttons_pressed(struct buttonset_dispatch *buttonset, + unsigned long buttons_pressed[NLONGS(KEY_CNT)]) +{ + struct button_state *state = &buttonset->button_state; + struct button_state *prev_state = &buttonset->prev_button_state; + unsigned int i; + + for (i = 0; i < NLONGS(KEY_CNT); i++) + buttons_pressed[i] = state->buttons[i] + & ~(prev_state->buttons[i]); +} + +static void +buttonset_get_buttons_released(struct buttonset_dispatch *buttonset, + unsigned long buttons_released[NLONGS(KEY_CNT)]) +{ + struct button_state *state = &buttonset->button_state; + struct button_state *prev_state = &buttonset->prev_button_state; + unsigned int i; + + for (i = 0; i < NLONGS(KEY_CNT); i++) + buttons_released[i] = prev_state->buttons[i] + & ~(state->buttons[i]); +} + +static inline bool +buttonset_button_is_down(const struct buttonset_dispatch *buttonset, + uint32_t button) +{ + return long_bit_is_set(buttonset->button_state.buttons, button); +} + +static inline void +buttonset_button_set_down(struct buttonset_dispatch *buttonset, + uint32_t button, + bool is_down) +{ + struct button_state *state = &buttonset->button_state; + + if (is_down) { + long_set_bit(state->buttons, button); + buttonset_set_status(buttonset, BUTTONSET_BUTTONS_PRESSED); + } else { + long_clear_bit(state->buttons, button); + buttonset_set_status(buttonset, BUTTONSET_BUTTONS_RELEASED); + } +} + +static void +buttonset_process_absolute(struct buttonset_dispatch *buttonset, + struct evdev_device *device, + struct input_event *e, + uint32_t time) +{ + unsigned int axis; + + switch (e->code) { + case ABS_WHEEL: + case ABS_THROTTLE: + case ABS_RX: + case ABS_RY: + axis = buttonset->evcode_map[e->code]; + if (axis == (unsigned int)-1) { + log_bug_libinput(device->base.seat->libinput, + "Unhandled EV_ABS mapping for %#x\n", + e->code); + break; + } + + set_bit(buttonset->changed_axes, axis); + buttonset_set_status(buttonset, BUTTONSET_AXES_UPDATED); + break; + case ABS_MISC: + /* The wacom driver always sends a 0 axis event on finger + up, but we also get an ABS_MISC 15 on touch down and + ABS_MISC 0 on touch up, on top of the actual event. This + is kernel behavior for xf86-input-wacom backwards + compatibility after the 3.17 wacom HID move. + + We use that event to tell when we truly went a full + rotation around the wheel vs. a finger release. + + FIXME: On the Intuos5 and later the kernel merges all + states into that event, so if any finger is down on any + button, the wheel release won't trigger the ABS_MISC 0 + but still send a 0 event. We can't currently detect this. + */ + buttonset->have_abs_misc_terminator = true; + break; + default: + log_info(device->base.seat->libinput, + "Unhandled EV_ABS event code %#x\n", e->code); + break; + } +} + +static 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; +} + +/* FIXME: + * - switch the loop in check_notify_axes to a similar approach as used + * in the tablet source + * - switch all special axis handling out into helper functions + * - switch everything over to the new axis struct + */ +static inline double +buttonset_handle_ring(struct buttonset_dispatch *buttonset, + struct evdev_device *device, + unsigned int axis) +{ + unsigned int code; + const struct input_absinfo *absinfo; + + code = buttonset->axes[axis].evcode; + assert(code != 0); + absinfo = libevdev_get_abs_info(device->evdev, code); + assert(absinfo); + + return normalize_ring(absinfo); +} + +static inline bool +buttonset_handle_strip(struct buttonset_dispatch *buttonset, + struct evdev_device *device, + unsigned int axis, + double *value) +{ + unsigned int code; + const struct input_absinfo *absinfo; + + code = buttonset->axes[axis].evcode; + assert(code != 0); + absinfo = libevdev_get_abs_info(device->evdev, code); + assert(absinfo); + + /* value 0 is a finger release, ignore it */ + if (absinfo->value == 0) + return false; + + *value = normalize_strip(absinfo); + + return true; +} + +static void +buttonset_check_notify_axes(struct buttonset_dispatch *buttonset, + struct evdev_device *device, + uint32_t time) +{ + struct libinput_device *base = &device->base; + bool axis_update_needed = false; + unsigned int a; + double value; + struct libinput_buttonset_axis axes[buttonset->naxes]; + + /* Suppress the reset to 0 on finger up. See the + comment in buttonset_process_absolute */ + if (buttonset->have_abs_misc_terminator && + libevdev_get_event_value(device->evdev, EV_ABS, ABS_MISC) == 0) { + goto out; + } + + for (a = 0; a < buttonset->naxes; a++) { + if (!bit_is_set(buttonset->changed_axes, a)) { + axes[a] = buttonset->axes[a].base; + continue; + } + + switch (buttonset->axes[a].base.type) { + case LIBINPUT_BUTTONSET_AXIS_RING: + value = buttonset_handle_ring(buttonset, + device, + a); + buttonset->axes[a].base.value.degrees = value; + buttonset->axes[a].base.source = + LIBINPUT_BUTTONSET_AXIS_SOURCE_UNKNOWN; + break; + case LIBINPUT_BUTTONSET_AXIS_STRIP: + if (buttonset_handle_strip(buttonset, + device, + a, + &value)) { + buttonset->axes[a].base.value.normalized = + value; + buttonset->axes[a].base.source = + LIBINPUT_BUTTONSET_AXIS_SOURCE_UNKNOWN; + } else { + clear_bit(buttonset->changed_axes, a); + continue; + } + break; + default: + log_bug_libinput(device->base.seat->libinput, + "Invalid axis update: %u\n", a); + break; + } + + axis_update_needed = true; + axes[a] = buttonset->axes[a].base; + } + + if (axis_update_needed) + buttonset_notify_axis(base, + time, + buttonset->changed_axes, + axes, + ARRAY_LENGTH(axes)); + +out: + memset(buttonset->changed_axes, 0, sizeof(buttonset->changed_axes)); + buttonset->have_abs_misc_terminator = false; +} + +static void +buttonset_process_key(struct buttonset_dispatch *buttonset, + struct evdev_device *device, + struct input_event *e, + uint32_t time) +{ + uint32_t button = e->code; + uint32_t is_press = e->value != 0; + + buttonset_button_set_down(buttonset, button, is_press); +} + +static void +buttonset_notify_button_mask(struct buttonset_dispatch *buttonset, + struct evdev_device *device, + uint32_t time, + unsigned long *buttons, + enum libinput_button_state state) +{ + struct libinput_device *base = &device->base; + int32_t num_button; + unsigned int i; + struct libinput_buttonset_axis axes[buttonset->naxes]; + unsigned int a; + + for (a = 0; a < buttonset->naxes; a++) + axes[a] = buttonset->axes[a].base; + + for (i = 0; i < NLONGS(KEY_CNT); i++) { + unsigned long buttons_slice = buttons[i]; + + num_button = i * LONG_BITS; + while (buttons_slice) { + int enabled; + + num_button++; + enabled = (buttons_slice & 1); + buttons_slice >>= 1; + + if (!enabled) + continue; + + buttonset_notify_button(base, + time, + axes, + ARRAY_LENGTH(axes), + num_button - 1, + state); + } + } +} + +static void +buttonset_notify_buttons(struct buttonset_dispatch *buttonset, + struct evdev_device *device, + uint32_t time, + enum libinput_button_state state) +{ + unsigned long buttons[NLONGS(KEY_CNT)]; + + if (state == LIBINPUT_BUTTON_STATE_PRESSED) + buttonset_get_buttons_pressed(buttonset, + buttons); + else + buttonset_get_buttons_released(buttonset, + buttons); + + buttonset_notify_button_mask(buttonset, + device, + time, + buttons, + state); +} + +static void +sanitize_buttonset_axes(struct buttonset_dispatch *buttonset) +{ +} + +static void +buttonset_flush(struct buttonset_dispatch *buttonset, + struct evdev_device *device, + uint32_t time) +{ + if (buttonset_has_status(buttonset, BUTTONSET_AXES_UPDATED)) { + sanitize_buttonset_axes(buttonset); + buttonset_check_notify_axes(buttonset, device, time); + buttonset_unset_status(buttonset, BUTTONSET_AXES_UPDATED); + } + + if (buttonset_has_status(buttonset, BUTTONSET_BUTTONS_RELEASED)) { + buttonset_notify_buttons(buttonset, + device, + time, + LIBINPUT_BUTTON_STATE_RELEASED); + buttonset_unset_status(buttonset, BUTTONSET_BUTTONS_RELEASED); + } + + if (buttonset_has_status(buttonset, BUTTONSET_BUTTONS_PRESSED)) { + buttonset_notify_buttons(buttonset, + device, + time, + LIBINPUT_BUTTON_STATE_PRESSED); + buttonset_unset_status(buttonset, BUTTONSET_BUTTONS_PRESSED); + } + + /* Update state */ + memcpy(&buttonset->prev_button_state, + &buttonset->button_state, + sizeof(buttonset->button_state)); +} + +static void +buttonset_process(struct evdev_dispatch *dispatch, + struct evdev_device *device, + struct input_event *e, + uint64_t time) +{ + struct buttonset_dispatch *buttonset = + (struct buttonset_dispatch *)dispatch; + + switch (e->type) { + case EV_ABS: + buttonset_process_absolute(buttonset, device, e, time); + break; + case EV_KEY: + buttonset_process_key(buttonset, device, e, time); + break; + case EV_SYN: + buttonset_flush(buttonset, device, time); + break; + default: + log_error(device->base.seat->libinput, + "Unexpected event type %s (%#x)\n", + libevdev_event_type_get_name(e->type), + e->type); + break; + } +} + +static void +buttonset_suspend(struct evdev_dispatch *dispatch, + struct evdev_device *device) +{ + struct buttonset_dispatch *buttonset = + (struct buttonset_dispatch *)dispatch; + struct libinput *libinput = device->base.seat->libinput; + unsigned int code; + + for (code = KEY_ESC; code < KEY_CNT; code++) { + if (buttonset_button_is_down(buttonset, code)) + buttonset_button_set_down(buttonset, code, false); + } + + buttonset_flush(buttonset, device, libinput_now(libinput)); +} + +static void +buttonset_destroy(struct evdev_dispatch *dispatch) +{ + struct buttonset_dispatch *buttonset = + (struct buttonset_dispatch*)dispatch; + + free(buttonset); +} + +static unsigned int +buttonset_get_num_axes(struct evdev_device *device) +{ + struct buttonset_dispatch *buttonset = + (struct buttonset_dispatch*)device->dispatch; + + return buttonset->naxes; +} + +static enum libinput_buttonset_axis_type +buttonset_get_axis_type(struct evdev_device *device, unsigned int axis) +{ + struct buttonset_dispatch *buttonset = + (struct buttonset_dispatch*)device->dispatch; + + if (axis < buttonset->naxes) + return buttonset->axes[axis].base.type; + + log_bug_client(device->base.seat->libinput, + "Axis %d does not exist on device %s\n", + axis, + device->devname); + + return 0; +} + +static struct evdev_dispatch_interface buttonset_interface = { + buttonset_process, + buttonset_suspend, /* suspend */ + NULL, /* remove */ + buttonset_destroy, + NULL, /* device_added */ + NULL, /* device_removed */ + NULL, /* device_suspended */ + NULL, /* device_resumed */ + NULL, /* post_added */ + buttonset_get_num_axes, + buttonset_get_axis_type, +}; + +static enum libinput_buttonset_axis_type +buttonset_guess_axis_type(struct evdev_device *device, + unsigned int evcode) +{ + switch (evcode) { + case ABS_WHEEL: + case ABS_THROTTLE: + return LIBINPUT_BUTTONSET_AXIS_RING; + case ABS_RX: + case ABS_RY: + return LIBINPUT_BUTTONSET_AXIS_STRIP; + default: + return LIBINPUT_BUTTONSET_AXIS_NONE; + } +} + +static int +buttonset_init(struct buttonset_dispatch *buttonset, + struct evdev_device *device) +{ + unsigned int naxes = 0; + int code; + enum libinput_buttonset_axis_type type; + + buttonset->base.interface = &buttonset_interface; + buttonset->device = device; + buttonset->status = BUTTONSET_NONE; + + /* We intentionally skip X/Y/Z, they're dead on most wacom pads and + the 27QHD sends accelerometer data through those three */ + for (code = ABS_RX; code <= ABS_MAX; code++) { + buttonset->evcode_map[code] = -1; + + if (!libevdev_has_event_code(device->evdev, EV_ABS, code)) + continue; + + /* Ignore axes we don't know about */ + type = buttonset_guess_axis_type(device, code); + if (type == LIBINPUT_BUTTONSET_AXIS_NONE) + continue; + + buttonset->evcode_map[code] = naxes; + buttonset->axes[naxes].base.type = type; + buttonset->axes[naxes].evcode = code; + naxes++; + } + + buttonset->naxes = naxes; + + return 0; +} + +static uint32_t +bs_sendevents_get_modes(struct libinput_device *device) +{ + return LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; +} + +static enum libinput_config_status +bs_sendevents_set_mode(struct libinput_device *device, + enum libinput_config_send_events_mode mode) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + struct buttonset_dispatch *buttonset = + (struct buttonset_dispatch*)evdev->dispatch; + + if (mode == buttonset->sendevents.current_mode) + return LIBINPUT_CONFIG_STATUS_SUCCESS; + + switch(mode) { + case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED: + break; + case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED: + buttonset_suspend(evdev->dispatch, evdev); + break; + default: + return LIBINPUT_CONFIG_STATUS_UNSUPPORTED; + } + + buttonset->sendevents.current_mode = mode; + + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + +static enum libinput_config_send_events_mode +bs_sendevents_get_mode(struct libinput_device *device) +{ + struct evdev_device *evdev = (struct evdev_device*)device; + struct buttonset_dispatch *dispatch = + (struct buttonset_dispatch*)evdev->dispatch; + + return dispatch->sendevents.current_mode; +} + +static enum libinput_config_send_events_mode +bs_sendevents_get_default_mode(struct libinput_device *device) +{ + return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; +} + +struct evdev_dispatch * +evdev_buttonset_create(struct evdev_device *device) +{ + struct buttonset_dispatch *buttonset; + + buttonset = zalloc(sizeof *buttonset); + if (!buttonset) + return NULL; + + if (buttonset_init(buttonset, device) != 0) { + buttonset_destroy(&buttonset->base); + return NULL; + } + + device->base.config.sendevents = &buttonset->sendevents.config; + buttonset->sendevents.current_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; + buttonset->sendevents.config.get_modes = bs_sendevents_get_modes; + buttonset->sendevents.config.set_mode = bs_sendevents_set_mode; + buttonset->sendevents.config.get_mode = bs_sendevents_get_mode; + buttonset->sendevents.config.get_default_mode = bs_sendevents_get_default_mode; + + return &buttonset->base; +} diff --git a/src/evdev-buttonset-wacom.h b/src/evdev-buttonset-wacom.h new file mode 100644 index 0000000..ad0afe3 --- /dev/null +++ b/src/evdev-buttonset-wacom.h @@ -0,0 +1,67 @@ +/* + * 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 buttonset_status { + BUTTONSET_NONE = 0, + BUTTONSET_AXES_UPDATED = 1 << 0, + BUTTONSET_BUTTONS_PRESSED = 1 << 1, + BUTTONSET_BUTTONS_RELEASED = 1 << 2, +}; + +struct button_state { + /* Bitmask of pressed buttons. */ + unsigned long buttons[NLONGS(KEY_CNT)]; +}; + +struct buttonset_axis { + struct libinput_buttonset_axis base; + unsigned int evcode; +}; + +struct buttonset_dispatch { + struct evdev_dispatch base; + struct evdev_device *device; + unsigned char status; + unsigned int naxes; + unsigned int evcode_map[ABS_CNT]; /* evcode to axis number */ + unsigned char changed_axes[NCHARS(LIBINPUT_BUTTONSET_MAX_NUM_AXES)]; + struct buttonset_axis axes[LIBINPUT_BUTTONSET_MAX_NUM_AXES]; + + struct button_state button_state; + struct button_state prev_button_state; + + bool have_abs_misc_terminator; + + struct { + struct libinput_device_config_send_events config; + enum libinput_config_send_events_mode current_mode; + } sendevents; +}; + +#endif diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 11cd1c3..c1cfbce 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1426,6 +1426,9 @@ static struct evdev_dispatch_interface tp_interface = { tp_interface_device_removed, /* device_suspended, treat as remove */ tp_interface_device_added, /* device_resumed, treat as add */ NULL, /* post_added */ + NULL, /* buttonset_to_phys */ + NULL, /* buttonset_get_num_axes */ + NULL, /* buttonset_get_axis_type */ }; static void diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index 1870e7e..5c98654 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -1429,6 +1429,9 @@ static struct evdev_dispatch_interface tablet_interface = { NULL, /* device_suspended */ NULL, /* device_resumed */ tablet_check_initial_proximity, + NULL, /* buttonset_to_phys */ + NULL, /* buttonset_get_num_axes */ + NULL, /* buttonset_get_axis_type */ }; static void diff --git a/src/evdev.c b/src/evdev.c index d28da06..1bf3c24 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -245,6 +245,26 @@ evdev_device_transform_y(struct evdev_device *device, return scale_axis(device->abs.absinfo_y, y, height); } +double +evdev_device_transform_x_mm(struct evdev_device *device, + double mm, + uint32_t width) +{ + mm *= device->abs.absinfo_x->resolution; + + return evdev_device_transform_x(device, mm, width); +} + +double +evdev_device_transform_y_mm(struct evdev_device *device, + double mm, + uint32_t height) +{ + mm *= device->abs.absinfo_y->resolution; + + return evdev_device_transform_y(device, mm, height); +} + static inline void normalize_delta(struct evdev_device *device, const struct device_coords *delta, @@ -963,6 +983,9 @@ struct evdev_dispatch_interface fallback_interface = { NULL, /* device_suspended */ NULL, /* device_resumed */ NULL, /* post_added */ + NULL, /* buttonset_to_phys */ + NULL, /* buttonset_get_num_axes */ + NULL, /* buttonset_get_axis_type */ }; static uint32_t @@ -2028,14 +2051,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", @@ -2065,13 +2080,23 @@ evdev_configure_device(struct evdev_device *device) } } - /* libwacom assigns touchpad (or touchscreen) _and_ tablet to the - tablet touch bits, so make sure we don't initialize the tablet - interface for the touch 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_BUTTONSET) { + device->dispatch = evdev_buttonset_create(device); + device->seat_caps |= EVDEV_DEVICE_BUTTONSET; + log_info(libinput, + "input device '%s', %s is a buttonset\n", + device->devname, devnode); + return device->dispatch == NULL ? -1 : 0; + + /* libwacom assigns touchpad _and_ tablet to the tablet touch bits, + so make sure we don't initialize the tablet interface for the + touch device */ + } 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, @@ -2465,6 +2490,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_BUTTONSET: + return !!(device->seat_caps & EVDEV_DEVICE_BUTTONSET); default: return 0; } @@ -2508,6 +2535,39 @@ evdev_device_keyboard_has_key(struct evdev_device *device, uint32_t code) return libevdev_has_event_code(device->evdev, EV_KEY, code); } +int +evdev_device_buttonset_has_button(struct evdev_device *device, + uint32_t code) +{ + if (!(device->seat_caps & EVDEV_DEVICE_BUTTONSET)) + return -1; + + return libevdev_has_event_code(device->evdev, EV_KEY, code); +} + +unsigned int +evdev_device_buttonset_get_num_axes(struct evdev_device *device) +{ + struct evdev_dispatch *dispatch = device->dispatch; + + if (!(device->seat_caps & EVDEV_DEVICE_BUTTONSET)) + return 0; + + return dispatch->interface->buttonset_get_num_axes(device); +} + +enum libinput_buttonset_axis_type +evdev_device_buttonset_get_axis_type(struct evdev_device *device, + unsigned int axis) +{ + struct evdev_dispatch *dispatch = device->dispatch; + + if (!(device->seat_caps & EVDEV_DEVICE_BUTTONSET)) + return 0; + + return dispatch->interface->buttonset_get_axis_type(device, axis); +} + static inline bool evdev_is_scrolling(const struct evdev_device *device, enum libinput_pointer_axis axis) diff --git a/src/evdev.h b/src/evdev.h index d2383c1..efafdbb 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_BUTTONSET = (1 << 4), EVDEV_DEVICE_GESTURE = (1 << 5), }; @@ -272,6 +273,14 @@ struct evdev_dispatch_interface { * was sent */ void (*post_added)(struct evdev_device *device, struct evdev_dispatch *dispatch); + + /* Return the number of axes on the buttonset device */ + unsigned int (*buttonset_get_num_axes)(struct evdev_device *device); + + /* Return the axis type of the given axes on the buttonset device */ + enum libinput_buttonset_axis_type (*buttonset_get_axis_type)( + struct evdev_device *device, + unsigned int axis); }; struct evdev_dispatch { @@ -309,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_buttonset_create(struct evdev_device *device); + void evdev_tag_touchpad(struct evdev_device *device, struct udev_device *udev_device); @@ -359,6 +371,15 @@ evdev_device_pointer_has_button(struct evdev_device *device, int evdev_device_keyboard_has_key(struct evdev_device *device, uint32_t code); +int +evdev_device_buttonset_has_button(struct evdev_device *device, + uint32_t code); + +enum libinput_buttonset_axis_type +evdev_device_buttonset_get_axis_type(struct evdev_device *device, + unsigned int axis); +unsigned int +evdev_device_buttonset_get_num_axes(struct evdev_device *device); double evdev_device_transform_x(struct evdev_device *device, @@ -369,6 +390,17 @@ double evdev_device_transform_y(struct evdev_device *device, double y, uint32_t height); + +double +evdev_device_transform_x_mm(struct evdev_device *device, + double mm, + uint32_t width); + +double +evdev_device_transform_y_mm(struct evdev_device *device, + double mm, + uint32_t height); + int evdev_device_suspend(struct evdev_device *device); diff --git a/src/libinput-private.h b/src/libinput-private.h index 845dc00..3035537 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -302,6 +302,20 @@ struct libinput_tablet_tool { bool has_pressure_offset; }; +struct libinput_buttonset_axis { + enum libinput_buttonset_axis_type type; + enum libinput_buttonset_axis_source source; + union { + double mm; + double normalized; + double delta; + double degrees; + double position; + + double any; + } value; +}; + struct libinput_event { enum libinput_event_type type; struct libinput_device *device; @@ -533,6 +547,21 @@ tablet_notify_button(struct libinput_device *device, int32_t button, enum libinput_button_state state); +void +buttonset_notify_axis(struct libinput_device *device, + uint32_t time, + unsigned char *changed_axes, + struct libinput_buttonset_axis *axes, + size_t naxes); + +void +buttonset_notify_button(struct libinput_device *device, + uint32_t time, + struct libinput_buttonset_axis *axes, + size_t naxes, + int32_t button, + enum libinput_button_state state); + static inline uint64_t libinput_now(struct libinput *libinput) { diff --git a/src/libinput.c b/src/libinput.c index 3aba899..b41f95c 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -168,18 +168,6 @@ struct libinput_event_tablet_tool { enum libinput_tablet_tool_tip_state tip_state; }; -struct libinput_buttonset_axis { - enum libinput_buttonset_axis_type type; - enum libinput_buttonset_axis_source source; - union { - double mm; - double normalized; - double delta; - double degrees; - double position; - } value; -}; - struct libinput_event_buttonset { struct libinput_event base; uint32_t button; @@ -1602,6 +1590,9 @@ libinput_event_buttonset_get_x_transformed(struct libinput_event_buttonset *even unsigned int axis, uint32_t width) { + struct evdev_device *device = + (struct evdev_device *) event->base.device; + require_event_type(libinput_event_get_context(&event->base), event->base.type, 0.0, @@ -1611,7 +1602,9 @@ libinput_event_buttonset_get_x_transformed(struct libinput_event_buttonset *even LIBINPUT_BUTTONSET_AXIS_X, 0.0); - return event->axes[axis].value.mm; /* FIXME */ + return evdev_device_transform_x_mm(device, + event->axes[axis].value.mm, + width); } LIBINPUT_EXPORT double @@ -1634,6 +1627,9 @@ libinput_event_buttonset_get_y_transformed(struct libinput_event_buttonset *even unsigned int axis, uint32_t height) { + struct evdev_device *device = + (struct evdev_device *) event->base.device; + require_event_type(libinput_event_get_context(&event->base), event->base.type, 0.0, @@ -1643,7 +1639,9 @@ libinput_event_buttonset_get_y_transformed(struct libinput_event_buttonset *even LIBINPUT_BUTTONSET_AXIS_Y, 0.0); - return event->axes[axis].value.mm; /* FIXME */ + return evdev_device_transform_y_mm(device, + event->axes[axis].value.mm, + height); } LIBINPUT_EXPORT double @@ -2691,6 +2689,75 @@ tablet_notify_button(struct libinput_device *device, &button_event->base); } +void +buttonset_notify_axis(struct libinput_device *device, + uint32_t time, + unsigned char *changed_axes, + struct libinput_buttonset_axis *axes, + size_t naxes) +{ + struct libinput_event_buttonset *axis_event; + + if (naxes > ARRAY_LENGTH(axis_event->axes)) { + log_bug_libinput(device->seat->libinput, + "%s: Invalid axis number %zd\n", + __func__, + naxes); + naxes = ARRAY_LENGTH(axis_event->axes); + } + + axis_event = zalloc(sizeof *axis_event); + if (!axis_event) + return; + + *axis_event = (struct libinput_event_buttonset) { + .time = time, + }; + + memcpy(axis_event->changed_axes, + changed_axes, + sizeof(axis_event->changed_axes)); + memcpy(axis_event->axes, axes, sizeof(axis_event->axes)); + + post_device_event(device, + time, + LIBINPUT_EVENT_BUTTONSET_AXIS, + &axis_event->base); +} + +void +buttonset_notify_button(struct libinput_device *device, + uint32_t time, + struct libinput_buttonset_axis *axes, + size_t naxes, + int32_t button, + enum libinput_button_state state) +{ + struct libinput_event_buttonset *button_event; + int32_t seat_button_count; + + button_event = zalloc(sizeof *button_event); + if (!button_event) + return; + + seat_button_count = update_seat_button_count(device->seat, + button, + state); + + *button_event = (struct libinput_event_buttonset) { + .time = time, + .button = button, + .state = state, + .seat_button_count = seat_button_count, + }; + memcpy(button_event->axes, axes, sizeof(button_event->axes)); + + post_device_event(device, + time, + LIBINPUT_EVENT_BUTTONSET_BUTTON, + &button_event->base); +} + static void gesture_notify(struct libinput_device *device, uint64_t time, @@ -3032,20 +3099,24 @@ libinput_device_keyboard_has_key(struct libinput_device *device, uint32_t code) LIBINPUT_EXPORT int libinput_device_buttonset_has_button(struct libinput_device *device, uint32_t code) { - return 0; /* FIXME */ + return evdev_device_buttonset_has_button((struct evdev_device *)device, + code); } LIBINPUT_EXPORT enum libinput_buttonset_axis_type libinput_device_buttonset_get_axis_type(struct libinput_device *device, unsigned int axis) { - return 0; /* FIXME */ + return evdev_device_buttonset_get_axis_type( + (struct evdev_device *)device, + axis); } LIBINPUT_EXPORT unsigned int libinput_device_buttonset_get_num_axes(struct libinput_device *device) { - return 0; /* FIXME */ + return evdev_device_buttonset_get_num_axes( + (struct evdev_device *)device); } LIBINPUT_EXPORT struct libinput_event * -- 2.5.0 _______________________________________________ wayland-devel mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/wayland-devel
