From 61e58a4c1f78145d9b29ab0ed822c74051b545a2 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Feb 2016 12:29:55 +1000 Subject: [PATCH 1/6] tablet: move the libwacom check for left-handed-ness into a helper function Signed-off-by: Peter Hutterer Reviewed-by: Jason Gerecke Reviewed-by: Carlos Garnacho --- src/evdev-tablet.c | 38 +-------------------------------- src/evdev.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++ src/evdev.h | 3 +++ 3 files changed, 56 insertions(+), 37 deletions(-) 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 e44dfd18..330ca6b9 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) @@ -2876,3 +2880,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 0e08f6d1..56f07dba 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -456,6 +456,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) From b2a370694881b849c4b3ea59b53a0a9cb0f44ea0 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 21 Jan 2016 12:35:11 +1000 Subject: [PATCH 2/6] Add the LIBINPUT_DEVICE_CAP_TABLET_PAD capability and matching interface This interface handles the buttons on the physical tablet itself, including the touch ring and the strip. A notable difference to other libinput interfaces here is that we do not use linux/input.h event codes for buttons. Instead, the buttons are merely numbered sequentially, starting at button 1. This means: * the API is different, instead of get_button() we have get_button_number() to drive the point home * there is no seat button count. pads are inherently different devices and compositors should treat them as such. The seat button count makes sense when you want to know how many devices have BTN_LEFT down, but it makes no sense for buttons where all the semantics are handled by the compositor anyway. Signed-off-by: Peter Hutterer Reviewed-by: Jason Gerecke Reviewed-by: Carlos Garnacho --- doc/tablet-support.dox | 46 +++++- src/libinput-private.h | 18 +++ src/libinput.c | 257 +++++++++++++++++++++++++++++++++ src/libinput.h | 313 ++++++++++++++++++++++++++++++++++++++++- src/libinput.sym | 19 +++ test/litest.c | 9 ++ tools/event-debug.c | 81 +++++++++++ tools/event-gui.c | 4 + 8 files changed, 740 insertions(+), 7 deletions(-) diff --git a/doc/tablet-support.dox b/doc/tablet-support.dox index 35b92303..07de478b 100644 --- a/doc/tablet-support.dox +++ b/doc/tablet-support.dox @@ -8,15 +8,21 @@ 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. + 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 +31,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 +119,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 +217,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/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..bd97fb0e 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 0; +} + +LIBINPUT_EXPORT int +libinput_device_tablet_pad_get_num_rings(struct libinput_device *device) +{ + return 0; +} + +LIBINPUT_EXPORT int +libinput_device_tablet_pad_get_num_strips(struct libinput_device *device) +{ + return 0; +} + 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..0fe89983 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -234,3 +234,22 @@ LIBINPUT_1.2 { libinput_tablet_tool_set_user_data; libinput_tablet_tool_unref; } LIBINPUT_1.1; + +TABLET_PAD { + 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/litest.c b/test/litest.c index 67ff9375..70b4bd37 100644 --- a/test/litest.c +++ b/test/litest.c @@ -1963,6 +1963,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; } 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); From c2c526a7c5287cf2c0beeff0cfbce66c51d79ce8 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 10 Feb 2016 14:13:10 +1000 Subject: [PATCH 3/6] doc: add a graphic illustrating pad vs tool support Signed-off-by: Peter Hutterer Reviewed-by: Jason Gerecke Reviewed-by: Carlos Garnacho --- doc/svg/tablet-interfaces.svg | 325 ++++++++++++++++++++++++++++++++++ doc/tablet-support.dox | 2 + 2 files changed, 327 insertions(+) create mode 100644 doc/svg/tablet-interfaces.svg 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 07de478b..0a44010b 100644 --- a/doc/tablet-support.dox +++ b/doc/tablet-support.dox @@ -23,6 +23,8 @@ 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 From 8e17a9ab5c293306ff0bc0b63551e6a79fd497b1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 5 Feb 2016 15:16:38 +1000 Subject: [PATCH 4/6] pad: implement wacom pad support Signed-off-by: Peter Hutterer Reviewed-by: Jason Gerecke Reviewed-by: Carlos Garnacho --- src/Makefile.am | 1 + src/evdev-tablet-pad.c | 620 +++++++++++++++++++++++++++++++++++++++++ src/evdev-tablet-pad.h | 69 +++++ src/evdev.c | 28 +- src/evdev.h | 13 + src/libinput.c | 6 +- 6 files changed, 722 insertions(+), 15 deletions(-) create mode 100644 src/evdev-tablet-pad.c create mode 100644 src/evdev-tablet-pad.h diff --git a/src/Makefile.am b/src/Makefile.am index 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.c b/src/evdev.c index 330ca6b9..7a28bf21 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -65,7 +65,7 @@ enum evdev_device_udev_tags { EVDEV_UDEV_TAG_TABLET = (1 << 5), EVDEV_UDEV_TAG_JOYSTICK = (1 << 6), EVDEV_UDEV_TAG_ACCELEROMETER = (1 << 7), - EVDEV_UDEV_TAG_BUTTONSET = (1 << 8), + EVDEV_UDEV_TAG_TABLET_PAD = (1 << 8), EVDEV_UDEV_TAG_POINTINGSTICK = (1 << 9), }; @@ -82,7 +82,7 @@ static const struct evdev_udev_tag_match evdev_udev_tag_matches[] = { {"ID_INPUT_TOUCHPAD", EVDEV_UDEV_TAG_TOUCHPAD}, {"ID_INPUT_TOUCHSCREEN", EVDEV_UDEV_TAG_TOUCHSCREEN}, {"ID_INPUT_TABLET", EVDEV_UDEV_TAG_TABLET}, - {"ID_INPUT_TABLET_PAD", EVDEV_UDEV_TAG_BUTTONSET}, + {"ID_INPUT_TABLET_PAD", EVDEV_UDEV_TAG_TABLET_PAD}, {"ID_INPUT_JOYSTICK", EVDEV_UDEV_TAG_JOYSTICK}, {"ID_INPUT_ACCELEROMETER", EVDEV_UDEV_TAG_ACCELEROMETER}, {"ID_INPUT_POINTINGSTICK", EVDEV_UDEV_TAG_POINTINGSTICK}, @@ -2049,7 +2049,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, @@ -2067,14 +2067,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", @@ -2110,7 +2102,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, @@ -2539,6 +2541,8 @@ evdev_device_has_capability(struct evdev_device *device, return !!(device->seat_caps & EVDEV_DEVICE_GESTURE); case LIBINPUT_DEVICE_CAP_TABLET_TOOL: return !!(device->seat_caps & EVDEV_DEVICE_TABLET); + case LIBINPUT_DEVICE_CAP_TABLET_PAD: + return !!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD); default: return 0; } diff --git a/src/evdev.h b/src/evdev.h index 56f07dba..3ed077b4 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), }; @@ -319,6 +320,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); @@ -369,6 +373,15 @@ evdev_device_has_button(struct evdev_device *device, uint32_t code); int evdev_device_has_key(struct evdev_device *device, uint32_t code); +int +evdev_device_tablet_pad_get_num_buttons(struct evdev_device *device); + +int +evdev_device_tablet_pad_get_num_rings(struct evdev_device *device); + +int +evdev_device_tablet_pad_get_num_strips(struct evdev_device *device); + double evdev_device_transform_x(struct evdev_device *device, double x, diff --git a/src/libinput.c b/src/libinput.c index bd97fb0e..bcd0dcd0 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -2794,19 +2794,19 @@ libinput_device_keyboard_has_key(struct libinput_device *device, uint32_t code) LIBINPUT_EXPORT int libinput_device_tablet_pad_get_num_buttons(struct libinput_device *device) { - return 0; + return evdev_device_tablet_pad_get_num_buttons((struct evdev_device *)device); } LIBINPUT_EXPORT int libinput_device_tablet_pad_get_num_rings(struct libinput_device *device) { - return 0; + return evdev_device_tablet_pad_get_num_rings((struct evdev_device *)device); } LIBINPUT_EXPORT int libinput_device_tablet_pad_get_num_strips(struct libinput_device *device) { - return 0; + return evdev_device_tablet_pad_get_num_strips((struct evdev_device *)device); } LIBINPUT_EXPORT struct libinput_event * From d82cfa1d53676cd52bb0ef4adfa14dd881550047 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 5 Feb 2016 10:57:37 +1000 Subject: [PATCH 5/6] test: tablet pad tests Signed-off-by: Peter Hutterer Reviewed-by: Jason Gerecke Reviewed-by: Carlos Garnacho --- test/Makefile.am | 7 + test/litest-device-wacom-intuos3-pad.c | 117 +++++++ test/litest-device-wacom-intuos5-pad.c | 122 +++++++ test/litest-int.h | 16 + test/litest.c | 219 ++++++++++++- test/litest.h | 40 +++ test/misc.c | 51 +++ test/pad.c | 431 +++++++++++++++++++++++++ 8 files changed, 1002 insertions(+), 1 deletion(-) create mode 100644 test/litest-device-wacom-intuos3-pad.c create mode 100644 test/litest-device-wacom-intuos5-pad.c create mode 100644 test/pad.c diff --git a/test/Makefile.am b/test/Makefile.am index d328c927..ba78b965 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -48,6 +48,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 \ @@ -71,6 +73,7 @@ run_tests = \ test-touchpad \ test-touchpad-tap \ test-touchpad-buttons \ + test-pad \ test-tablet \ test-device \ test-gestures \ @@ -120,6 +123,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 70b4bd37..b10c42e3 100644 --- a/test/litest.c +++ b/test/litest.c @@ -379,6 +379,8 @@ extern struct litest_test_device litest_cyborg_rat_device; 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_wacom_intuos3_pad_device; +extern struct litest_test_device litest_wacom_intuos5_pad_device; struct litest_test_device* devices[] = { &litest_synaptics_clickpad_device, @@ -424,6 +426,8 @@ struct litest_test_device* devices[] = { &litest_yubikey_device, &litest_synaptics_i2c_device, &litest_wacom_cintiq_24hd_device, + &litest_wacom_intuos3_pad_device, + &litest_wacom_intuos5_pad_device, NULL, }; @@ -1809,6 +1813,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) { @@ -1819,12 +1832,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) { @@ -1981,6 +2102,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; @@ -2042,6 +2164,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; } @@ -2463,6 +2605,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 c218361e..81ce44d8 100644 --- a/test/litest.h +++ b/test/litest.h @@ -196,6 +196,8 @@ enum litest_device_type { LITEST_YUBIKEY = -42, LITEST_SYNAPTICS_I2C = -43, LITEST_WACOM_CINTIQ_24HD = -44, + LITEST_WACOM_INTUOS3_PAD = -45, + LITEST_WACOM_INTUOS5_PAD = -46, }; enum litest_device_feature { @@ -222,6 +224,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 { @@ -439,6 +444,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, @@ -525,6 +548,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, @@ -548,6 +584,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 */ +} From 83771f1b17f451618997395e4dc63a8c684fde38 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 18 Apr 2016 13:23:39 +1000 Subject: [PATCH 6/6] fixup! Add the LIBINPUT_DEVICE_CAP_TABLET_PAD capability and matching interface --- src/libinput.sym | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libinput.sym b/src/libinput.sym index 0fe89983..6be2940a 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -251,5 +251,4 @@ TABLET_PAD { libinput_event_tablet_pad_get_strip_source; libinput_event_tablet_pad_get_time; libinput_event_tablet_pad_get_time_usec; - } LIBINPUT_1.2;