pad: fix handling of direct mode switch buttons

A tablet with multiple mode toggle buttons had each mode toggle button
merely cycle to the next mode in the sequence, removing the whole point
of having multiple toggle buttons.

Fix this by defaulting each mode toggle button to "next". Once we
have initialized all buttons we can check if we have multiple buttons -
if so we number them sequentially so that the first button maps to mode
0, the second maps to mode 1, etc.

Closes #1082

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1132>
This commit is contained in:
Peter Hutterer 2025-01-31 15:53:18 +10:00 committed by Marge Bot
parent 9f98e6d573
commit 39fa451e7e
3 changed files with 74 additions and 11 deletions

View file

@ -174,6 +174,10 @@ if have_libwacom
dependencies : dep_libwacom) dependencies : dep_libwacom)
config_h.set('HAVE_LIBWACOM_BUTTON_DIAL_MODESWITCH', '1') config_h.set('HAVE_LIBWACOM_BUTTON_DIAL_MODESWITCH', '1')
endif endif
if cc.has_function('libwacom_get_button_modeswitch_mode',
dependencies: dep_libwacom)
config_h.set('HAVE_LIBWACOM_BUTTON_MODESWITCH_MODE', '1')
endif
else else
dep_libwacom = declare_dependency() dep_libwacom = declare_dependency()
endif endif

View file

@ -32,6 +32,22 @@
#include <libwacom/libwacom.h> #include <libwacom/libwacom.h>
#endif #endif
/* We only have 4 modes on current devices but
* anything other than MODE_NEXT is used as
* numeric value anyway so it doesn't matter if a value is
* missing here
*
* Once libwacom 2.15 is commonplace we can use the
* libwacom enum directly.
*/
enum pad_toggle_button_target_mode {
MODE_NEXT = -1,
MODE_0,
MODE_1,
MODE_2,
MODE_3,
};
struct pad_led_group { struct pad_led_group {
struct libinput_tablet_pad_mode_group base; struct libinput_tablet_pad_mode_group base;
struct list led_list; struct list led_list;
@ -41,6 +57,7 @@ struct pad_led_group {
struct pad_mode_toggle_button { struct pad_mode_toggle_button {
struct list link; struct list link;
unsigned int button_index; unsigned int button_index;
enum pad_toggle_button_target_mode target_mode;
}; };
struct pad_mode_led { struct pad_mode_led {
@ -204,13 +221,15 @@ pad_mode_toggle_button_new(unsigned int button_index)
button = zalloc(sizeof *button); button = zalloc(sizeof *button);
button->button_index = button_index; button->button_index = button_index;
button->target_mode = MODE_NEXT;
return button; return button;
} }
static int static int
pad_led_group_add_toggle_button(struct pad_led_group *group, pad_led_group_add_toggle_button(struct pad_led_group *group,
int button_index) int button_index,
enum pad_toggle_button_target_mode mode)
{ {
struct pad_mode_toggle_button *button; struct pad_mode_toggle_button *button;
@ -218,7 +237,8 @@ pad_led_group_add_toggle_button(struct pad_led_group *group,
if (!button) if (!button)
return -ENOMEM; return -ENOMEM;
list_insert(&group->toggle_button_list, &button->link); button->target_mode = mode;
list_append(&group->toggle_button_list, &button->link);
group->base.toggle_button_mask |= bit(button_index); group->base.toggle_button_mask |= bit(button_index);
group->base.button_mask |= bit(button_index); group->base.button_mask |= bit(button_index);
@ -266,6 +286,7 @@ pad_add_mode_group(struct pad_dispatch *pad,
unsigned int group_index, unsigned int group_index,
int nmodes, int nmodes,
int button_index, int button_index,
enum pad_toggle_button_target_mode mode,
uint32_t ring_mask, uint32_t ring_mask,
uint32_t strip_mask, uint32_t strip_mask,
uint32_t dial_mask, uint32_t dial_mask,
@ -289,7 +310,7 @@ pad_add_mode_group(struct pad_dispatch *pad,
group->base.dial_mask = dial_mask; group->base.dial_mask = dial_mask;
group->base.button_mask |= bit(button_index); group->base.button_mask |= bit(button_index);
rc = pad_led_group_add_toggle_button(group, button_index); rc = pad_led_group_add_toggle_button(group, button_index, mode);
if (rc < 0) if (rc < 0)
goto out; goto out;
@ -402,6 +423,21 @@ pad_find_button_group(struct pad_dispatch *pad,
return -1; return -1;
} }
static enum pad_toggle_button_target_mode
pad_button_target_mode(WacomDevice *wacom, char button)
{
#ifdef HAVE_LIBWACOM_BUTTON_MODESWITCH_MODE
switch (libwacom_get_button_modeswitch_mode(wacom, button)) {
case WACOM_MODE_SWITCH_NEXT: return MODE_NEXT;
case WACOM_MODE_SWITCH_0: return MODE_0;
case WACOM_MODE_SWITCH_1: return MODE_1;
case WACOM_MODE_SWITCH_2: return MODE_2;
case WACOM_MODE_SWITCH_3: return MODE_3;
}
#endif
return MODE_NEXT;
}
static int static int
pad_init_leds_from_libwacom(struct pad_dispatch *pad, pad_init_leds_from_libwacom(struct pad_dispatch *pad,
struct evdev_device *device, struct evdev_device *device,
@ -424,6 +460,8 @@ pad_init_leds_from_libwacom(struct pad_dispatch *pad,
if ((flags & WACOM_BUTTON_MODESWITCH) == 0) if ((flags & WACOM_BUTTON_MODESWITCH) == 0)
continue; continue;
enum pad_toggle_button_target_mode target_mode = pad_button_target_mode(wacom, btn);
int group_index = pad_fetch_group_index(pad, wacom, b); int group_index = pad_fetch_group_index(pad, wacom, b);
switch (flags & WACOM_BUTTON_MODESWITCH) { switch (flags & WACOM_BUTTON_MODESWITCH) {
case WACOM_BUTTON_RING_MODESWITCH: case WACOM_BUTTON_RING_MODESWITCH:
@ -463,13 +501,13 @@ pad_init_leds_from_libwacom(struct pad_dispatch *pad,
if (nmodes > 1) { if (nmodes > 1) {
struct libinput_tablet_pad_mode_group *group = pad_get_mode_group(pad, group_index); struct libinput_tablet_pad_mode_group *group = pad_get_mode_group(pad, group_index);
if (!group) { if (!group) {
rc = pad_add_mode_group(pad, device, group_index, nmodes, b, rc = pad_add_mode_group(pad, device, group_index, nmodes, b, target_mode,
ring_mask, strip_mask, dial_mask, ring_mask, strip_mask, dial_mask,
have_status_led); have_status_led);
} else { } else {
struct pad_led_group *led_group = (struct pad_led_group *)group; struct pad_led_group *led_group = (struct pad_led_group *)group;
/* Multiple toggle buttons (Wacom MobileStudio Pro 16) */ /* Multiple toggle buttons (Wacom MobileStudio Pro 16) */
rc = pad_led_group_add_toggle_button(led_group, b); rc = pad_led_group_add_toggle_button(led_group, b, target_mode);
if (rc < 0) if (rc < 0)
goto out; goto out;
} }
@ -591,7 +629,7 @@ pad_button_update_mode(struct libinput_tablet_pad_mode_group *g,
enum libinput_button_state state) enum libinput_button_state state)
{ {
struct pad_led_group *group = (struct pad_led_group*)g; struct pad_led_group *group = (struct pad_led_group*)g;
int rc; int rc = -ENODEV;
if (state != LIBINPUT_BUTTON_STATE_PRESSED) if (state != LIBINPUT_BUTTON_STATE_PRESSED)
return; return;
@ -600,9 +638,18 @@ pad_button_update_mode(struct libinput_tablet_pad_mode_group *g,
return; return;
if (list_empty(&group->led_list)) { if (list_empty(&group->led_list)) {
unsigned int nmodes = group->base.num_modes; struct pad_mode_toggle_button *button;
rc = (group->base.current_mode + 1) % nmodes; list_for_each(button, &group->toggle_button_list, link) {
if (button->button_index == button_index) {
if (button->target_mode == MODE_NEXT) {
unsigned int nmodes = group->base.num_modes;
rc = (group->base.current_mode + 1) % nmodes;
} else {
rc = button->target_mode;
}
break;
}
}
} else { } else {
rc = pad_led_group_get_mode(group); rc = pad_led_group_get_mode(group);
} }

View file

@ -383,12 +383,24 @@ START_TEST(pad_button_mode_groups)
pev = libinput_event_get_tablet_pad_event(ev); pev = libinput_event_get_tablet_pad_event(ev);
group = libinput_event_tablet_pad_get_mode_group(pev); group = libinput_event_tablet_pad_get_mode_group(pev);
#if HAVE_LIBWACOM #ifdef HAVE_LIBWACOM_BUTTON_MODESWITCH_MODE
/* One of the few devices that has very specific mode switch buttons
* so we hardcode these here in the test */
if (dev->which == LITEST_WACOM_MOBILESTUDIO_PRO_16_PAD) {
switch (code) {
case BTN_9: expected_mode = 0; break;
case BTN_A: expected_mode = 1; break;
case BTN_B: expected_mode = 2; break;
case BTN_C: expected_mode = 3; break;
default:
break;
}
} else
#endif
if (libinput_tablet_pad_mode_group_button_is_toggle(group, b)) { if (libinput_tablet_pad_mode_group_button_is_toggle(group, b)) {
int num_modes = libinput_tablet_pad_mode_group_get_num_modes(group); int num_modes = libinput_tablet_pad_mode_group_get_num_modes(group);
expected_mode = (expected_mode + 1) % num_modes; expected_mode = (expected_mode + 1) % num_modes;
} }
#endif
mode = libinput_event_tablet_pad_get_mode(pev); mode = libinput_event_tablet_pad_get_mode(pev);
litest_assert_int_eq(mode, expected_mode); litest_assert_int_eq(mode, expected_mode);