mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-28 09:40:07 +01:00
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>
690 lines
17 KiB
C
690 lines
17 KiB
C
/*
|
|
* Copyright © 2016 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 <limits.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "evdev-tablet-pad.h"
|
|
|
|
#if HAVE_LIBWACOM
|
|
#include <libwacom/libwacom.h>
|
|
#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 libinput_tablet_pad_mode_group base;
|
|
struct list led_list;
|
|
struct list toggle_button_list;
|
|
};
|
|
|
|
struct pad_mode_toggle_button {
|
|
struct list link;
|
|
unsigned int button_index;
|
|
enum pad_toggle_button_target_mode target_mode;
|
|
};
|
|
|
|
struct pad_mode_led {
|
|
struct list link;
|
|
/* /sys/devices/..../input1235/input1235::wacom-led_0.1/brightness */
|
|
int brightness_fd;
|
|
int mode_idx;
|
|
};
|
|
|
|
static inline void
|
|
pad_mode_toggle_button_destroy(struct pad_mode_toggle_button* button)
|
|
{
|
|
list_remove(&button->link);
|
|
free(button);
|
|
}
|
|
|
|
static inline int
|
|
pad_led_group_get_mode(struct pad_led_group *group)
|
|
{
|
|
char buf[4] = {0};
|
|
int rc;
|
|
unsigned int brightness;
|
|
struct pad_mode_led *led;
|
|
|
|
list_for_each(led, &group->led_list, link) {
|
|
rc = lseek(led->brightness_fd, 0, SEEK_SET);
|
|
if (rc == -1)
|
|
return -errno;
|
|
|
|
rc = read(led->brightness_fd, buf, sizeof(buf) - 1);
|
|
if (rc == -1)
|
|
return -errno;
|
|
|
|
rc = sscanf(buf, "%u\n", &brightness);
|
|
if (rc != 1)
|
|
return -EINVAL;
|
|
|
|
/* Assumption: only one LED lit up at any time */
|
|
if (brightness != 0)
|
|
return led->mode_idx;
|
|
}
|
|
|
|
/* Wacom PTH-660 doesn't light up any LEDs
|
|
* until the button is pressed, so let's assume mode 0 */
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
pad_led_destroy(struct libinput *libinput,
|
|
struct pad_mode_led *led)
|
|
{
|
|
list_remove(&led->link);
|
|
if (led->brightness_fd != -1)
|
|
close_restricted(libinput, led->brightness_fd);
|
|
free(led);
|
|
}
|
|
|
|
#if HAVE_LIBWACOM
|
|
static inline struct pad_mode_led *
|
|
pad_led_new(struct libinput *libinput, const char *prefix, int group, int mode)
|
|
{
|
|
struct pad_mode_led *led;
|
|
char path[PATH_MAX];
|
|
int rc, fd, save_errno;
|
|
|
|
led = zalloc(sizeof *led);
|
|
led->brightness_fd = -1;
|
|
led->mode_idx = mode;
|
|
list_init(&led->link);
|
|
|
|
/* /sys/devices/..../input1235/input1235::wacom-0.1/brightness,
|
|
* where 0 and 1 are group and mode index. */
|
|
rc = snprintf(path,
|
|
sizeof(path),
|
|
"%s%d.%d/brightness",
|
|
prefix,
|
|
group,
|
|
mode);
|
|
if (rc == -1)
|
|
goto error;
|
|
|
|
fd = open_restricted(libinput, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
|
|
if (fd < 0) {
|
|
errno = -fd;
|
|
goto error;
|
|
}
|
|
|
|
led->brightness_fd = fd;
|
|
|
|
return led;
|
|
|
|
error:
|
|
save_errno = errno;
|
|
pad_led_destroy(libinput, led);
|
|
errno = save_errno;
|
|
return NULL;
|
|
}
|
|
#endif /* HAVE_LIBWACOM */
|
|
|
|
static void
|
|
pad_led_group_destroy(struct libinput_tablet_pad_mode_group *g)
|
|
{
|
|
struct pad_led_group *group = (struct pad_led_group *)g;
|
|
struct pad_mode_toggle_button *button;
|
|
struct pad_mode_led *led;
|
|
|
|
list_for_each_safe(button, &group->toggle_button_list, link)
|
|
pad_mode_toggle_button_destroy(button);
|
|
|
|
list_for_each_safe(led, &group->led_list, link)
|
|
pad_led_destroy(g->device->seat->libinput, led);
|
|
|
|
free(group);
|
|
}
|
|
|
|
static struct pad_led_group *
|
|
pad_group_new(struct pad_dispatch *pad,
|
|
unsigned int group_index,
|
|
int num_modes)
|
|
{
|
|
struct pad_led_group *group;
|
|
|
|
group = zalloc(sizeof *group);
|
|
group->base.device = &pad->device->base;
|
|
group->base.refcount = 1;
|
|
group->base.index = group_index;
|
|
group->base.current_mode = 0;
|
|
group->base.num_modes = num_modes;
|
|
group->base.destroy = pad_led_group_destroy;
|
|
list_init(&group->toggle_button_list);
|
|
list_init(&group->led_list);
|
|
|
|
return group;
|
|
}
|
|
|
|
static inline struct libinput_tablet_pad_mode_group *
|
|
pad_get_mode_group(struct pad_dispatch *pad, unsigned int index)
|
|
{
|
|
struct libinput_tablet_pad_mode_group *group;
|
|
|
|
list_for_each(group, &pad->modes.mode_group_list, link) {
|
|
if (group->index == index)
|
|
return group;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#if HAVE_LIBWACOM
|
|
static inline bool
|
|
is_litest_device(struct evdev_device *device)
|
|
{
|
|
return !!udev_device_get_property_value(device->udev_device,
|
|
"LIBINPUT_TEST_DEVICE");
|
|
}
|
|
|
|
static inline struct pad_mode_toggle_button *
|
|
pad_mode_toggle_button_new(unsigned int button_index)
|
|
{
|
|
struct pad_mode_toggle_button *button;
|
|
|
|
button = zalloc(sizeof *button);
|
|
button->button_index = button_index;
|
|
button->target_mode = MODE_NEXT;
|
|
|
|
return button;
|
|
}
|
|
|
|
static int
|
|
pad_led_group_add_toggle_button(struct pad_led_group *group,
|
|
int button_index,
|
|
enum pad_toggle_button_target_mode mode)
|
|
{
|
|
struct pad_mode_toggle_button *button;
|
|
|
|
button = pad_mode_toggle_button_new(button_index);
|
|
if (!button)
|
|
return -ENOMEM;
|
|
|
|
button->target_mode = mode;
|
|
list_append(&group->toggle_button_list, &button->link);
|
|
group->base.toggle_button_mask |= bit(button_index);
|
|
group->base.button_mask |= bit(button_index);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline bool
|
|
pad_led_get_sysfs_base_path(struct evdev_device *device,
|
|
char *path_out,
|
|
size_t path_out_sz)
|
|
{
|
|
struct udev_device *parent, *udev_device;
|
|
const char *test_path;
|
|
int rc;
|
|
|
|
udev_device = device->udev_device;
|
|
|
|
/* For testing purposes only allow for a base path set through a
|
|
* udev rule. We still expect the normal directory hierarchy inside */
|
|
test_path = udev_device_get_property_value(udev_device,
|
|
"LIBINPUT_TEST_TABLET_PAD_SYSFS_PATH");
|
|
if (test_path) {
|
|
rc = snprintf(path_out, path_out_sz, "%s", test_path);
|
|
return rc != -1;
|
|
}
|
|
|
|
parent = udev_device_get_parent_with_subsystem_devtype(udev_device,
|
|
"input",
|
|
NULL);
|
|
if (!parent)
|
|
return false;
|
|
|
|
rc = snprintf(path_out,
|
|
path_out_sz,
|
|
"%s/%s::wacom-",
|
|
udev_device_get_syspath(parent),
|
|
udev_device_get_sysname(parent));
|
|
|
|
return rc != -1;
|
|
}
|
|
|
|
static int
|
|
pad_add_mode_group(struct pad_dispatch *pad,
|
|
struct evdev_device *device,
|
|
unsigned int group_index,
|
|
int nmodes,
|
|
int button_index,
|
|
enum pad_toggle_button_target_mode mode,
|
|
uint32_t ring_mask,
|
|
uint32_t strip_mask,
|
|
uint32_t dial_mask,
|
|
bool create_leds)
|
|
{
|
|
struct libinput *li = pad_libinput_context(pad);
|
|
struct pad_led_group *group = NULL;
|
|
int rc = -ENOMEM;
|
|
char syspath[PATH_MAX];
|
|
|
|
/* syspath is /sys/class/leds/input1234/input12345::wacom-" and
|
|
only needs the group + mode appended */
|
|
if (!pad_led_get_sysfs_base_path(device, syspath, sizeof(syspath)))
|
|
return -ENOMEM;
|
|
|
|
group = pad_group_new(pad, group_index, nmodes);
|
|
if (!group)
|
|
goto out;
|
|
group->base.ring_mask = ring_mask;
|
|
group->base.strip_mask = strip_mask;
|
|
group->base.dial_mask = dial_mask;
|
|
group->base.button_mask |= bit(button_index);
|
|
|
|
rc = pad_led_group_add_toggle_button(group, button_index, mode);
|
|
if (rc < 0)
|
|
goto out;
|
|
|
|
for (int mode = 0; create_leds && mode < nmodes; mode++) {
|
|
struct pad_mode_led *led;
|
|
|
|
led = pad_led_new(li, syspath, group_index, mode);
|
|
if (!led) {
|
|
rc = -errno;
|
|
goto out;
|
|
}
|
|
list_append(&group->led_list, &led->link);
|
|
}
|
|
|
|
if (create_leds) {
|
|
rc = pad_led_group_get_mode(group);
|
|
if (rc < 0) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
list_insert(&pad->modes.mode_group_list, &group->base.link);
|
|
rc = 0;
|
|
out:
|
|
if (rc)
|
|
pad_led_group_destroy(&group->base);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
pad_fetch_group_index(struct pad_dispatch *pad,
|
|
WacomDevice *wacom,
|
|
int button_index)
|
|
{
|
|
char btn = 'A' + button_index;
|
|
WacomButtonFlags flags = libwacom_get_button_flag(wacom, btn);
|
|
int led_group = libwacom_get_button_led_group(wacom, btn);
|
|
|
|
if ((flags & WACOM_BUTTON_MODESWITCH) == 0) {
|
|
evdev_log_bug_libinput(pad->device,
|
|
"Cannot fetch group index for non-mode toggle button %c\n", btn);
|
|
return -1;
|
|
}
|
|
|
|
if (led_group >= 0)
|
|
return led_group;
|
|
|
|
/* Note on group_index: libwacom gives us the led_group
|
|
* but this is really just the index of the entry in
|
|
* the StatusLEDs line. This is effectively always
|
|
* 0 for the first ring or strip and 1 for the second
|
|
* ring or strip. The few devices that matter
|
|
* have either a pair of rings or strips, not mixed.
|
|
*
|
|
* Which means for devices where we don't have StatusLEDs
|
|
* we can hardcode this behavior, if we ever get a ring+strip
|
|
* devcice we need to update libwacom for that anyway.
|
|
*/
|
|
|
|
int group_index = -1;
|
|
switch (flags & WACOM_BUTTON_MODESWITCH) {
|
|
case WACOM_BUTTON_RING_MODESWITCH:
|
|
group_index = 0;
|
|
break;
|
|
case WACOM_BUTTON_RING2_MODESWITCH:
|
|
group_index = 1;
|
|
break;
|
|
case WACOM_BUTTON_TOUCHSTRIP_MODESWITCH:
|
|
group_index = 0;
|
|
break;
|
|
case WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH:
|
|
group_index = 1;
|
|
break;
|
|
#ifdef HAVE_LIBWACOM_BUTTON_DIAL_MODESWITCH
|
|
case WACOM_BUTTON_DIAL_MODESWITCH:
|
|
group_index = 0;
|
|
break;
|
|
case WACOM_BUTTON_DIAL2_MODESWITCH:
|
|
group_index = 1;
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
return group_index;
|
|
}
|
|
|
|
static inline int
|
|
pad_find_button_group(struct pad_dispatch *pad,
|
|
WacomDevice *wacom,
|
|
int button_index,
|
|
WacomButtonFlags button_flags)
|
|
{
|
|
int i;
|
|
WacomButtonFlags flags;
|
|
|
|
for (i = 0; i < libwacom_get_num_buttons(wacom); i++) {
|
|
if (i == button_index)
|
|
continue;
|
|
|
|
flags = libwacom_get_button_flag(wacom, 'A' + i);
|
|
if ((flags & WACOM_BUTTON_MODESWITCH) == 0)
|
|
continue;
|
|
|
|
if ((flags & WACOM_BUTTON_DIRECTION) ==
|
|
(button_flags & WACOM_BUTTON_DIRECTION))
|
|
return pad_fetch_group_index(pad, wacom, i);
|
|
}
|
|
|
|
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
|
|
pad_init_leds_from_libwacom(struct pad_dispatch *pad,
|
|
struct evdev_device *device,
|
|
WacomDevice *wacom)
|
|
{
|
|
int rc = -EINVAL;
|
|
|
|
if (!wacom)
|
|
return -ENOENT;
|
|
|
|
for (int b = 0; b < libwacom_get_num_buttons(wacom); b++) {
|
|
char btn = 'A' + b;
|
|
WacomButtonFlags flags = libwacom_get_button_flag(wacom, btn);
|
|
int nmodes = 0;
|
|
uint32_t ring_mask = 0;
|
|
uint32_t strip_mask = 0;
|
|
uint32_t dial_mask = 0;
|
|
bool have_status_led = false;
|
|
|
|
if ((flags & WACOM_BUTTON_MODESWITCH) == 0)
|
|
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);
|
|
switch (flags & WACOM_BUTTON_MODESWITCH) {
|
|
case WACOM_BUTTON_RING_MODESWITCH:
|
|
nmodes = libwacom_get_ring_num_modes(wacom);
|
|
ring_mask = 0x1;
|
|
break;
|
|
case WACOM_BUTTON_RING2_MODESWITCH:
|
|
nmodes = libwacom_get_ring2_num_modes(wacom);
|
|
ring_mask = 0x2;
|
|
break;
|
|
case WACOM_BUTTON_TOUCHSTRIP_MODESWITCH:
|
|
nmodes = libwacom_get_strips_num_modes(wacom);
|
|
strip_mask = 0x1;
|
|
break;
|
|
case WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH:
|
|
/* there is no get_strips2_... */
|
|
nmodes = libwacom_get_strips_num_modes(wacom);
|
|
strip_mask = 0x2;
|
|
break;
|
|
#ifdef HAVE_LIBWACOM_BUTTON_DIAL_MODESWITCH
|
|
case WACOM_BUTTON_DIAL_MODESWITCH:
|
|
nmodes = libwacom_get_dial_num_modes(wacom);
|
|
dial_mask = 0x1;
|
|
break;
|
|
case WACOM_BUTTON_DIAL2_MODESWITCH:
|
|
nmodes = libwacom_get_dial2_num_modes(wacom);
|
|
dial_mask = 0x2;
|
|
break;
|
|
#endif
|
|
default:
|
|
evdev_log_error(pad->device,
|
|
"unable to init pad mode group: button %c has multiple modeswitch flags 0x%x\n",
|
|
btn, flags);
|
|
goto out;
|
|
}
|
|
have_status_led = libwacom_get_button_led_group(wacom, btn) >= 0;
|
|
if (nmodes > 1) {
|
|
struct libinput_tablet_pad_mode_group *group = pad_get_mode_group(pad, group_index);
|
|
if (!group) {
|
|
rc = pad_add_mode_group(pad, device, group_index, nmodes, b, target_mode,
|
|
ring_mask, strip_mask, dial_mask,
|
|
have_status_led);
|
|
} else {
|
|
struct pad_led_group *led_group = (struct pad_led_group *)group;
|
|
/* Multiple toggle buttons (Wacom MobileStudio Pro 16) */
|
|
rc = pad_led_group_add_toggle_button(led_group, b, target_mode);
|
|
if (rc < 0)
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (list_empty(&pad->modes.mode_group_list)) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* Now loop again to match the other buttons with the existing
|
|
* mode groups */
|
|
for (int b = 0; b < libwacom_get_num_buttons(wacom); b++) {
|
|
char btn = 'A' + b;
|
|
WacomButtonFlags flags = libwacom_get_button_flag(wacom, btn);
|
|
|
|
if ((flags & WACOM_BUTTON_MODESWITCH))
|
|
continue;
|
|
|
|
int group_index = pad_find_button_group(pad, wacom, b, flags);
|
|
if (group_index == -1) {
|
|
evdev_log_bug_libinput(pad->device,
|
|
"unhandled position for button %i\n",
|
|
b);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
struct libinput_tablet_pad_mode_group *group = pad_get_mode_group(pad, group_index);
|
|
if (!group) {
|
|
evdev_log_bug_libinput(pad->device,
|
|
"Failed to find group %d for button %i\n",
|
|
group_index,
|
|
b);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
group->button_mask |= bit(b);
|
|
}
|
|
|
|
rc = 0;
|
|
out:
|
|
if (rc != 0) {
|
|
if (rc == -ENOENT && is_litest_device(pad->device)) {
|
|
evdev_log_error(pad->device,
|
|
"unable to init pad mode group: %s\n",
|
|
strerror(-rc));
|
|
}
|
|
pad_destroy_leds(pad);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
#endif /* HAVE_LIBWACOM */
|
|
|
|
static int
|
|
pad_init_fallback_group(struct pad_dispatch *pad)
|
|
{
|
|
struct pad_led_group *group;
|
|
|
|
group = pad_group_new(pad, 0, 1);
|
|
if (!group)
|
|
return 1;
|
|
|
|
/* If we only have one group, all buttons/strips/rings are part of
|
|
* that group. We rely on the other layers to filter out invalid
|
|
* indices */
|
|
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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pad_init_leds(struct pad_dispatch *pad,
|
|
struct evdev_device *device,
|
|
WacomDevice *wacom)
|
|
{
|
|
int rc = 1;
|
|
|
|
list_init(&pad->modes.mode_group_list);
|
|
|
|
if (pad->nbuttons > 32) {
|
|
evdev_log_bug_libinput(pad->device,
|
|
"Too many pad buttons for modes %d\n",
|
|
pad->nbuttons);
|
|
return rc;
|
|
}
|
|
|
|
/* If libwacom fails, we init one fallback group anyway */
|
|
#if HAVE_LIBWACOM
|
|
rc = pad_init_leds_from_libwacom(pad, device, wacom);
|
|
#endif
|
|
if (rc != 0)
|
|
rc = pad_init_fallback_group(pad);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void
|
|
pad_destroy_leds(struct pad_dispatch *pad)
|
|
{
|
|
struct libinput_tablet_pad_mode_group *group;
|
|
|
|
list_for_each_safe(group, &pad->modes.mode_group_list, link)
|
|
libinput_tablet_pad_mode_group_unref(group);
|
|
}
|
|
|
|
void
|
|
pad_button_update_mode(struct libinput_tablet_pad_mode_group *g,
|
|
unsigned int button_index,
|
|
enum libinput_button_state state)
|
|
{
|
|
struct pad_led_group *group = (struct pad_led_group*)g;
|
|
int rc = -ENODEV;
|
|
|
|
if (state != LIBINPUT_BUTTON_STATE_PRESSED)
|
|
return;
|
|
|
|
if (!libinput_tablet_pad_mode_group_button_is_toggle(g, button_index))
|
|
return;
|
|
|
|
if (list_empty(&group->led_list)) {
|
|
struct pad_mode_toggle_button *button;
|
|
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 {
|
|
rc = pad_led_group_get_mode(group);
|
|
}
|
|
if (rc >= 0)
|
|
group->base.current_mode = rc;
|
|
}
|
|
|
|
int
|
|
evdev_device_tablet_pad_get_num_mode_groups(struct evdev_device *device)
|
|
{
|
|
struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch;
|
|
struct libinput_tablet_pad_mode_group *group;
|
|
int num_groups = 0;
|
|
|
|
if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
|
|
return -1;
|
|
|
|
list_for_each(group, &pad->modes.mode_group_list, link)
|
|
num_groups++;
|
|
|
|
return num_groups;
|
|
}
|
|
|
|
struct libinput_tablet_pad_mode_group *
|
|
evdev_device_tablet_pad_get_mode_group(struct evdev_device *device,
|
|
unsigned int index)
|
|
{
|
|
struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch;
|
|
|
|
if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
|
|
return NULL;
|
|
|
|
if (index >=
|
|
(unsigned int)evdev_device_tablet_pad_get_num_mode_groups(device))
|
|
return NULL;
|
|
|
|
return pad_get_mode_group(pad, index);
|
|
}
|