summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Goins <josh@redstrate.com>2024-01-30 14:43:59 +1000
committerMarge Bot <emma+marge@anholt.net>2024-02-20 02:49:05 +0000
commitbeca998122089f55e79071a1d9ea77208a25f4c9 (patch)
tree565fcef8a83471f5f6ea27f259522c24fabc939d
parentd487ca36a4730d41c71a293b910bd0f48e58eac0 (diff)
tablet: add API for relative dials
Some tablets such as those in the XP-PEN PRO series use "dials" which are actually scrollwheels and emit EV_REL events. These should not be emulated as rings (which are absolute) so we must expose them as a new tablet event. Adds LIBINPUT_EVENT_TABLET_PAD_DIAL that work largely identical as our high-resolution wheel events (i.e. the values are in multiples or fractions of of 120). Currently supports two dials. This is a lot of copy/paste from the ring axes because the interface is virtually identical. The main difference is that dials give us a v120 value in the same manner as our scroll axes. Notes: - REL_DIAL is mutually exclusive with REL_WHEEL, we assume the kernel doesn't (at this point) give us devices with both. If this changes for devices with three dials (wheel + hwheel + dial) we need to add code for that. - REL_DIAL does not have a high-resolution axis and we assume that any device with REL_WHEEL_HI_RES will also have REL_HWHEEL_HI_RES (if the second wheel exists). - With dials being REL_DIAL or REL_WHEEL there is no possibility of detecting a finger release (the kernel does not route EV_RELs with a value of zero). Unless this is implemented via a side-channel - and it doesn't look like any hardware that supports dials does that - we cannot forward any information here. So unlike absolute rings we cannot provide a source information here. Closes #600 Co-authored-by: Peter Hutterer <peter.hutterer@who-t.net> Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/967>
-rw-r--r--meson.build3
-rw-r--r--src/evdev-tablet-pad-leds.c2
-rw-r--r--src/evdev-tablet-pad.c115
-rw-r--r--src/evdev-tablet-pad.h8
-rw-r--r--src/evdev.h3
-rw-r--r--src/libinput-private.h8
-rw-r--r--src/libinput.c79
-rw-r--r--src/libinput.h85
-rw-r--r--src/libinput.sym4
-rw-r--r--test/litest-device-huion-q620m-dial.c76
-rw-r--r--test/litest-device-tablet-doubledial.c81
-rw-r--r--test/litest-device-tablet-rel-dial.c78
-rw-r--r--test/litest-int.h8
-rw-r--r--test/litest.c43
-rw-r--r--test/litest.h7
-rw-r--r--test/test-pad.c86
-rw-r--r--tools/libinput-debug-events.c21
-rw-r--r--tools/libinput-debug-gui.c33
-rw-r--r--tools/libinput-debug-gui.man7
19 files changed, 735 insertions, 12 deletions
diff --git a/meson.build b/meson.build
index b4c85c15..26356838 100644
--- a/meson.build
+++ b/meson.build
@@ -757,6 +757,7 @@ if get_option('tests')
'test/litest-device-generic-singletouch.c',
'test/litest-device-gpio-keys.c',
'test/litest-device-huion-pentablet.c',
+ 'test/litest-device-huion-q620m-dial.c',
'test/litest-device-hp-wmi-hotkeys.c',
'test/litest-device-ignored-mouse.c',
'test/litest-device-keyboard.c',
@@ -792,7 +793,9 @@ if get_option('tests')
'test/litest-device-synaptics-t440.c',
'test/litest-device-synaptics-x1-carbon-3rd.c',
'test/litest-device-synaptics-phantomclicks.c',
+ 'test/litest-device-tablet-doubledial.c',
'test/litest-device-tablet-mode-switch.c',
+ 'test/litest-device-tablet-rel-dial.c',
'test/litest-device-thinkpad-extrabuttons.c',
'test/litest-device-trackpoint.c',
'test/litest-device-touch-screen.c',
diff --git a/src/evdev-tablet-pad-leds.c b/src/evdev-tablet-pad-leds.c
index ad8e6fa6..7419bb5b 100644
--- a/src/evdev-tablet-pad-leds.c
+++ b/src/evdev-tablet-pad-leds.c
@@ -517,6 +517,7 @@ pad_init_leds_from_libwacom(struct pad_dispatch *pad,
pad_init_mode_rings(pad, wacom);
pad_init_mode_strips(pad, wacom);
+ /* Note: libwacom doesn't do dials yet */
out:
if (wacom)
@@ -546,6 +547,7 @@ pad_init_fallback_group(struct pad_dispatch *pad)
group->base.button_mask = -1;
group->base.strip_mask = -1;
group->base.ring_mask = -1;
+ group->base.dial_mask = -1;
group->base.toggle_button_mask = 0;
list_insert(&pad->modes.mode_group_list, &group->base.link);
diff --git a/src/evdev-tablet-pad.c b/src/evdev-tablet-pad.c
index de3d7cbe..59f63cb5 100644
--- a/src/evdev-tablet-pad.c
+++ b/src/evdev-tablet-pad.c
@@ -98,6 +98,50 @@ pad_button_set_down(struct pad_dispatch *pad,
}
static void
+pad_process_relative(struct pad_dispatch *pad,
+ struct evdev_device *device,
+ struct input_event *e,
+ uint64_t time)
+{
+ switch (e->code) {
+ case REL_DIAL:
+ pad->dials.dial1 = e->value * 120;
+ pad->changed_axes |= PAD_AXIS_DIAL1;
+ pad_set_status(pad, PAD_AXES_UPDATED);
+ break;
+ case REL_WHEEL:
+ if (!pad->dials.has_hires_dial) {
+ pad->dials.dial1 = e->value * 120;
+ pad->changed_axes |= PAD_AXIS_DIAL1;
+ pad_set_status(pad, PAD_AXES_UPDATED);
+ }
+ break;
+ case REL_HWHEEL:
+ 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;
+ case REL_WHEEL_HI_RES:
+ pad->dials.dial1 = e->value;
+ pad->changed_axes |= PAD_AXIS_DIAL1;
+ pad_set_status(pad, PAD_AXES_UPDATED);
+ break;
+ case REL_HWHEEL_HI_RES:
+ pad->dials.dial2 = e->value * 120;
+ 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",
+ e->code);
+ break;
+ }
+}
+
+static void
pad_process_absolute(struct pad_dispatch *pad,
struct evdev_device *device,
struct input_event *e,
@@ -217,6 +261,22 @@ pad_handle_strip(struct pad_dispatch *pad,
}
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) {
+ if (libinput_tablet_pad_mode_group_has_dial(group, dial))
+ return group;
+ }
+
+ assert(!"Unable to find dial mode group");
+
+ return NULL;
+}
+
+static inline struct libinput_tablet_pad_mode_group *
pad_ring_get_mode_group(struct pad_dispatch *pad,
unsigned int ring)
{
@@ -264,6 +324,26 @@ pad_check_notify_axes(struct pad_dispatch *pad,
libevdev_get_event_value(device->evdev, EV_ABS, ABS_MISC) == 0)
send_finger_up = true;
+ /* 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);
+ tablet_pad_notify_dial(base,
+ time,
+ 0,
+ pad->dials.dial1,
+ group);
+ }
+
+ if (pad->changed_axes & PAD_AXIS_DIAL2) {
+ group = pad_dial_get_mode_group(pad, 1);
+ tablet_pad_notify_dial(base,
+ time,
+ 1,
+ pad->dials.dial2,
+ group);
+ }
+
if (pad->changed_axes & PAD_AXIS_RING1) {
value = pad_handle_ring(pad, device, ABS_WHEEL);
if (send_finger_up)
@@ -473,6 +553,8 @@ pad_flush(struct pad_dispatch *pad,
memcpy(&pad->prev_button_state,
&pad->button_state,
sizeof(pad->button_state));
+ pad->dials.dial1 = 0;
+ pad->dials.dial2 = 0;
}
static void
@@ -484,6 +566,9 @@ pad_process(struct evdev_dispatch *dispatch,
struct pad_dispatch *pad = pad_dispatch(dispatch);
switch (e->type) {
+ case EV_REL:
+ pad_process_relative(pad, device, e, time);
+ break;
case EV_ABS:
pad_process_absolute(pad, device, e, time);
break;
@@ -686,6 +771,16 @@ pad_init(struct pad_dispatch *pad, struct evdev_device *device)
pad->status = PAD_NONE;
pad->changed_axes = PAD_AXIS_NONE;
+ /* 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)) {
+ log_bug_libinput(pad_libinput_context(pad), "Unsupported combination REL_DIAL and REL_WHEEL\n");
+ }
+
pad_init_buttons(pad, device);
pad_init_left_handed(device);
if (pad_init_leds(pad, device) != 0)
@@ -783,6 +878,26 @@ evdev_device_tablet_pad_get_num_buttons(struct evdev_device *device)
}
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;
+}
+
+int
evdev_device_tablet_pad_get_num_rings(struct evdev_device *device)
{
int nrings = 0;
diff --git a/src/evdev-tablet-pad.h b/src/evdev-tablet-pad.h
index 3fc0f796..fe7aa8b7 100644
--- a/src/evdev-tablet-pad.h
+++ b/src/evdev-tablet-pad.h
@@ -41,6 +41,8 @@ enum pad_axes {
PAD_AXIS_RING2 = bit(1),
PAD_AXIS_STRIP1 = bit(2),
PAD_AXIS_STRIP2 = bit(3),
+ PAD_AXIS_DIAL1 = bit(4),
+ PAD_AXIS_DIAL2 = bit(5),
};
struct button_state {
@@ -74,6 +76,12 @@ struct pad_dispatch {
bool have_abs_misc_terminator;
struct {
+ bool has_hires_dial;
+ double dial1;
+ double dial2;
+ } dials;
+
+ struct {
struct libinput_device_config_send_events config;
enum libinput_config_send_events_mode current_mode;
} sendevents;
diff --git a/src/evdev.h b/src/evdev.h
index 0f0087a3..7e2567be 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -519,6 +519,9 @@ int
evdev_device_tablet_pad_get_num_buttons(struct evdev_device *device);
int
+evdev_device_tablet_pad_get_num_dials(struct evdev_device *device);
+
+int
evdev_device_tablet_pad_get_num_rings(struct evdev_device *device);
int
diff --git a/src/libinput-private.h b/src/libinput-private.h
index b218b390..de1d7d64 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -503,6 +503,7 @@ struct libinput_tablet_pad_mode_group {
uint32_t button_mask;
uint32_t ring_mask;
uint32_t strip_mask;
+ uint32_t dial_mask;
uint32_t toggle_button_mask;
@@ -797,6 +798,13 @@ tablet_pad_notify_button(struct libinput_device *device,
enum libinput_button_state state,
struct libinput_tablet_pad_mode_group *group);
void
+tablet_pad_notify_dial(struct libinput_device *device,
+ uint64_t time,
+ unsigned int number,
+ double value,
+ struct libinput_tablet_pad_mode_group *group);
+
+void
tablet_pad_notify_ring(struct libinput_device *device,
uint64_t time,
unsigned int number,
diff --git a/src/libinput.c b/src/libinput.c
index 42be3f92..0b42db2e 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -104,6 +104,7 @@ event_type_to_str(enum libinput_event_type type)
CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_RING);
CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_STRIP);
CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_KEY);
+ CASE_RETURN_STRING(LIBINPUT_EVENT_TABLET_PAD_DIAL);
CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN);
CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE);
CASE_RETURN_STRING(LIBINPUT_EVENT_GESTURE_SWIPE_END);
@@ -233,6 +234,10 @@ struct libinput_event_tablet_pad {
enum libinput_key_state state;
} key;
struct {
+ double v120;
+ int number;
+ } dial;
+ struct {
enum libinput_tablet_pad_ring_axis_source source;
double position;
int number;
@@ -443,6 +448,7 @@ libinput_event_get_tablet_pad_event(struct libinput_event *event)
event->type,
NULL,
LIBINPUT_EVENT_TABLET_PAD_RING,
+ LIBINPUT_EVENT_TABLET_PAD_DIAL,
LIBINPUT_EVENT_TABLET_PAD_STRIP,
LIBINPUT_EVENT_TABLET_PAD_BUTTON,
LIBINPUT_EVENT_TABLET_PAD_KEY);
@@ -2024,6 +2030,7 @@ libinput_event_destroy(struct libinput_event *event)
libinput_event_get_tablet_tool_event(event));
break;
case LIBINPUT_EVENT_TABLET_PAD_RING:
+ case LIBINPUT_EVENT_TABLET_PAD_DIAL:
case LIBINPUT_EVENT_TABLET_PAD_STRIP:
case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
case LIBINPUT_EVENT_TABLET_PAD_KEY:
@@ -2910,6 +2917,34 @@ tablet_pad_notify_button(struct libinput_device *device,
}
void
+tablet_pad_notify_dial(struct libinput_device *device,
+ uint64_t time,
+ unsigned int number,
+ double value,
+ struct libinput_tablet_pad_mode_group *group)
+{
+ struct libinput_event_tablet_pad *dial_event;
+ unsigned int mode;
+
+ dial_event = zalloc(sizeof *dial_event);
+
+ mode = libinput_tablet_pad_mode_group_get_mode(group);
+
+ *dial_event = (struct libinput_event_tablet_pad) {
+ .time = time,
+ .dial.number = number,
+ .dial.v120 = value,
+ .mode_group = libinput_tablet_pad_mode_group_ref(group),
+ .mode = mode,
+ };
+
+ post_device_event(device,
+ time,
+ LIBINPUT_EVENT_TABLET_PAD_DIAL,
+ &dial_event->base);
+}
+
+void
tablet_pad_notify_ring(struct libinput_device *device,
uint64_t time,
unsigned int number,
@@ -3376,6 +3411,12 @@ libinput_device_tablet_pad_get_num_buttons(struct libinput_device *device)
}
LIBINPUT_EXPORT int
+libinput_device_tablet_pad_get_num_dials(struct libinput_device *device)
+{
+ return evdev_device_tablet_pad_get_num_dials((struct evdev_device *)device);
+}
+
+LIBINPUT_EXPORT int
libinput_device_tablet_pad_get_num_rings(struct libinput_device *device)
{
return evdev_device_tablet_pad_get_num_rings((struct evdev_device *)device);
@@ -3432,6 +3473,17 @@ libinput_tablet_pad_mode_group_has_button(struct libinput_tablet_pad_mode_group
}
LIBINPUT_EXPORT int
+libinput_tablet_pad_mode_group_has_dial(struct libinput_tablet_pad_mode_group *group,
+ unsigned int dial)
+{
+ if ((int)dial >=
+ libinput_device_tablet_pad_get_num_dials(group->device))
+ return 0;
+
+ return !!(group->dial_mask & bit(dial));
+}
+
+LIBINPUT_EXPORT int
libinput_tablet_pad_mode_group_has_ring(struct libinput_tablet_pad_mode_group *group,
unsigned int ring)
{
@@ -3590,6 +3642,28 @@ libinput_event_tablet_tool_get_base_event(struct libinput_event_tablet_tool *eve
}
LIBINPUT_EXPORT double
+libinput_event_tablet_pad_get_dial_delta_v120(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0.0,
+ LIBINPUT_EVENT_TABLET_PAD_DIAL);
+
+ return event->dial.v120;
+}
+
+LIBINPUT_EXPORT unsigned int
+libinput_event_tablet_pad_get_dial_number(struct libinput_event_tablet_pad *event)
+{
+ require_event_type(libinput_event_get_context(&event->base),
+ event->base.type,
+ 0,
+ LIBINPUT_EVENT_TABLET_PAD_DIAL);
+
+ return event->dial.number;
+}
+
+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),
@@ -3706,6 +3780,7 @@ libinput_event_tablet_pad_get_mode(struct libinput_event_tablet_pad *event)
event->base.type,
0,
LIBINPUT_EVENT_TABLET_PAD_RING,
+ LIBINPUT_EVENT_TABLET_PAD_DIAL,
LIBINPUT_EVENT_TABLET_PAD_STRIP,
LIBINPUT_EVENT_TABLET_PAD_BUTTON);
@@ -3719,6 +3794,7 @@ libinput_event_tablet_pad_get_mode_group(struct libinput_event_tablet_pad *event
event->base.type,
NULL,
LIBINPUT_EVENT_TABLET_PAD_RING,
+ LIBINPUT_EVENT_TABLET_PAD_DIAL,
LIBINPUT_EVENT_TABLET_PAD_STRIP,
LIBINPUT_EVENT_TABLET_PAD_BUTTON);
@@ -3732,6 +3808,7 @@ libinput_event_tablet_pad_get_time(struct libinput_event_tablet_pad *event)
event->base.type,
0,
LIBINPUT_EVENT_TABLET_PAD_RING,
+ LIBINPUT_EVENT_TABLET_PAD_DIAL,
LIBINPUT_EVENT_TABLET_PAD_STRIP,
LIBINPUT_EVENT_TABLET_PAD_BUTTON,
LIBINPUT_EVENT_TABLET_PAD_KEY);
@@ -3746,6 +3823,7 @@ libinput_event_tablet_pad_get_time_usec(struct libinput_event_tablet_pad *event)
event->base.type,
0,
LIBINPUT_EVENT_TABLET_PAD_RING,
+ LIBINPUT_EVENT_TABLET_PAD_DIAL,
LIBINPUT_EVENT_TABLET_PAD_STRIP,
LIBINPUT_EVENT_TABLET_PAD_BUTTON,
LIBINPUT_EVENT_TABLET_PAD_KEY);
@@ -3760,6 +3838,7 @@ libinput_event_tablet_pad_get_base_event(struct libinput_event_tablet_pad *event
event->base.type,
NULL,
LIBINPUT_EVENT_TABLET_PAD_RING,
+ LIBINPUT_EVENT_TABLET_PAD_DIAL,
LIBINPUT_EVENT_TABLET_PAD_STRIP,
LIBINPUT_EVENT_TABLET_PAD_BUTTON,
LIBINPUT_EVENT_TABLET_PAD_KEY);
diff --git a/src/libinput.h b/src/libinput.h
index 67445946..0c14098a 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -164,7 +164,8 @@ struct libinput_event_tablet_tool;
*
* 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
+ * LIBINPUT_EVENT_TABLET_PAD_BUTTON, @ref LIBINPUT_EVENT_TABLET_PAD_DIAL,
+ * @ref LIBINPUT_EVENT_TABLET_PAD_RING and
* @ref LIBINPUT_EVENT_TABLET_PAD_STRIP.
*
* @since 1.3
@@ -429,7 +430,8 @@ struct libinput_tablet_pad_mode_group;
* the Wacom Cintiq 22HD provide two mode groups. If multiple mode groups
* are available, a caller should use
* libinput_tablet_pad_mode_group_has_button(),
- * libinput_tablet_pad_mode_group_has_ring() and
+ * libinput_tablet_pad_mode_group_has_ring(),
+ * libinput_tablet_pad_mode_group_has_dial() and
* libinput_tablet_pad_mode_group_has_strip() to associate each button,
* ring and strip with the correct mode group.
*
@@ -542,6 +544,22 @@ libinput_tablet_pad_mode_group_has_button(struct libinput_tablet_pad_mode_group
/**
* @ingroup tablet_pad_modes
*
+ * Devices without mode switching capabilities return true for every dial.
+ *
+ * @param group A previously obtained mode group
+ * @param dial A dial index, starting at 0
+ * @return true if the given dial index is part of this mode group or
+ * false otherwise
+ *
+ * @since 1.26
+ */
+int
+libinput_tablet_pad_mode_group_has_dial(struct libinput_tablet_pad_mode_group *group,
+ unsigned int dial);
+
+/**
+ * @ingroup tablet_pad_modes
+ *
* Devices without mode switching capabilities return true for every ring.
*
* @param group A previously obtained mode group
@@ -968,6 +986,14 @@ enum libinput_event_type {
*/
LIBINPUT_EVENT_TABLET_PAD_KEY,
+ /**
+ * A status change on a tablet dial with the @ref
+ * LIBINPUT_DEVICE_CAP_TABLET_PAD capability.
+ *
+ * @since 1.26
+ */
+ LIBINPUT_EVENT_TABLET_PAD_DIAL,
+
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN = 800,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
LIBINPUT_EVENT_GESTURE_SWIPE_END,
@@ -3317,6 +3343,44 @@ libinput_event_tablet_pad_get_key_state(struct libinput_event_tablet_pad *event)
/**
* @ingroup event_tablet_pad
*
+ * Returns the delta change of the dial, in multiples or fractions of 120, with
+ * each multiple of 120 indicating one logical wheel event.
+ * See libinput_event_pointer_get_scroll_value_v120() for more details.
+ *
+ * @note It is an application bug to call this function for events other than
+ * @ref LIBINPUT_EVENT_TABLET_PAD_DIAL. For other events, this function
+ * returns 0.
+ *
+ * @param event The libinput tablet pad event
+ * @return The delta of the the axis
+ *
+ * @since 1.26
+ */
+double
+libinput_event_tablet_pad_get_dial_delta_v120(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet_pad
+ *
+ * Returns the number of the dial that has changed state, with 0 being the
+ * first dial. On tablets with only one dial, this function always returns
+ * 0.
+ *
+ * @note It is an application bug to call this function for events other than
+ * @ref LIBINPUT_EVENT_TABLET_PAD_DIAL. For other events, this function
+ * returns 0.
+ *
+ * @param event The libinput tablet pad event
+ * @return The index of the dial that changed state
+ *
+ * @since 1.26
+ */
+unsigned int
+libinput_event_tablet_pad_get_dial_number(struct libinput_event_tablet_pad *event);
+
+/**
+ * @ingroup event_tablet_pad
+ *
* Returns the mode the button, ring, or strip that triggered this event is
* in, at the time of the event.
*
@@ -4380,6 +4444,23 @@ libinput_device_tablet_pad_get_num_buttons(struct libinput_device *device);
/**
* @ingroup device
*
+ * Return the number of dials a device with the @ref
+ * LIBINPUT_DEVICE_CAP_TABLET_PAD capability provides.
+ *
+ * @param device A current input device
+ *
+ * @return The number of dials or 0 if the device has no dials. -1 on error.
+ *
+ * @see libinput_event_tablet_pad_get_dial_number
+ *
+ * @since 1.26
+ */
+int
+libinput_device_tablet_pad_get_num_dials(struct libinput_device *device);
+
+/**
+ * @ingroup device
+ *
* Return the number of rings a device with the @ref
* LIBINPUT_DEVICE_CAP_TABLET_PAD capability provides.
*
diff --git a/src/libinput.sym b/src/libinput.sym
index 78739059..f48e39e2 100644
--- a/src/libinput.sym
+++ b/src/libinput.sym
@@ -335,6 +335,10 @@ LIBINPUT_1.23 {
} LIBINPUT_1.21;
LIBINPUT_1.26 {
+ libinput_device_tablet_pad_get_num_dials;
+ libinput_event_tablet_pad_get_dial_delta_v120;
+ libinput_event_tablet_pad_get_dial_number;
+ libinput_tablet_pad_mode_group_has_dial;
libinput_tablet_tool_config_pressure_range_is_available;
libinput_tablet_tool_config_pressure_range_set;
libinput_tablet_tool_config_pressure_range_get_minimum;
diff --git a/test/litest-device-huion-q620m-dial.c b/test/litest-device-huion-q620m-dial.c
new file mode 100644
index 00000000..e76313c3
--- /dev/null
+++ b/test/litest-device-huion-q620m-dial.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2024 Red Hat, Inc.
+ *
+ * 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:
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include "litest.h"
+#include "litest-int.h"
+
+static struct input_event down[] = {
+ { .type = -1, .code = -1 },
+};
+
+static struct input_event move[] = {
+ { .type = -1, .code = -1 },
+};
+
+static struct litest_device_interface interface = {
+ .touch_down_events = down,
+ .touch_move_events = move,
+};
+
+static struct input_absinfo absinfo[] = {
+ { ABS_X, 0, 1, 0, 0, 0 },
+ { ABS_Y, 0, 1, 0, 0, 0 },
+ { ABS_MISC, 0, 255, 0, 0, 0 },
+ { .value = -1 },
+};
+
+static struct input_id input_id = {
+ .bustype = 0x3,
+ .vendor = 0x256c,
+ .product = 0x006d,
+};
+
+static int events[] = {
+ EV_KEY, BTN_0,
+ EV_REL, REL_WHEEL,
+ EV_REL, REL_WHEEL_HI_RES,
+ -1, -1,
+};
+
+/* Device from https://gitlab.freedesktop.org/libinput/libinput/-/issues/600 */
+TEST_DEVICE("huion-q620m-dial-pad",
+ .type = LITEST_HUION_Q620M_DIAL,
+ .features = LITEST_TABLET_PAD | LITEST_DIAL,
+ .interface = &interface,
+
+ .name = "HUION Huion Tablet_Q620M Dial",
+ .id = &input_id,
+ .events = events,
+ .absinfo = absinfo,
+ .udev_properties = {
+ { "ID_INPUT_TABLET_PAD", "1" },
+ { NULL },
+ },
+)
diff --git a/test/litest-device-tablet-doubledial.c b/test/litest-device-tablet-doubledial.c
new file mode 100644
index 00000000..34e4fd26
--- /dev/null
+++ b/test/litest-device-tablet-doubledial.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2024 Red Hat, Inc.
+ *
+ * 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:
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include "litest.h"
+#include "litest-int.h"
+
+static struct input_event down[] = {
+ { .type = -1, .code = -1 },
+};
+
+static struct input_event move[] = {
+ { .type = -1, .code = -1 },
+};
+
+static struct litest_device_interface interface = {
+ .touch_down_events = down,
+ .touch_move_events = move,
+};
+
+static struct input_absinfo absinfo[] = {
+ { ABS_X, 0, 1, 0, 0, 0 },
+ { ABS_Y, 0, 1, 0, 0, 0 },
+ { ABS_MISC, 0, 0, 0, 0, 0 },
+ { .value = -1 },
+};
+
+static struct input_id input_id = {
+ .bustype = 0x3,
+ .vendor = 0x123,
+ .product = 0x678,
+};
+
+static int events[] = {
+ EV_KEY, BTN_0,
+ EV_KEY, BTN_1,
+ EV_KEY, BTN_2,
+ EV_KEY, BTN_3,
+ EV_KEY, BTN_STYLUS,
+ EV_REL, REL_WHEEL,
+ EV_REL, REL_WHEEL_HI_RES,
+ EV_REL, REL_HWHEEL,
+ EV_REL, REL_HWHEEL_HI_RES,
+ -1, -1,
+};
+
+TEST_DEVICE("tablet-doubledial-pad",
+ .type = LITEST_TABLET_DOUBLEDIAL_PAD,
+ .features = LITEST_TABLET_PAD | LITEST_DIAL,
+ .interface = &interface,
+
+ .name = "Generic Double Dial Pad",
+ .id = &input_id,
+ .events = events,
+ .absinfo = absinfo,
+ .udev_properties = {
+ { "ID_INPUT_TABLET_PAD", "1" },
+ { NULL },
+ },
+)
diff --git a/test/litest-device-tablet-rel-dial.c b/test/litest-device-tablet-rel-dial.c
new file mode 100644
index 00000000..c32b4c47
--- /dev/null
+++ b/test/litest-device-tablet-rel-dial.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2024 Red Hat, Inc.
+ *
+ * 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:
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include "litest.h"
+#include "litest-int.h"
+
+static struct input_event down[] = {
+ { .type = -1, .code = -1 },
+};
+
+static struct input_event move[] = {
+ { .type = -1, .code = -1 },
+};
+
+static struct litest_device_interface interface = {
+ .touch_down_events = down,
+ .touch_move_events = move,
+};
+
+static struct input_absinfo absinfo[] = {
+ { ABS_X, 0, 1, 0, 0, 0 },
+ { ABS_Y, 0, 1, 0, 0, 0 },
+ { ABS_MISC, 0, 0, 0, 0, 0 },
+ { .value = -1 },
+};
+
+static struct input_id input_id = {
+ .bustype = 0x3,
+ .vendor = 0x123,
+ .product = 0x456,
+};
+
+static int events[] = {
+ EV_KEY, BTN_0,
+ EV_KEY, BTN_1,
+ EV_KEY, BTN_2,
+ EV_KEY, BTN_3,
+ EV_KEY, BTN_STYLUS,
+ EV_REL, REL_DIAL,
+ -1, -1,
+};
+
+TEST_DEVICE("tablet-rel-dial-pad",
+ .type = LITEST_TABLET_REL_DIAL_PAD,
+ .features = LITEST_TABLET_PAD | LITEST_DIAL,
+ .interface = &interface,
+
+ .name = "Generic Rel DialPad",
+ .id = &input_id,
+ .events = events,
+ .absinfo = absinfo,
+ .udev_properties = {
+ { "ID_INPUT_TABLET_PAD", "1" },
+ { NULL },
+ },
+)
diff --git a/test/litest-int.h b/test/litest-int.h
index 9226f574..8b76e7ee 100644
--- a/test/litest-int.h
+++ b/test/litest-int.h
@@ -139,6 +139,14 @@ struct litest_device_interface {
/**
* Pad events, LITEST_AUTO_ASSIGN is allowed on event values
+ * for ABS_WHEEL
+ */
+ struct input_event *pad_dial_start_events;
+ struct input_event *pad_dial_change_events;
+ struct input_event *pad_dial_end_events;
+
+ /**
+ * Pad events, LITEST_AUTO_ASSIGN is allowed on event values
* for ABS_RX
*/
struct input_event *pad_strip_start_events;
diff --git a/test/litest.c b/test/litest.c
index 0ff9d1e3..792b0a54 100644
--- a/test/litest.c
+++ b/test/litest.c
@@ -2901,10 +2901,23 @@ auto_assign_pad_value(struct litest_device *dev,
{
const struct input_absinfo *abs;
- if (ev->value != LITEST_AUTO_ASSIGN ||
- ev->type != EV_ABS)
+ if (ev->value != LITEST_AUTO_ASSIGN)
return value;
+ if (ev->type == EV_REL) {
+ switch (ev->code) {
+ case REL_WHEEL:
+ case REL_HWHEEL:
+ case REL_DIAL:
+ assert (fmod(value, 120.0) == 0.0); /* Fractions not supported yet */
+ return value/120.0;
+ default:
+ return value;
+ }
+ } else if (ev->type != EV_ABS) {
+ return value;
+ }
+
abs = libevdev_get_abs_info(dev->evdev, ev->code);
litest_assert_notnull(abs);
@@ -3213,6 +3226,9 @@ litest_event_type_str(enum libinput_event_type type)
case LIBINPUT_EVENT_TABLET_PAD_KEY:
str = "TABLET PAD KEY";
break;
+ case LIBINPUT_EVENT_TABLET_PAD_DIAL:
+ str = "TABLET PAD DIAL";
+ break;
case LIBINPUT_EVENT_SWITCH_TOGGLE:
str = "SWITCH TOGGLE";
break;
@@ -3314,6 +3330,12 @@ litest_print_event(struct libinput_event *event)
libinput_event_tablet_pad_get_ring_position(pad),
libinput_event_tablet_pad_get_ring_source(pad));
break;
+ case LIBINPUT_EVENT_TABLET_PAD_DIAL:
+ pad = libinput_event_get_tablet_pad_event(event);
+ fprintf(stderr, "dial %d delta %.2f",
+ libinput_event_tablet_pad_get_dial_number(pad),
+ libinput_event_tablet_pad_get_dial_delta_v120(pad));
+ break;
default:
break;
}
@@ -3921,6 +3943,23 @@ litest_is_pad_button_event(struct libinput_event *event,
}
struct libinput_event_tablet_pad *
+litest_is_pad_dial_event(struct libinput_event *event,
+ unsigned int number)
+{
+ struct libinput_event_tablet_pad *p;
+ enum libinput_event_type type = LIBINPUT_EVENT_TABLET_PAD_DIAL;
+
+ litest_assert_ptr_notnull(event);
+ litest_assert_event_type(event, type);
+ p = libinput_event_get_tablet_pad_event(event);
+
+ litest_assert_int_eq(libinput_event_tablet_pad_get_dial_number(p),
+ number);
+
+ 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)
diff --git a/test/litest.h b/test/litest.h
index 672e6ef5..bdb217b2 100644
--- a/test/litest.h
+++ b/test/litest.h
@@ -315,6 +315,9 @@ enum litest_device_type {
/* Tablets */
LITEST_ELAN_TABLET,
LITEST_HUION_TABLET,
+ LITEST_HUION_Q620M_DIAL,
+ LITEST_TABLET_DOUBLEDIAL_PAD,
+ LITEST_TABLET_REL_DIAL_PAD,
LITEST_QEMU_TABLET,
LITEST_UCLOGIC_TABLET,
LITEST_WACOM_BAMBOO,
@@ -380,6 +383,7 @@ enum litest_device_type {
#define LITEST_TOTEM bit(31)
#define LITEST_FORCED_PROXOUT bit(32)
#define LITEST_PRECALIBRATED bit(33)
+#define LITEST_DIAL bit(34)
/* this is a semi-mt device, so we keep track of the touches that the tests
* send and modify them so that the first touch is always slot 0 and sends
@@ -812,6 +816,9 @@ 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_dial_event(struct libinput_event *event,
+ unsigned int number);
+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);
diff --git a/test/test-pad.c b/test/test-pad.c
index 2aeb735b..1ee12534 100644
--- a/test/test-pad.c
+++ b/test/test-pad.c
@@ -486,6 +486,82 @@ START_TEST(pad_ring_finger_up)
}
END_TEST
+START_TEST(pad_has_dial)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput_device *device = dev->libinput_device;
+ int ndials;
+ int expected_ndials = 1;
+
+ if (libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL))
+ expected_ndials = 2;
+
+ ndials = libinput_device_tablet_pad_get_num_dials(device);
+ ck_assert_int_ge(ndials, expected_ndials);
+}
+END_TEST
+
+START_TEST(pad_dial_low_res)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ unsigned int code = 0;
+
+ if (libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL))
+ code = REL_WHEEL;
+ if (libevdev_has_event_code(dev->evdev, EV_REL, REL_DIAL))
+ code = REL_DIAL;
+
+ litest_drain_events(li);
+
+ for (int i = 0; i < 10; i++) {
+ int direction = -1 + 2 * i % 2;
+ litest_event(dev, EV_REL, code, direction);
+ if (code == REL_WHEEL)
+ litest_event(dev, EV_REL, REL_WHEEL_HI_RES, direction * 120);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+ libinput_dispatch(li);
+
+ struct libinput_event *ev = libinput_get_event(li);
+ struct libinput_event_tablet_pad *pev = litest_is_pad_dial_event(ev, 0);
+
+ double v120 = libinput_event_tablet_pad_get_dial_delta_v120(pev);
+ ck_assert_double_ge(v120, 120.0 * direction);
+ libinput_event_destroy(ev);
+ }
+}
+END_TEST
+
+START_TEST(pad_dial_hi_res)
+{
+ struct litest_device *dev = litest_current_device();
+ struct libinput *li = dev->libinput;
+ const int increment = 30;
+ int accumulated = 0;
+
+ if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES))
+ return;
+
+ litest_drain_events(li);
+
+ for (int i = 0; i < 10; i++) {
+ litest_event(dev, EV_REL, REL_WHEEL_HI_RES, increment);
+ accumulated += increment;
+ if (accumulated % 120 == 0)
+ litest_event(dev, EV_REL, REL_WHEEL, 1);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+ libinput_dispatch(li);
+
+ struct libinput_event *ev = libinput_get_event(li);
+ struct libinput_event_tablet_pad *pev = litest_is_pad_dial_event(ev, 0);
+
+ double v120 = libinput_event_tablet_pad_get_dial_delta_v120(pev);
+ ck_assert_double_ge(v120, increment);
+ libinput_event_destroy(ev);
+ }
+}
+END_TEST
+
START_TEST(pad_has_strip)
{
struct litest_device *dev = litest_current_device();
@@ -994,10 +1070,12 @@ TEST_COLLECTION(tablet_pad)
litest_add(pad_time, LITEST_TABLET_PAD, LITEST_ANY);
litest_add(pad_num_buttons, LITEST_TABLET_PAD, LITEST_ANY);
- litest_add(pad_num_buttons_libwacom, LITEST_TABLET_PAD, LITEST_ANY);
+ /* None of our dial devices have libwacom entries */
+ litest_add(pad_num_buttons_libwacom, LITEST_TABLET_PAD, LITEST_DIAL);
litest_add(pad_button_intuos, LITEST_TABLET_PAD, LITEST_ANY);
litest_add(pad_button_bamboo, LITEST_TABLET_PAD, LITEST_ANY);
- litest_add(pad_button_libwacom, LITEST_TABLET_PAD, LITEST_ANY);
+ /* None of our dial devices have libwacom entries */
+ litest_add(pad_button_libwacom, LITEST_TABLET_PAD, LITEST_DIAL);
litest_add(pad_button_mode_groups, LITEST_TABLET_PAD, LITEST_ANY);
litest_add(pad_has_ring, LITEST_RING, LITEST_ANY);
@@ -1008,6 +1086,10 @@ TEST_COLLECTION(tablet_pad)
litest_add(pad_strip, LITEST_STRIP, LITEST_ANY);
litest_add(pad_strip_finger_up, LITEST_STRIP, LITEST_ANY);
+ litest_add(pad_has_dial, LITEST_DIAL, LITEST_ANY);
+ litest_add(pad_dial_low_res, LITEST_DIAL, LITEST_ANY);
+ litest_add(pad_dial_hi_res, LITEST_DIAL, LITEST_ANY);
+
litest_add_for_device(pad_left_handed_default, LITEST_WACOM_INTUOS5_PAD);
litest_add_for_device(pad_no_left_handed, LITEST_WACOM_INTUOS3_PAD);
litest_add_for_device(pad_left_handed_ring, LITEST_WACOM_INTUOS5_PAD);
diff --git a/tools/libinput-debug-events.c b/tools/libinput-debug-events.c
index c4877c62..6230b18f 100644
--- a/tools/libinput-debug-events.c
+++ b/tools/libinput-debug-events.c
@@ -158,6 +158,9 @@ print_event_header(struct libinput_event *ev)
case LIBINPUT_EVENT_TABLET_PAD_KEY:
type = "TABLET_PAD_KEY";
break;
+ case LIBINPUT_EVENT_TABLET_PAD_DIAL:
+ type = "TABLET_PAD_DIAL";
+ break;
case LIBINPUT_EVENT_SWITCH_TOGGLE:
type = "SWITCH_TOGGLE";
break;
@@ -820,6 +823,21 @@ print_tablet_pad_key_event(struct libinput_event *ev)
}
static void
+print_tablet_pad_dial_event(struct libinput_event *ev)
+{
+ struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
+ unsigned int mode;
+
+ print_event_time(libinput_event_tablet_pad_get_time(p));
+
+ mode = libinput_event_tablet_pad_get_mode(p);
+ printq("dial %d delta %.2f (mode %d)\n",
+ libinput_event_tablet_pad_get_dial_number(p),
+ libinput_event_tablet_pad_get_dial_delta_v120(p),
+ mode);
+}
+
+static void
print_switch_event(struct libinput_event *ev)
{
struct libinput_event_switch *sw = libinput_event_get_switch_event(ev);
@@ -943,6 +961,9 @@ handle_and_print_events(struct libinput *li)
case LIBINPUT_EVENT_TABLET_PAD_KEY:
print_tablet_pad_key_event(ev);
break;
+ case LIBINPUT_EVENT_TABLET_PAD_DIAL:
+ print_tablet_pad_dial_event(ev);
+ break;
case LIBINPUT_EVENT_SWITCH_TOGGLE:
print_switch_event(ev);
break;
diff --git a/tools/libinput-debug-gui.c b/tools/libinput-debug-gui.c
index 619828cc..a1300c64 100644
--- a/tools/libinput-debug-gui.c
+++ b/tools/libinput-debug-gui.c
@@ -190,6 +190,10 @@ struct window {
double position;
int number;
} strip;
+ struct {
+ double position;
+ int number;
+ } dial;
} pad;
struct {
@@ -720,12 +724,12 @@ draw_pad(struct window *w, cairo_t *cr)
ry = w->height/2 + 100;
cairo_save(cr);
- /* outer ring */
+ /* outer ring (for ring) */
cairo_set_source_rgb(cr, .7, .7, .0);
cairo_arc(cr, rx, ry, 50, 0, 2 * M_PI);
cairo_fill(cr);
- /* inner ring */
+ /* inner ring (for dial) */
cairo_set_source_rgb(cr, 1., 1., 1.);
cairo_arc(cr, rx, ry, 30, 0, 2 * M_PI);
cairo_fill(cr);
@@ -743,7 +747,20 @@ draw_pad(struct window *w, cairo_t *cr)
snprintf(number, sizeof(number), "%d", w->pad.ring.number);
cairo_set_source_rgb(cr, .0, .0, .0);
draw_text(cr, number, rx, ry);
+ }
+ if (w->pad.dial.position != -1) {
+ const int degrees_per_click = 15.0;
+ double degrees = fmod(w->pad.dial.position/120 * degrees_per_click, 360);
+ pos = (degrees + 270) * M_PI/180.0;
+ cairo_set_source_rgb(cr, .0, .0, .0);
+ cairo_set_line_width(cr, 20);
+ cairo_arc(cr, rx, ry, 20, pos - M_PI/12 , pos + M_PI/12);
+ cairo_stroke(cr);
+
+ snprintf(number, sizeof(number), "%d", w->pad.dial.number);
+ cairo_set_source_rgb(cr, .0, .0, .0);
+ draw_text(cr, number, rx, ry);
}
cairo_restore(cr);
@@ -1160,6 +1177,7 @@ window_init(struct window *w)
w->pad.ring.position = -1;
w->pad.strip.position = -1;
+ w->pad.dial.position = -1;
}
static void
@@ -1726,7 +1744,7 @@ handle_event_tablet_pad(struct libinput_event *ev, struct window *w)
"Pad 0", "Pad 1", "Pad 2", "Pad 3", "Pad 4", "Pad 5",
"Pad 6", "Pad 7", "Pad 8", "Pad 9", "Pad >= 10"
};
- double position;
+ double position, delta;
double number;
switch (libinput_event_get_type(ev)) {
@@ -1748,6 +1766,14 @@ handle_event_tablet_pad(struct libinput_event *ev, struct window *w)
w->pad.strip.number = number;
w->pad.strip.position = position;
break;
+ case LIBINPUT_EVENT_TABLET_PAD_DIAL:
+ delta = libinput_event_tablet_pad_get_dial_delta_v120(p);
+ number = libinput_event_tablet_pad_get_dial_number(p);
+ if (w->pad.dial.number != number)
+ w->pad.dial.position = -delta;
+ w->pad.dial.number = number;
+ w->pad.dial.position += delta;
+ break;
default:
abort();
}
@@ -1825,6 +1851,7 @@ handle_event_libinput(GIOChannel *source, GIOCondition condition, gpointer data)
case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
case LIBINPUT_EVENT_TABLET_PAD_RING:
case LIBINPUT_EVENT_TABLET_PAD_STRIP:
+ case LIBINPUT_EVENT_TABLET_PAD_DIAL:
handle_event_tablet_pad(ev, w);
break;
case LIBINPUT_EVENT_TABLET_PAD_KEY:
diff --git a/tools/libinput-debug-gui.man b/tools/libinput-debug-gui.man
index 7cfe64bb..6865ea93 100644
--- a/tools/libinput-debug-gui.man
+++ b/tools/libinput-debug-gui.man
@@ -91,9 +91,10 @@ displayed on press.
.TP 8
.B Tablet pads
Button events are displayed in the bottom-most button oblong, with the name
-of the button displayed on press. Ring and strip events are displayed in the
-yellow 'IO' symbol, with the position and the number of the ring/strip
-filled in when events are available.
+of the button displayed on press. Dials, ring and strip events are displayed in
+the yellow 'IO' symbol, with the position of the ring or strip or the
+delta of the dial filled in when events are available. The number of the dial,
+ring or strip is displayed when an event is available.
.TP 8
.B Kernel events
Left of the center is a blue ring to debug kernel relative events (REL_X and