2016-02-05 15:16:38 +10:00
|
|
|
/*
|
|
|
|
|
* Copyright © 2016 Red Hat, Inc.
|
|
|
|
|
*
|
2018-08-07 08:26:53 +10:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
|
|
|
* to deal in the Software without restriction, including without limitation
|
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
|
* Software is furnished to do so, subject to the following conditions:
|
2016-02-05 15:16:38 +10:00
|
|
|
*
|
2018-08-07 08:26:53 +10:00
|
|
|
* The above copyright notice and this permission notice (including the next
|
|
|
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
|
|
|
* Software.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
|
* DEALINGS IN THE SOFTWARE.
|
2016-02-05 15:16:38 +10:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
2023-08-28 09:06:49 +10:00
|
|
|
#include "util-input-event.h"
|
2025-07-01 16:30:11 +10:00
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
#include "evdev-tablet-pad.h"
|
2025-07-01 16:30:11 +10:00
|
|
|
|
2025-07-21 13:23:29 +10:00
|
|
|
#ifdef HAVE_LIBWACOM
|
2017-10-18 13:00:16 +10:00
|
|
|
#include <libwacom/libwacom.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
#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, evdev_usage_t button, bool is_down)
|
|
|
|
|
{
|
|
|
|
|
struct button_state *state = &pad->button_state;
|
2025-06-05 19:44:10 +10:00
|
|
|
unsigned int code = evdev_usage_code(button);
|
2016-02-05 15:16:38 +10:00
|
|
|
|
|
|
|
|
if (is_down) {
|
2025-06-05 19:44:10 +10:00
|
|
|
set_bit(state->bits, code);
|
2016-02-05 15:16:38 +10:00
|
|
|
pad_set_status(pad, PAD_BUTTONS_PRESSED);
|
|
|
|
|
} else {
|
2025-06-05 19:44:10 +10:00
|
|
|
clear_bit(state->bits, code);
|
2016-02-05 15:16:38 +10:00
|
|
|
pad_set_status(pad, PAD_BUTTONS_RELEASED);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-30 14:43:59 +10:00
|
|
|
static void
|
|
|
|
|
pad_process_relative(struct pad_dispatch *pad,
|
|
|
|
|
struct evdev_device *device,
|
2025-06-05 19:44:10 +10:00
|
|
|
struct evdev_event *e,
|
2025-12-01 09:17:37 +10:00
|
|
|
usec_t time)
|
2024-01-30 14:43:59 +10:00
|
|
|
{
|
2025-06-05 19:44:10 +10:00
|
|
|
switch (evdev_usage_enum(e->usage)) {
|
|
|
|
|
case EVDEV_REL_DIAL:
|
2024-01-30 14:43:59 +10:00
|
|
|
pad->dials.dial1 = e->value * 120;
|
|
|
|
|
pad->changed_axes |= PAD_AXIS_DIAL1;
|
|
|
|
|
pad_set_status(pad, PAD_AXES_UPDATED);
|
|
|
|
|
break;
|
2025-06-05 19:44:10 +10:00
|
|
|
case EVDEV_REL_WHEEL:
|
2024-01-30 14:43:59 +10:00
|
|
|
if (!pad->dials.has_hires_dial) {
|
2024-06-21 08:39:10 +10:00
|
|
|
pad->dials.dial1 = -1 * e->value * 120;
|
2024-01-30 14:43:59 +10:00
|
|
|
pad->changed_axes |= PAD_AXIS_DIAL1;
|
|
|
|
|
pad_set_status(pad, PAD_AXES_UPDATED);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2025-06-05 19:44:10 +10:00
|
|
|
case EVDEV_REL_HWHEEL:
|
2024-01-30 14:43:59 +10:00
|
|
|
if (!pad->dials.has_hires_dial) {
|
|
|
|
|
pad->dials.dial2 = e->value * 120;
|
|
|
|
|
pad->changed_axes |= PAD_AXIS_DIAL2;
|
|
|
|
|
pad_set_status(pad, PAD_AXES_UPDATED);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2025-06-05 19:44:10 +10:00
|
|
|
case EVDEV_REL_WHEEL_HI_RES:
|
2024-06-21 08:39:10 +10:00
|
|
|
pad->dials.dial1 = -1 * e->value;
|
2024-01-30 14:43:59 +10:00
|
|
|
pad->changed_axes |= PAD_AXIS_DIAL1;
|
|
|
|
|
pad_set_status(pad, PAD_AXES_UPDATED);
|
|
|
|
|
break;
|
2025-06-05 19:44:10 +10:00
|
|
|
case EVDEV_REL_HWHEEL_HI_RES:
|
2024-07-29 22:46:57 +00:00
|
|
|
pad->dials.dial2 = e->value;
|
2024-01-30 14:43:59 +10:00
|
|
|
pad->changed_axes |= PAD_AXIS_DIAL2;
|
|
|
|
|
pad_set_status(pad, PAD_AXES_UPDATED);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
evdev_log_info(device,
|
|
|
|
|
"Unhandled EV_REL event code %#x\n",
|
2025-06-05 19:44:10 +10:00
|
|
|
evdev_usage_as_uint32_t(e->usage));
|
2024-01-30 14:43:59 +10:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
pad: be more robust to kernel bugs of multiple EV_ABS
This is a workaround for a kernel bug with the Wacom Mobile Studio Pro
13: this device sends erroneous ABS_WHEEL events when pressing
buttons:
- evdev:
- [ 0, 0, 1, 256, 1] # EV_KEY / BTN_0 1
- [ 0, 0, 3, 8, 17] # EV_ABS / ABS_WHEEL 17 (+17)
- [ 0, 0, 3, 8, 0] # EV_ABS / ABS_WHEEL 0 (-17)
- [ 0, 0, 3, 40, 15] # EV_ABS / ABS_MISC 15 (+15)
- [ 0, 0, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +0ms
ABS_WHEEL to 17 then to 0 in the same frame. We should (and do) treat this
as a zero event and drop the 17 to the floor. Alas, we then generate a
ring event for the zero value despite our current value being zero
anyway. This again causes confusing behavior.
This is simple enough to work around, at least until the kernel is fixed
but also to prevent this happening in the future: warn if we get
multiple events and if so and the later event is zero, undo the axis
change state.
Closes: #1081
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1136>
2025-02-06 13:04:27 +10:00
|
|
|
static void
|
|
|
|
|
pad_update_changed_axis(struct pad_dispatch *pad,
|
|
|
|
|
enum pad_axes axis,
|
2025-06-05 19:44:10 +10:00
|
|
|
const struct evdev_event *e)
|
pad: be more robust to kernel bugs of multiple EV_ABS
This is a workaround for a kernel bug with the Wacom Mobile Studio Pro
13: this device sends erroneous ABS_WHEEL events when pressing
buttons:
- evdev:
- [ 0, 0, 1, 256, 1] # EV_KEY / BTN_0 1
- [ 0, 0, 3, 8, 17] # EV_ABS / ABS_WHEEL 17 (+17)
- [ 0, 0, 3, 8, 0] # EV_ABS / ABS_WHEEL 0 (-17)
- [ 0, 0, 3, 40, 15] # EV_ABS / ABS_MISC 15 (+15)
- [ 0, 0, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +0ms
ABS_WHEEL to 17 then to 0 in the same frame. We should (and do) treat this
as a zero event and drop the 17 to the floor. Alas, we then generate a
ring event for the zero value despite our current value being zero
anyway. This again causes confusing behavior.
This is simple enough to work around, at least until the kernel is fixed
but also to prevent this happening in the future: warn if we get
multiple events and if so and the later event is zero, undo the axis
change state.
Closes: #1081
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1136>
2025-02-06 13:04:27 +10:00
|
|
|
{
|
|
|
|
|
if (pad->changed_axes & axis) {
|
|
|
|
|
evdev_log_bug_kernel_ratelimit(
|
|
|
|
|
pad->device,
|
|
|
|
|
&pad->duplicate_abs_limit,
|
|
|
|
|
"Multiple EV_ABS %s events in the same SYN_REPORT\n",
|
2025-06-05 19:44:10 +10:00
|
|
|
evdev_event_get_code_name(e));
|
pad: be more robust to kernel bugs of multiple EV_ABS
This is a workaround for a kernel bug with the Wacom Mobile Studio Pro
13: this device sends erroneous ABS_WHEEL events when pressing
buttons:
- evdev:
- [ 0, 0, 1, 256, 1] # EV_KEY / BTN_0 1
- [ 0, 0, 3, 8, 17] # EV_ABS / ABS_WHEEL 17 (+17)
- [ 0, 0, 3, 8, 0] # EV_ABS / ABS_WHEEL 0 (-17)
- [ 0, 0, 3, 40, 15] # EV_ABS / ABS_MISC 15 (+15)
- [ 0, 0, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +0ms
ABS_WHEEL to 17 then to 0 in the same frame. We should (and do) treat this
as a zero event and drop the 17 to the floor. Alas, we then generate a
ring event for the zero value despite our current value being zero
anyway. This again causes confusing behavior.
This is simple enough to work around, at least until the kernel is fixed
but also to prevent this happening in the future: warn if we get
multiple events and if so and the later event is zero, undo the axis
change state.
Closes: #1081
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1136>
2025-02-06 13:04:27 +10:00
|
|
|
|
|
|
|
|
/* Special heuristics probably good enough:
|
|
|
|
|
* if we get multiple EV_ABS in the same SYN_REPORT
|
|
|
|
|
* and one of them is zero, assume they're all
|
|
|
|
|
* zero and unchanged. That's not perfectly
|
|
|
|
|
* correct but probably covers all cases */
|
|
|
|
|
if (e->value == 0) {
|
|
|
|
|
pad->changed_axes &= ~axis;
|
|
|
|
|
if (pad->changed_axes == 0)
|
|
|
|
|
pad_unset_status(pad, PAD_AXES_UPDATED);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pad->changed_axes |= axis;
|
|
|
|
|
pad_set_status(pad, PAD_AXES_UPDATED);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
static void
|
|
|
|
|
pad_process_absolute(struct pad_dispatch *pad,
|
|
|
|
|
struct evdev_device *device,
|
2025-06-05 19:44:10 +10:00
|
|
|
struct evdev_event *e,
|
2025-12-01 09:17:37 +10:00
|
|
|
usec_t time)
|
2016-02-05 15:16:38 +10:00
|
|
|
{
|
pad: be more robust to kernel bugs of multiple EV_ABS
This is a workaround for a kernel bug with the Wacom Mobile Studio Pro
13: this device sends erroneous ABS_WHEEL events when pressing
buttons:
- evdev:
- [ 0, 0, 1, 256, 1] # EV_KEY / BTN_0 1
- [ 0, 0, 3, 8, 17] # EV_ABS / ABS_WHEEL 17 (+17)
- [ 0, 0, 3, 8, 0] # EV_ABS / ABS_WHEEL 0 (-17)
- [ 0, 0, 3, 40, 15] # EV_ABS / ABS_MISC 15 (+15)
- [ 0, 0, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +0ms
ABS_WHEEL to 17 then to 0 in the same frame. We should (and do) treat this
as a zero event and drop the 17 to the floor. Alas, we then generate a
ring event for the zero value despite our current value being zero
anyway. This again causes confusing behavior.
This is simple enough to work around, at least until the kernel is fixed
but also to prevent this happening in the future: warn if we get
multiple events and if so and the later event is zero, undo the axis
change state.
Closes: #1081
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1136>
2025-02-06 13:04:27 +10:00
|
|
|
enum pad_axes axis = PAD_AXIS_NONE;
|
|
|
|
|
|
2025-06-05 19:44:10 +10:00
|
|
|
switch (evdev_usage_enum(e->usage)) {
|
|
|
|
|
case EVDEV_ABS_WHEEL:
|
|
|
|
|
axis = PAD_AXIS_RING1;
|
|
|
|
|
break;
|
|
|
|
|
case EVDEV_ABS_THROTTLE:
|
|
|
|
|
axis = PAD_AXIS_RING2;
|
|
|
|
|
break;
|
|
|
|
|
case EVDEV_ABS_RX:
|
|
|
|
|
axis = PAD_AXIS_STRIP1;
|
|
|
|
|
break;
|
|
|
|
|
case EVDEV_ABS_RY:
|
|
|
|
|
axis = PAD_AXIS_STRIP2;
|
|
|
|
|
break;
|
|
|
|
|
case EVDEV_ABS_MISC:
|
2016-02-05 15:16:38 +10:00
|
|
|
/* 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:
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_info(device,
|
|
|
|
|
"Unhandled EV_ABS event code %#x\n",
|
2025-06-05 19:44:10 +10:00
|
|
|
evdev_usage_as_uint32_t(e->usage));
|
2016-02-05 15:16:38 +10:00
|
|
|
break;
|
|
|
|
|
}
|
pad: be more robust to kernel bugs of multiple EV_ABS
This is a workaround for a kernel bug with the Wacom Mobile Studio Pro
13: this device sends erroneous ABS_WHEEL events when pressing
buttons:
- evdev:
- [ 0, 0, 1, 256, 1] # EV_KEY / BTN_0 1
- [ 0, 0, 3, 8, 17] # EV_ABS / ABS_WHEEL 17 (+17)
- [ 0, 0, 3, 8, 0] # EV_ABS / ABS_WHEEL 0 (-17)
- [ 0, 0, 3, 40, 15] # EV_ABS / ABS_MISC 15 (+15)
- [ 0, 0, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +0ms
ABS_WHEEL to 17 then to 0 in the same frame. We should (and do) treat this
as a zero event and drop the 17 to the floor. Alas, we then generate a
ring event for the zero value despite our current value being zero
anyway. This again causes confusing behavior.
This is simple enough to work around, at least until the kernel is fixed
but also to prevent this happening in the future: warn if we get
multiple events and if so and the later event is zero, undo the axis
change state.
Closes: #1081
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1136>
2025-02-06 13:04:27 +10:00
|
|
|
|
|
|
|
|
if (axis != PAD_AXIS_NONE) {
|
|
|
|
|
pad_update_changed_axis(pad, axis, e);
|
|
|
|
|
}
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline double
|
2024-04-12 10:43:45 +10:00
|
|
|
normalize_wacom_ring(const struct input_absinfo *absinfo)
|
2016-02-05 15:16:38 +10:00
|
|
|
{
|
|
|
|
|
/* 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.
|
|
|
|
|
*/
|
2023-08-28 09:06:49 +10:00
|
|
|
double range = absinfo_range(absinfo);
|
2016-02-05 15:16:38 +10:00
|
|
|
double value = (absinfo->value - absinfo->minimum) / range - 0.25;
|
|
|
|
|
|
|
|
|
|
if (value < 0.0)
|
|
|
|
|
value += 1.0;
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline double
|
2024-04-12 10:43:45 +10:00
|
|
|
normalize_wacom_strip(const struct input_absinfo *absinfo)
|
2016-02-05 15:16:38 +10:00
|
|
|
{
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-12 10:59:13 +10:00
|
|
|
static inline double
|
|
|
|
|
normalize_strip(const struct input_absinfo *absinfo)
|
|
|
|
|
{
|
|
|
|
|
return absinfo_normalize_value(absinfo, absinfo->value);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
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);
|
|
|
|
|
|
2024-04-12 10:43:45 +10:00
|
|
|
degrees = normalize_wacom_ring(absinfo) * 360;
|
2016-02-05 15:16:38 +10:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2024-04-12 10:59:13 +10:00
|
|
|
if (evdev_device_get_id_vendor(device) == VENDOR_ID_WACOM)
|
|
|
|
|
pos = normalize_wacom_strip(absinfo);
|
|
|
|
|
else
|
|
|
|
|
pos = normalize_strip(absinfo);
|
2016-02-05 15:16:38 +10:00
|
|
|
|
|
|
|
|
if (device->left_handed.enabled)
|
|
|
|
|
pos = 1.0 - pos;
|
|
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-30 14:43:59 +10:00
|
|
|
static inline struct libinput_tablet_pad_mode_group *
|
|
|
|
|
pad_dial_get_mode_group(struct pad_dispatch *pad, unsigned int dial)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_tablet_pad_mode_group *group;
|
|
|
|
|
|
|
|
|
|
list_for_each(group, &pad->modes.mode_group_list, link) {
|
2025-08-11 15:10:35 +10:00
|
|
|
assert(group != NULL); /* for clang-tidy */
|
2024-01-30 14:43:59 +10:00
|
|
|
if (libinput_tablet_pad_mode_group_has_dial(group, dial))
|
|
|
|
|
return group;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-04 12:53:10 +10:00
|
|
|
evdev_log_bug_libinput_ratelimit(pad->device,
|
|
|
|
|
&pad->modes.group_not_found,
|
2025-12-17 14:37:59 +10:00
|
|
|
"Unable to find mode group for dial %d\n",
|
2025-08-04 12:53:10 +10:00
|
|
|
dial);
|
2024-01-30 14:43:59 +10:00
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-02 15:35:43 +10:00
|
|
|
static inline struct libinput_tablet_pad_mode_group *
|
|
|
|
|
pad_ring_get_mode_group(struct pad_dispatch *pad, unsigned int ring)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_tablet_pad_mode_group *group;
|
|
|
|
|
|
|
|
|
|
list_for_each(group, &pad->modes.mode_group_list, link) {
|
2025-08-11 15:10:35 +10:00
|
|
|
assert(group != NULL); /* for clang-tidy */
|
2016-06-02 15:35:43 +10:00
|
|
|
if (libinput_tablet_pad_mode_group_has_ring(group, ring))
|
|
|
|
|
return group;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-04 12:53:10 +10:00
|
|
|
evdev_log_bug_libinput_ratelimit(pad->device,
|
|
|
|
|
&pad->modes.group_not_found,
|
2025-12-17 14:37:59 +10:00
|
|
|
"Unable to find mode group for ring %d\n",
|
2025-08-04 12:53:10 +10:00
|
|
|
ring);
|
2016-06-02 15:35:43 +10:00
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline struct libinput_tablet_pad_mode_group *
|
|
|
|
|
pad_strip_get_mode_group(struct pad_dispatch *pad, unsigned int strip)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_tablet_pad_mode_group *group;
|
|
|
|
|
|
|
|
|
|
list_for_each(group, &pad->modes.mode_group_list, link) {
|
2025-08-11 15:10:35 +10:00
|
|
|
assert(group != NULL); /* for clang-tidy */
|
2016-06-02 15:35:43 +10:00
|
|
|
if (libinput_tablet_pad_mode_group_has_strip(group, strip))
|
|
|
|
|
return group;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-04 12:53:10 +10:00
|
|
|
evdev_log_bug_libinput_ratelimit(pad->device,
|
|
|
|
|
&pad->modes.group_not_found,
|
2025-12-17 14:37:59 +10:00
|
|
|
"Unable to find mode group for strip %d\n",
|
2025-08-04 12:53:10 +10:00
|
|
|
strip);
|
2016-06-02 15:35:43 +10:00
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
static void
|
|
|
|
|
pad_check_notify_axes(struct pad_dispatch *pad,
|
|
|
|
|
struct evdev_device *device,
|
2025-12-01 09:17:37 +10:00
|
|
|
usec_t time)
|
2016-02-05 15:16:38 +10:00
|
|
|
{
|
|
|
|
|
struct libinput_device *base = &device->base;
|
2016-06-02 15:35:43 +10:00
|
|
|
struct libinput_tablet_pad_mode_group *group;
|
2016-02-05 15:16:38 +10:00
|
|
|
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;
|
|
|
|
|
|
2024-01-30 14:43:59 +10:00
|
|
|
/* Unlike the ring axis we don't get an event when we release
|
|
|
|
|
* so we can't set a source */
|
|
|
|
|
if (pad->changed_axes & PAD_AXIS_DIAL1) {
|
|
|
|
|
group = pad_dial_get_mode_group(pad, 0);
|
2025-08-04 12:53:10 +10:00
|
|
|
if (group)
|
|
|
|
|
tablet_pad_notify_dial(base, time, 0, pad->dials.dial1, group);
|
2024-01-30 14:43:59 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pad->changed_axes & PAD_AXIS_DIAL2) {
|
|
|
|
|
group = pad_dial_get_mode_group(pad, 1);
|
2025-08-04 12:53:10 +10:00
|
|
|
if (group)
|
|
|
|
|
tablet_pad_notify_dial(base, time, 1, pad->dials.dial2, group);
|
2024-01-30 14:43:59 +10:00
|
|
|
}
|
|
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
if (pad->changed_axes & PAD_AXIS_RING1) {
|
|
|
|
|
value = pad_handle_ring(pad, device, ABS_WHEEL);
|
|
|
|
|
if (send_finger_up)
|
|
|
|
|
value = -1.0;
|
|
|
|
|
|
2016-06-02 15:35:43 +10:00
|
|
|
group = pad_ring_get_mode_group(pad, 0);
|
2025-08-04 12:53:10 +10:00
|
|
|
if (group)
|
|
|
|
|
tablet_pad_notify_ring(base,
|
|
|
|
|
time,
|
|
|
|
|
0,
|
|
|
|
|
value,
|
|
|
|
|
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
|
|
|
|
|
group);
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pad->changed_axes & PAD_AXIS_RING2) {
|
|
|
|
|
value = pad_handle_ring(pad, device, ABS_THROTTLE);
|
|
|
|
|
if (send_finger_up)
|
|
|
|
|
value = -1.0;
|
|
|
|
|
|
2016-06-02 15:35:43 +10:00
|
|
|
group = pad_ring_get_mode_group(pad, 1);
|
2025-08-04 12:53:10 +10:00
|
|
|
if (group)
|
|
|
|
|
tablet_pad_notify_ring(base,
|
|
|
|
|
time,
|
|
|
|
|
1,
|
|
|
|
|
value,
|
|
|
|
|
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
|
|
|
|
|
group);
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pad->changed_axes & PAD_AXIS_STRIP1) {
|
|
|
|
|
value = pad_handle_strip(pad, device, ABS_RX);
|
|
|
|
|
if (send_finger_up)
|
|
|
|
|
value = -1.0;
|
|
|
|
|
|
2016-06-02 15:35:43 +10:00
|
|
|
group = pad_strip_get_mode_group(pad, 0);
|
2025-08-04 12:53:10 +10:00
|
|
|
if (group)
|
|
|
|
|
tablet_pad_notify_strip(base,
|
|
|
|
|
time,
|
|
|
|
|
0,
|
|
|
|
|
value,
|
|
|
|
|
LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
|
|
|
|
|
group);
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pad->changed_axes & PAD_AXIS_STRIP2) {
|
|
|
|
|
value = pad_handle_strip(pad, device, ABS_RY);
|
|
|
|
|
if (send_finger_up)
|
|
|
|
|
value = -1.0;
|
|
|
|
|
|
2016-06-02 15:35:43 +10:00
|
|
|
group = pad_strip_get_mode_group(pad, 1);
|
2025-08-04 12:53:10 +10:00
|
|
|
if (group)
|
|
|
|
|
tablet_pad_notify_strip(base,
|
|
|
|
|
time,
|
|
|
|
|
1,
|
|
|
|
|
value,
|
|
|
|
|
LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
|
|
|
|
|
group);
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
2025-06-05 19:44:10 +10:00
|
|
|
struct evdev_event *e,
|
2025-12-01 09:17:37 +10:00
|
|
|
usec_t time)
|
2016-02-05 15:16:38 +10:00
|
|
|
{
|
|
|
|
|
uint32_t is_press = e->value != 0;
|
|
|
|
|
|
2020-06-02 10:52:11 +10:00
|
|
|
/* ignore kernel key repeat */
|
|
|
|
|
if (e->value == 2)
|
|
|
|
|
return;
|
|
|
|
|
|
2025-06-05 19:44:10 +10:00
|
|
|
pad_button_set_down(pad, e->usage, is_press);
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
|
2016-06-02 15:35:43 +10:00
|
|
|
static inline struct libinput_tablet_pad_mode_group *
|
|
|
|
|
pad_button_get_mode_group(struct pad_dispatch *pad, unsigned int button)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_tablet_pad_mode_group *group;
|
|
|
|
|
|
|
|
|
|
list_for_each(group, &pad->modes.mode_group_list, link) {
|
|
|
|
|
if (libinput_tablet_pad_mode_group_has_button(group, button))
|
|
|
|
|
return group;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(!"Unable to find button mode group\n");
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
static void
|
|
|
|
|
pad_notify_button_mask(struct pad_dispatch *pad,
|
|
|
|
|
struct evdev_device *device,
|
2025-12-01 09:17:37 +10:00
|
|
|
usec_t time,
|
2016-02-05 15:16:38 +10:00
|
|
|
const struct button_state *buttons,
|
|
|
|
|
enum libinput_button_state state)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_device *base = &device->base;
|
2016-06-02 15:35:43 +10:00
|
|
|
struct libinput_tablet_pad_mode_group *group;
|
2016-02-05 15:16:38 +10:00
|
|
|
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;
|
2019-01-17 11:49:39 +10:00
|
|
|
key_or_button_map_t map;
|
2016-02-05 15:16:38 +10:00
|
|
|
|
|
|
|
|
code++;
|
|
|
|
|
enabled = (buttons_slice & 1);
|
|
|
|
|
buttons_slice >>= 1;
|
|
|
|
|
|
|
|
|
|
if (!enabled)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
map = pad->button_map[code - 1];
|
2019-01-17 11:49:39 +10:00
|
|
|
if (map_is_unmapped(map))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (map_is_button(map)) {
|
|
|
|
|
int32_t button = map_value(map);
|
|
|
|
|
|
|
|
|
|
group = pad_button_get_mode_group(pad, button);
|
|
|
|
|
pad_button_update_mode(group, button, state);
|
|
|
|
|
tablet_pad_notify_button(
|
|
|
|
|
base,
|
|
|
|
|
time,
|
2025-06-06 10:48:46 +10:00
|
|
|
pad_button_from_uint32_t(button),
|
2019-01-17 11:49:39 +10:00
|
|
|
state,
|
|
|
|
|
group);
|
2019-01-17 11:08:27 +10:00
|
|
|
} else if (map_is_key(map)) {
|
|
|
|
|
uint32_t key = map_value(map);
|
|
|
|
|
|
2020-03-21 21:13:14 +10:00
|
|
|
tablet_pad_notify_key(base,
|
|
|
|
|
time,
|
|
|
|
|
key,
|
|
|
|
|
(enum libinput_key_state)state);
|
2019-01-17 11:49:39 +10:00
|
|
|
} else {
|
|
|
|
|
abort();
|
2016-06-02 15:35:43 +10:00
|
|
|
}
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
pad_notify_buttons(struct pad_dispatch *pad,
|
|
|
|
|
struct evdev_device *device,
|
2025-12-01 09:17:37 +10:00
|
|
|
usec_t time,
|
2016-02-05 15:16:38 +10:00
|
|
|
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
|
2025-12-01 09:17:37 +10:00
|
|
|
pad_flush(struct pad_dispatch *pad, struct evdev_device *device, usec_t time)
|
2016-02-05 15:16:38 +10:00
|
|
|
{
|
|
|
|
|
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));
|
2024-01-30 14:43:59 +10:00
|
|
|
pad->dials.dial1 = 0;
|
|
|
|
|
pad->dials.dial2 = 0;
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2025-06-23 15:06:36 +10:00
|
|
|
pad_process_event(struct evdev_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
struct evdev_event *e,
|
2025-12-01 09:17:37 +10:00
|
|
|
usec_t time)
|
2016-02-05 15:16:38 +10:00
|
|
|
{
|
2017-01-30 18:01:09 +10:00
|
|
|
struct pad_dispatch *pad = pad_dispatch(dispatch);
|
2016-02-05 15:16:38 +10:00
|
|
|
|
2025-06-05 19:39:15 +10:00
|
|
|
uint16_t type = evdev_event_type(e);
|
2025-06-05 19:44:10 +10:00
|
|
|
switch (type) {
|
2024-01-30 14:43:59 +10:00
|
|
|
case EV_REL:
|
2025-06-05 19:39:15 +10:00
|
|
|
pad_process_relative(pad, device, e, time);
|
2024-01-30 14:43:59 +10:00
|
|
|
break;
|
2016-02-05 15:16:38 +10:00
|
|
|
case EV_ABS:
|
2025-06-05 19:39:15 +10:00
|
|
|
pad_process_absolute(pad, device, e, time);
|
2016-02-05 15:16:38 +10:00
|
|
|
break;
|
|
|
|
|
case EV_KEY:
|
2025-06-05 19:39:15 +10:00
|
|
|
pad_process_key(pad, device, e, time);
|
2016-02-05 15:16:38 +10:00
|
|
|
break;
|
|
|
|
|
case EV_SYN:
|
|
|
|
|
pad_flush(pad, device, time);
|
|
|
|
|
break;
|
2016-05-26 08:40:17 +10:00
|
|
|
case EV_MSC:
|
|
|
|
|
/* The EKR sends the serial as MSC_SERIAL, ignore this for
|
|
|
|
|
* now */
|
|
|
|
|
break;
|
2016-02-05 15:16:38 +10:00
|
|
|
default:
|
2017-02-13 14:17:52 +10:00
|
|
|
evdev_log_error(device,
|
|
|
|
|
"Unexpected event type %s (%#x)\n",
|
2025-06-05 19:44:10 +10:00
|
|
|
libevdev_event_type_get_name(type),
|
2025-06-05 19:39:15 +10:00
|
|
|
evdev_usage_as_uint32_t(e->usage));
|
2016-02-05 15:16:38 +10:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-23 15:06:36 +10:00
|
|
|
static void
|
|
|
|
|
pad_process(struct evdev_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
struct evdev_frame *frame,
|
2025-12-01 09:17:37 +10:00
|
|
|
usec_t time)
|
2025-06-23 15:06:36 +10:00
|
|
|
{
|
|
|
|
|
size_t nevents;
|
|
|
|
|
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < nevents; i++) {
|
|
|
|
|
pad_process_event(dispatch, device, &events[i], time);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
static void
|
|
|
|
|
pad_suspend(struct evdev_dispatch *dispatch, struct evdev_device *device)
|
|
|
|
|
{
|
2017-01-30 18:01:09 +10:00
|
|
|
struct pad_dispatch *pad = pad_dispatch(dispatch);
|
2016-06-09 11:23:43 +10:00
|
|
|
struct libinput *libinput = pad_libinput_context(pad);
|
2016-02-05 15:16:38 +10:00
|
|
|
|
2025-06-06 11:58:53 +10:00
|
|
|
for (evdev_usage_t usage = evdev_usage_from(EVDEV_KEY_ESC);
|
|
|
|
|
evdev_usage_le(usage, EVDEV_KEY_MAX);
|
|
|
|
|
usage = evdev_usage_next(usage)) {
|
|
|
|
|
if (pad_button_is_down(pad, evdev_usage_code(usage)))
|
2025-06-05 19:44:10 +10:00
|
|
|
pad_button_set_down(pad, usage, false);
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pad_flush(pad, device, libinput_now(libinput));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
pad_destroy(struct evdev_dispatch *dispatch)
|
|
|
|
|
{
|
2017-01-30 18:01:09 +10:00
|
|
|
struct pad_dispatch *pad = pad_dispatch(dispatch);
|
2016-02-05 15:16:38 +10:00
|
|
|
|
2016-06-02 15:35:43 +10:00
|
|
|
pad_destroy_leds(pad);
|
2016-02-05 15:16:38 +10:00
|
|
|
free(pad);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct evdev_dispatch_interface pad_interface = {
|
2017-09-19 13:28:58 +10:00
|
|
|
.process = pad_process,
|
|
|
|
|
.suspend = pad_suspend,
|
|
|
|
|
.remove = NULL,
|
|
|
|
|
.destroy = pad_destroy,
|
|
|
|
|
.device_added = NULL,
|
|
|
|
|
.device_removed = NULL,
|
|
|
|
|
.device_suspended = NULL,
|
|
|
|
|
.device_resumed = NULL,
|
|
|
|
|
.post_added = NULL,
|
2018-09-19 12:02:51 +10:00
|
|
|
.touch_arbitration_toggle = NULL,
|
|
|
|
|
.touch_arbitration_update_rect = NULL,
|
2017-09-19 13:45:22 +10:00
|
|
|
.get_switch_state = NULL,
|
2016-02-05 15:16:38 +10:00
|
|
|
};
|
|
|
|
|
|
2017-10-18 13:00:16 +10:00
|
|
|
static bool
|
|
|
|
|
pad_init_buttons_from_libwacom(struct pad_dispatch *pad,
|
2024-10-29 09:46:05 +10:00
|
|
|
struct evdev_device *device,
|
|
|
|
|
WacomDevice *tablet)
|
2017-10-18 13:00:16 +10:00
|
|
|
{
|
|
|
|
|
bool rc = false;
|
2025-07-21 13:23:29 +10:00
|
|
|
#ifdef HAVE_LIBWACOM
|
2022-02-05 09:08:35 +01:00
|
|
|
|
2024-10-29 09:46:05 +10:00
|
|
|
if (tablet) {
|
|
|
|
|
int num_buttons = libwacom_get_num_buttons(tablet);
|
|
|
|
|
int map = 0;
|
|
|
|
|
for (int i = 0; i < num_buttons; i++) {
|
|
|
|
|
unsigned int code;
|
2017-10-18 13:00:16 +10:00
|
|
|
|
2024-10-29 09:46:05 +10:00
|
|
|
code = libwacom_get_button_evdev_code(tablet, 'A' + i);
|
|
|
|
|
if (code == 0)
|
|
|
|
|
continue;
|
2017-10-18 13:00:16 +10:00
|
|
|
|
2024-10-29 09:46:05 +10:00
|
|
|
map_set_button_map(pad->button_map[code], map++);
|
|
|
|
|
}
|
2017-10-18 13:00:16 +10:00
|
|
|
|
2024-10-29 09:46:05 +10:00
|
|
|
pad->nbuttons = map;
|
|
|
|
|
rc = true;
|
2017-10-18 13:00:16 +10:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
static void
|
2017-10-18 13:00:16 +10:00
|
|
|
pad_init_buttons_from_kernel(struct pad_dispatch *pad, struct evdev_device *device)
|
2016-02-05 15:16:38 +10:00
|
|
|
{
|
|
|
|
|
unsigned int code;
|
|
|
|
|
int map = 0;
|
|
|
|
|
|
|
|
|
|
/* 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))
|
2019-01-17 11:49:39 +10:00
|
|
|
map_set_button_map(pad->button_map[code], map++);
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
|
2016-06-08 11:22:17 +10:00
|
|
|
for (code = BTN_BASE; code < BTN_BASE + 2; code++) {
|
2016-02-05 15:16:38 +10:00
|
|
|
if (libevdev_has_event_code(device->evdev, EV_KEY, code))
|
2019-01-17 11:49:39 +10:00
|
|
|
map_set_button_map(pad->button_map[code], map++);
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
|
2016-06-08 11:22:17 +10:00
|
|
|
for (code = BTN_A; code < BTN_A + 6; code++) {
|
2016-02-05 15:16:38 +10:00
|
|
|
if (libevdev_has_event_code(device->evdev, EV_KEY, code))
|
2019-01-17 11:49:39 +10:00
|
|
|
map_set_button_map(pad->button_map[code], map++);
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
|
2017-02-13 16:57:07 +10:00
|
|
|
for (code = BTN_LEFT; code < BTN_LEFT + 7; code++) {
|
|
|
|
|
if (libevdev_has_event_code(device->evdev, EV_KEY, code))
|
2019-01-17 11:49:39 +10:00
|
|
|
map_set_button_map(pad->button_map[code], map++);
|
2017-02-13 16:57:07 +10:00
|
|
|
}
|
|
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
pad->nbuttons = map;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-17 11:08:27 +10:00
|
|
|
static void
|
|
|
|
|
pad_init_keys(struct pad_dispatch *pad, struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
unsigned int codes[] = {
|
|
|
|
|
KEY_BUTTONCONFIG,
|
|
|
|
|
KEY_ONSCREEN_KEYBOARD,
|
|
|
|
|
KEY_CONTROLPANEL,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Wacom's keys are the only ones we know anything about */
|
|
|
|
|
if (libevdev_get_id_vendor(device->evdev) != VENDOR_ID_WACOM)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ARRAY_FOR_EACH(codes, code) {
|
|
|
|
|
if (libevdev_has_event_code(device->evdev, EV_KEY, *code))
|
|
|
|
|
map_set_key_map(pad->button_map[*code], *code);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-18 13:00:16 +10:00
|
|
|
static void
|
|
|
|
|
pad_init_buttons(struct pad_dispatch *pad,
|
2024-10-29 09:46:05 +10:00
|
|
|
struct evdev_device *device,
|
|
|
|
|
WacomDevice *wacom)
|
2017-10-18 13:00:16 +10:00
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_LENGTH(pad->button_map); i++)
|
2019-01-17 11:49:39 +10:00
|
|
|
map_init(pad->button_map[i]);
|
2017-10-18 13:00:16 +10:00
|
|
|
|
2024-10-29 09:46:05 +10:00
|
|
|
if (!pad_init_buttons_from_libwacom(pad, device, wacom))
|
2017-10-18 13:00:16 +10:00
|
|
|
pad_init_buttons_from_kernel(pad, device);
|
|
|
|
|
|
2019-01-17 11:08:27 +10:00
|
|
|
pad_init_keys(pad, device);
|
2017-10-18 13:00:16 +10:00
|
|
|
}
|
|
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
static void
|
2024-10-29 09:46:05 +10:00
|
|
|
pad_init_left_handed(struct evdev_device *device, WacomDevice *wacom)
|
2016-02-05 15:16:38 +10:00
|
|
|
{
|
2024-10-29 09:46:05 +10:00
|
|
|
bool has_left_handed = true;
|
|
|
|
|
|
2025-07-21 13:23:29 +10:00
|
|
|
#ifdef HAVE_LIBWACOM
|
2024-10-29 09:46:05 +10:00
|
|
|
has_left_handed = !wacom || libwacom_is_reversible(wacom);
|
|
|
|
|
#endif
|
|
|
|
|
if (has_left_handed)
|
2016-02-05 15:16:38 +10:00
|
|
|
evdev_init_left_handed(device, pad_change_to_left_handed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
pad_init(struct pad_dispatch *pad, struct evdev_device *device)
|
|
|
|
|
{
|
2024-10-29 09:46:05 +10:00
|
|
|
int rc = 1;
|
|
|
|
|
struct libinput *li = evdev_libinput_context(device);
|
|
|
|
|
WacomDevice *wacom = NULL;
|
2025-07-21 13:23:29 +10:00
|
|
|
#ifdef HAVE_LIBWACOM
|
2024-10-29 09:46:05 +10:00
|
|
|
WacomDeviceDatabase *db = libinput_libwacom_ref(li);
|
|
|
|
|
if (db) {
|
|
|
|
|
char event_path[64];
|
|
|
|
|
snprintf(event_path,
|
|
|
|
|
sizeof(event_path),
|
|
|
|
|
"/dev/input/%s",
|
|
|
|
|
evdev_device_get_sysname(device));
|
|
|
|
|
wacom = libwacom_new_from_path(db, event_path, WFALLBACK_NONE, NULL);
|
|
|
|
|
if (!wacom) {
|
|
|
|
|
wacom = libwacom_new_from_usbid(
|
|
|
|
|
db,
|
|
|
|
|
evdev_device_get_id_vendor(device),
|
|
|
|
|
evdev_device_get_id_product(device),
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
|
|
|
|
if (!wacom) {
|
|
|
|
|
evdev_log_info(
|
|
|
|
|
device,
|
|
|
|
|
"device \"%s\" (%04x:%04x) is not known to libwacom\n",
|
|
|
|
|
evdev_device_get_name(device),
|
|
|
|
|
evdev_device_get_id_vendor(device),
|
|
|
|
|
evdev_device_get_id_product(device));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-01-30 18:01:09 +10:00
|
|
|
pad->base.dispatch_type = DISPATCH_TABLET_PAD;
|
2016-02-05 15:16:38 +10:00
|
|
|
pad->base.interface = &pad_interface;
|
|
|
|
|
pad->device = device;
|
|
|
|
|
pad->status = PAD_NONE;
|
|
|
|
|
pad->changed_axes = PAD_AXIS_NONE;
|
2025-12-01 09:17:37 +10:00
|
|
|
ratelimit_init(&pad->modes.group_not_found, usec_from_hours(1), 3);
|
2016-02-05 15:16:38 +10:00
|
|
|
|
2024-01-30 14:43:59 +10:00
|
|
|
/* We expect the kernel to either give us both axes as hires or neither.
|
|
|
|
|
* Getting one is a kernel bug we don't need to care about */
|
|
|
|
|
pad->dials.has_hires_dial =
|
|
|
|
|
libevdev_has_event_code(device->evdev, EV_REL, REL_WHEEL_HI_RES) ||
|
|
|
|
|
libevdev_has_event_code(device->evdev, EV_REL, REL_HWHEEL_HI_RES);
|
|
|
|
|
|
|
|
|
|
if (libevdev_has_event_code(device->evdev, EV_REL, REL_WHEEL) &&
|
|
|
|
|
libevdev_has_event_code(device->evdev, EV_REL, REL_DIAL)) {
|
2024-10-29 09:46:05 +10:00
|
|
|
log_bug_libinput(li,
|
|
|
|
|
"Unsupported combination REL_DIAL and REL_WHEEL\n");
|
2024-01-30 14:43:59 +10:00
|
|
|
}
|
|
|
|
|
|
2024-10-29 09:46:05 +10:00
|
|
|
pad_init_buttons(pad, device, wacom);
|
|
|
|
|
pad_init_left_handed(device, wacom);
|
2016-02-05 15:16:38 +10:00
|
|
|
|
2024-10-29 09:46:05 +10:00
|
|
|
rc = pad_init_leds(pad, device, wacom);
|
|
|
|
|
|
pad: be more robust to kernel bugs of multiple EV_ABS
This is a workaround for a kernel bug with the Wacom Mobile Studio Pro
13: this device sends erroneous ABS_WHEEL events when pressing
buttons:
- evdev:
- [ 0, 0, 1, 256, 1] # EV_KEY / BTN_0 1
- [ 0, 0, 3, 8, 17] # EV_ABS / ABS_WHEEL 17 (+17)
- [ 0, 0, 3, 8, 0] # EV_ABS / ABS_WHEEL 0 (-17)
- [ 0, 0, 3, 40, 15] # EV_ABS / ABS_MISC 15 (+15)
- [ 0, 0, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +0ms
ABS_WHEEL to 17 then to 0 in the same frame. We should (and do) treat this
as a zero event and drop the 17 to the floor. Alas, we then generate a
ring event for the zero value despite our current value being zero
anyway. This again causes confusing behavior.
This is simple enough to work around, at least until the kernel is fixed
but also to prevent this happening in the future: warn if we get
multiple events and if so and the later event is zero, undo the axis
change state.
Closes: #1081
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1136>
2025-02-06 13:04:27 +10:00
|
|
|
/* at most 5 "Multiple EV_ABS events" log messages per hour */
|
2025-12-01 09:17:37 +10:00
|
|
|
ratelimit_init(&pad->duplicate_abs_limit, usec_from_seconds(60 * 60), 5);
|
pad: be more robust to kernel bugs of multiple EV_ABS
This is a workaround for a kernel bug with the Wacom Mobile Studio Pro
13: this device sends erroneous ABS_WHEEL events when pressing
buttons:
- evdev:
- [ 0, 0, 1, 256, 1] # EV_KEY / BTN_0 1
- [ 0, 0, 3, 8, 17] # EV_ABS / ABS_WHEEL 17 (+17)
- [ 0, 0, 3, 8, 0] # EV_ABS / ABS_WHEEL 0 (-17)
- [ 0, 0, 3, 40, 15] # EV_ABS / ABS_MISC 15 (+15)
- [ 0, 0, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +0ms
ABS_WHEEL to 17 then to 0 in the same frame. We should (and do) treat this
as a zero event and drop the 17 to the floor. Alas, we then generate a
ring event for the zero value despite our current value being zero
anyway. This again causes confusing behavior.
This is simple enough to work around, at least until the kernel is fixed
but also to prevent this happening in the future: warn if we get
multiple events and if so and the later event is zero, undo the axis
change state.
Closes: #1081
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1136>
2025-02-06 13:04:27 +10:00
|
|
|
|
2025-07-21 13:23:29 +10:00
|
|
|
#ifdef HAVE_LIBWACOM
|
2024-10-29 09:46:05 +10:00
|
|
|
if (wacom)
|
|
|
|
|
libwacom_destroy(wacom);
|
|
|
|
|
if (db)
|
|
|
|
|
libinput_libwacom_unref(li);
|
|
|
|
|
#endif
|
|
|
|
|
return rc;
|
2016-02-05 15:16:38 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct evdev_dispatch *
|
|
|
|
|
evdev_tablet_pad_create(struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct pad_dispatch *pad;
|
|
|
|
|
|
|
|
|
|
pad = zalloc(sizeof *pad);
|
|
|
|
|
|
|
|
|
|
if (pad_init(pad, device) != 0) {
|
|
|
|
|
pad_destroy(&pad->base);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-21 15:12:25 -04:00
|
|
|
evdev_init_sendevents(device, &pad->base);
|
2016-02-05 15:16:38 +10:00
|
|
|
|
|
|
|
|
return &pad->base;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-17 11:08:27 +10:00
|
|
|
int
|
|
|
|
|
evdev_device_tablet_pad_has_key(struct evdev_device *device, uint32_t code)
|
|
|
|
|
{
|
|
|
|
|
if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return libevdev_has_event_code(device->evdev, EV_KEY, code);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-30 14:43:59 +10:00
|
|
|
int
|
|
|
|
|
evdev_device_tablet_pad_get_num_dials(struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
int ndials = 0;
|
|
|
|
|
|
|
|
|
|
if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (libevdev_has_event_code(device->evdev, EV_REL, REL_WHEEL) ||
|
|
|
|
|
libevdev_has_event_code(device->evdev, EV_REL, REL_DIAL)) {
|
|
|
|
|
ndials++;
|
|
|
|
|
if (libevdev_has_event_code(device->evdev, EV_REL, REL_HWHEEL))
|
|
|
|
|
ndials++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ndials;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-05 15:16:38 +10:00
|
|
|
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;
|
|
|
|
|
}
|