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 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pad buttons + + Pad buttons + Pad buttons + Pad ring + + + + Pad ring + Tool buttons + + Tool tip + + + 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);