}
static struct pad_led_group *
-pad_group_new_basic(struct pad_dispatch *pad,
+pad_group_new(struct pad_dispatch *pad,
unsigned int group_index,
int num_modes)
{
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)
"LIBINPUT_TEST_DEVICE");
}
-static inline struct pad_led_group *
-pad_group_new(struct pad_dispatch *pad,
- unsigned int group_index,
- int nleds,
- const char *syspath)
+static inline struct pad_mode_toggle_button *
+pad_mode_toggle_button_new(unsigned int button_index)
{
- struct libinput *libinput = pad->device->base.seat->libinput;
- struct pad_led_group *group;
- int rc;
-
- group = pad_group_new_basic(pad, group_index, nleds);
- if (!group)
- return NULL;
-
- while (nleds--) {
- struct pad_mode_led *led;
+ struct pad_mode_toggle_button *button;
- led = pad_led_new(libinput, syspath, group_index, nleds);
- if (!led) {
- rc = -errno;
- goto error;
- }
- list_insert(&group->led_list, &led->link);
- }
+ button = zalloc(sizeof *button);
+ button->button_index = button_index;
- rc = pad_led_group_get_mode(group);
- if (rc < 0) {
- goto error;
- }
+ return button;
+}
- group->base.current_mode = rc;
+static int
+pad_led_group_add_toggle_button(struct pad_led_group *group,
+ int button_index)
+{
+ struct pad_mode_toggle_button *button;
- return group;
+ button = pad_mode_toggle_button_new(button_index);
+ if (!button)
+ return -ENOMEM;
-error:
- if (!is_litest_device(pad->device))
- evdev_log_error(pad->device,
- "unable to init LED group: %s\n",
- strerror(-rc));
- pad_led_group_destroy(&group->base);
+ list_insert(&group->toggle_button_list, &button->link);
+ group->base.toggle_button_mask |= bit(button_index);
+ group->base.button_mask |= bit(button_index);
- return NULL;
+ return 0;
}
static inline bool
}
static int
-pad_init_led_groups(struct pad_dispatch *pad,
- struct evdev_device *device,
- WacomDevice *wacom)
+pad_add_mode_group(struct pad_dispatch *pad,
+ struct evdev_device *device,
+ unsigned int group_index,
+ int nmodes,
+ int button_index,
+ uint32_t ring_mask,
+ uint32_t strip_mask,
+ bool create_leds)
{
- const WacomStatusLEDs *leds;
- int nleds, nmodes;
- int i;
- struct pad_led_group *group;
+ struct libinput *li = pad_libinput_context(pad);
+ struct pad_led_group *group = NULL;
+ int rc = -ENOMEM;
char syspath[PATH_MAX];
- leds = libwacom_get_status_leds(wacom, &nleds);
- if (nleds == 0)
- return 1;
-
/* 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 1;
+ return -ENOMEM;
- for (i = 0; i < nleds; i++) {
- switch(leds[i]) {
- case WACOM_STATUS_LED_UNAVAILABLE:
- evdev_log_bug_libinput(device,
- "Invalid led type %d\n",
- leds[i]);
- return 1;
- case WACOM_STATUS_LED_RING:
- nmodes = libwacom_get_ring_num_modes(wacom);
- group = pad_group_new(pad, i, nmodes, syspath);
- if (!group)
- return 1;
- list_insert(&pad->modes.mode_group_list, &group->base.link);
- break;
- case WACOM_STATUS_LED_RING2:
- nmodes = libwacom_get_ring2_num_modes(wacom);
- group = pad_group_new(pad, i, nmodes, syspath);
- if (!group)
- return 1;
- list_insert(&pad->modes.mode_group_list, &group->base.link);
- break;
- case WACOM_STATUS_LED_TOUCHSTRIP:
- nmodes = libwacom_get_strips_num_modes(wacom);
- group = pad_group_new(pad, i, nmodes, syspath);
- if (!group)
- return 1;
- list_insert(&pad->modes.mode_group_list, &group->base.link);
- break;
- case WACOM_STATUS_LED_TOUCHSTRIP2:
- /* there is no get_strips2_... */
- nmodes = libwacom_get_strips_num_modes(wacom);
- group = pad_group_new(pad, i, nmodes, syspath);
- if (!group)
- return 1;
- list_insert(&pad->modes.mode_group_list, &group->base.link);
- break;
- }
- }
+ 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.button_mask |= bit(button_index);
- return 0;
-}
+ rc = pad_led_group_add_toggle_button(group, button_index);
+ if (rc < 0)
+ goto out;
-#endif
+ for (int mode = 0; create_leds && mode < nmodes; mode++) {
+ struct pad_mode_led *led;
-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;
+ led = pad_led_new(li, syspath, group_index, mode);
+ if (!led) {
+ rc = -errno;
+ goto out;
+ }
+ list_append(&group->led_list, &led->link);
+ }
- list_for_each(group, &pad->modes.mode_group_list, link) {
- if (group->index == index)
- return group;
+ if (create_leds) {
+ rc = pad_led_group_get_mode(group);
+ if (rc < 0) {
+ goto out;
+ }
}
- return NULL;
-}
+ list_insert(&pad->modes.mode_group_list, &group->base.link);
+ rc = 0;
+out:
+ if (rc)
+ pad_led_group_destroy(&group->base);
-#if HAVE_LIBWACOM
+ return rc;
+}
-static inline struct pad_mode_toggle_button *
-pad_mode_toggle_button_new(struct pad_dispatch *pad,
- unsigned int button_index)
+static int
+pad_fetch_group_index(struct pad_dispatch *pad,
+ WacomDevice *wacom,
+ int button_index)
{
- struct pad_mode_toggle_button *button;
+ char btn = 'A' + button_index;
+ WacomButtonFlags flags = libwacom_get_button_flag(wacom, btn);
+ int led_group = libwacom_get_button_led_group(wacom, btn);
- button = zalloc(sizeof *button);
- button->button_index = button_index;
+ 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;
+ }
- return button;
+ 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;
+ }
+
+ return group_index;
}
static inline int
-pad_find_button_group(WacomDevice *wacom,
+pad_find_button_group(struct pad_dispatch *pad,
+ WacomDevice *wacom,
int button_index,
WacomButtonFlags button_flags)
{
if ((flags & WACOM_BUTTON_DIRECTION) ==
(button_flags & WACOM_BUTTON_DIRECTION))
- return libwacom_get_button_led_group(wacom, 'A' + i);
+ return pad_fetch_group_index(pad, wacom, i);
}
return -1;
}
-static int
-pad_init_mode_buttons(struct pad_dispatch *pad,
- WacomDevice *wacom)
-{
- struct libinput_tablet_pad_mode_group *group;
- unsigned int group_idx;
- int i;
- WacomButtonFlags flags;
-
- /* libwacom numbers buttons as 'A', 'B', etc. We number them with 0,
- * 1, ...
- */
- for (i = 0; i < libwacom_get_num_buttons(wacom); i++) {
- group_idx = libwacom_get_button_led_group(wacom, 'A' + i);
- flags = libwacom_get_button_flag(wacom, 'A' + i);
-
- /* If this button is not a mode toggle button, find the mode
- * toggle button with the same position flags and take that
- * button's group idx */
- if ((int)group_idx == -1) {
- group_idx = pad_find_button_group(wacom, i, flags);
- }
-
- if ((int)group_idx == -1) {
- evdev_log_bug_libinput(pad->device,
- "unhandled position for button %i\n",
- i);
- return 1;
- }
-
- group = pad_get_mode_group(pad, group_idx);
- if (!group) {
- evdev_log_bug_libinput(pad->device,
- "Failed to find group %d for button %i\n",
- group_idx,
- i);
- return 1;
- }
-
- group->button_mask |= bit(i);
-
- if (flags & WACOM_BUTTON_MODESWITCH) {
- struct pad_mode_toggle_button *b;
- struct pad_led_group *g;
-
- b = pad_mode_toggle_button_new(pad, i);
- if (!b)
- return 1;
- g = (struct pad_led_group*)group;
- list_insert(&g->toggle_button_list, &b->link);
- group->toggle_button_mask |= bit(i);
- }
- }
-
- return 0;
-}
-
-static void
-pad_init_mode_rings(struct pad_dispatch *pad, WacomDevice *wacom)
-{
- struct libinput_tablet_pad_mode_group *group;
- const WacomStatusLEDs *leds;
- int i, nleds;
-
- leds = libwacom_get_status_leds(wacom, &nleds);
- if (nleds == 0)
- return;
-
- for (i = 0; i < nleds; i++) {
- switch(leds[i]) {
- case WACOM_STATUS_LED_RING:
- group = pad_get_mode_group(pad, i);
- group->ring_mask |= 0x1;
- break;
- case WACOM_STATUS_LED_RING2:
- group = pad_get_mode_group(pad, i);
- group->ring_mask |= 0x2;
- break;
- default:
- break;
- }
- }
-}
-
-static void
-pad_init_mode_strips(struct pad_dispatch *pad, WacomDevice *wacom)
-{
- struct libinput_tablet_pad_mode_group *group;
- const WacomStatusLEDs *leds;
- int i, nleds;
-
- leds = libwacom_get_status_leds(wacom, &nleds);
- if (nleds == 0)
- return;
-
- for (i = 0; i < nleds; i++) {
- switch(leds[i]) {
- case WACOM_STATUS_LED_TOUCHSTRIP:
- group = pad_get_mode_group(pad, i);
- group->strip_mask |= 0x1;
- break;
- case WACOM_STATUS_LED_TOUCHSTRIP2:
- group = pad_get_mode_group(pad, i);
- group->strip_mask |= 0x2;
- break;
- default:
- break;
- }
- }
-}
-
static int
pad_init_leds_from_libwacom(struct pad_dispatch *pad,
struct evdev_device *device)
struct libinput *li = pad_libinput_context(pad);
WacomDeviceDatabase *db = NULL;
WacomDevice *wacom = NULL;
- int rc = 1;
+ int rc = -EINVAL;
db = libinput_libwacom_ref(li);
if (!db)
if (!wacom)
goto out;
- rc = pad_init_led_groups(pad, device, wacom);
- if (rc != 0)
- goto out;
+ 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;
+ bool have_status_led = false;
+
+ if ((flags & WACOM_BUTTON_MODESWITCH) == 0)
+ continue;
+
+ 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;
+ 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,
+ ring_mask, strip_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);
+ if (rc < 0)
+ goto out;
+ }
+ }
+ }
- if ((rc = pad_init_mode_buttons(pad, wacom)) != 0)
+ 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;
+ }
- pad_init_mode_rings(pad, wacom);
- pad_init_mode_strips(pad, wacom);
- /* Note: libwacom doesn't do dials yet */
+ 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 (wacom)
libwacom_destroy(wacom);
if (db)
libinput_libwacom_unref(li);
- if (rc != 0)
+ 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
{
struct pad_led_group *group;
- group = pad_group_new_basic(pad, 0, 1);
+ group = pad_group_new(pad, 0, 1);
if (!group)
return 1;
if (!libinput_tablet_pad_mode_group_button_is_toggle(g, button_index))
return;
- rc = pad_led_group_get_mode(group);
+ if (list_empty(&group->led_list)) {
+ unsigned int nmodes = group->base.num_modes;
+ rc = (group->base.current_mode + 1) % nmodes;
+
+ } else {
+ rc = pad_led_group_get_mode(group);
+ }
if (rc >= 0)
group->base.current_mode = rc;
}
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
- unsigned int code;
struct libinput_event *ev;
struct libinput_event_tablet_pad *pev;
+ unsigned int expected_mode = 0;
+ int evdev_codes[KEY_OK - BTN_0] = {0};
+#if HAVE_LIBWACOM
+ WacomDeviceDatabase *db = NULL;
+ WacomDevice *wacom = NULL;
+
+ db = libwacom_database_new();
+ litest_assert_notnull(db);
+
+ wacom = libwacom_new_from_usbid(db,
+ libevdev_get_id_vendor(dev->evdev),
+ libevdev_get_id_product(dev->evdev),
+ NULL);
+ if (wacom) {
+ for (size_t i = 0; i < ARRAY_LENGTH(evdev_codes); i++) {
+ evdev_codes[i] = libwacom_get_button_evdev_code(wacom, 'A' + i);
+ }
+ }
+
+ libwacom_destroy(wacom);
+ libwacom_database_destroy(db);
+#else
+ for (size_t i = 0; i < ARRAY_LENGTH(evdev_codes); i++) {
+ evdev_codes[i] = BTN_0 + 1;
+ }
+#endif
litest_drain_events(li);
- for (code = BTN_0; code < KEY_OK; code++) {
+ for (unsigned int b = 0; b < ARRAY_LENGTH(evdev_codes) && evdev_codes[b] != 0; b++) {
+ unsigned int code = evdev_codes[b];
unsigned int mode, index;
struct libinput_tablet_pad_mode_group *group;
litest_assert_event_type(ev, LIBINPUT_EVENT_TABLET_PAD_BUTTON);
pev = libinput_event_get_tablet_pad_event(ev);
- /* litest virtual devices don't have modes */
- mode = libinput_event_tablet_pad_get_mode(pev);
- litest_assert_int_eq(mode, 0U);
group = libinput_event_tablet_pad_get_mode_group(pev);
+#if HAVE_LIBWACOM
+ if (libinput_tablet_pad_mode_group_button_is_toggle(group, b)) {
+ int num_modes = libinput_tablet_pad_mode_group_get_num_modes(group);
+ expected_mode = (expected_mode + 1) % num_modes;
+ }
+#endif
+ mode = libinput_event_tablet_pad_get_mode(pev);
+ litest_assert_int_eq(mode, expected_mode);
+
index = libinput_tablet_pad_mode_group_get_index(group);
litest_assert_int_eq(index, 0U);
pev = libinput_event_get_tablet_pad_event(ev);
mode = libinput_event_tablet_pad_get_mode(pev);
- litest_assert_int_eq(mode, 0U);
+ litest_assert_int_eq(mode, expected_mode);
group = libinput_event_tablet_pad_get_mode_group(pev);
index = libinput_tablet_pad_mode_group_get_index(group);
litest_assert_int_eq(index, 0U);
}
END_TEST
+static bool
+pad_has_groups(struct litest_device *dev)
+{
+ bool rc = false;
+#if HAVE_LIBWACOM
+ WacomDeviceDatabase *db = NULL;
+ WacomDevice *wacom = NULL;
+
+ db = libwacom_database_new();
+ litest_assert_notnull(db);
+
+ wacom = libwacom_new_from_usbid(db,
+ libevdev_get_id_vendor(dev->evdev),
+ libevdev_get_id_product(dev->evdev),
+ NULL);
+ if (wacom &&
+ (libwacom_get_ring_num_modes(wacom) != 0 ||
+ libwacom_get_ring2_num_modes(wacom) != 0 ||
+ libwacom_get_strips_num_modes(wacom) != 0))
+ rc = true;
+
+ libwacom_destroy(wacom);
+ libwacom_database_destroy(db);
+#endif
+ return rc;
+}
+
START_TEST(pad_mode_groups)
{
struct litest_device *dev = litest_current_device();
int ngroups;
unsigned int i;
- /* libinput relies on the LED sysfs files to init on pad group mode
- toggles. Since we don't have those in tests we expect all devices to have
- one mode only. */
ngroups = libinput_device_tablet_pad_get_num_mode_groups(device);
- litest_assert_int_eq(ngroups, 1);
+ litest_assert_int_ge(ngroups, 1);
for (i = 0; i < (unsigned int)ngroups; i++) {
group = libinput_device_tablet_pad_get_mode_group(device, i);
int ngroups;
unsigned int nmodes, mode;
- /* libinput relies on the LED sysfs files to init on pad group mode
- toggles. Since we don't have those in tests we expect all devices to have
- one mode only. */
+ if (pad_has_groups(dev))
+ return LITEST_NOT_APPLICABLE;
+
ngroups = libinput_device_tablet_pad_get_num_mode_groups(device);
- litest_assert_int_eq(ngroups, 1);
+ litest_assert_int_ge(ngroups, 1);
group = libinput_device_tablet_pad_get_mode_group(device, 0);
int ngroups, nbuttons, nrings, nstrips;
int i, b, r, s;
- /* libinput relies on the LED sysfs files to init on pad group mode
- toggles. Since we don't have those in tests we expect all devices to have
- one mode only. */
ngroups = libinput_device_tablet_pad_get_num_mode_groups(device);
- litest_assert_int_eq(ngroups, 1);
+ litest_assert_int_ge(ngroups, 1);
nbuttons = libinput_device_tablet_pad_get_num_buttons(device);
nrings = libinput_device_tablet_pad_get_num_rings(device);
int i;
int rc;
- /* libinput relies on the LED sysfs files to init on pad group mode
- toggles. Since we don't have those in tests we expect all devices to have
- one mode only. */
ngroups = libinput_device_tablet_pad_get_num_mode_groups(device);
- litest_assert_int_eq(ngroups, 1);
+ litest_assert_int_ge(ngroups, 1);
nbuttons = libinput_device_tablet_pad_get_num_buttons(device);
nrings = libinput_device_tablet_pad_get_num_rings(device);
START_TEST(pad_mode_group_has_no_toggle)
{
+#if HAVE_LIBWACOM
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
struct libinput_tablet_pad_mode_group* group;
int ngroups, nbuttons;
int i, b;
- /* libinput relies on the LED sysfs files to init on pad group mode
- toggles. Since we don't have those in tests we expect all devices to have
- one mode only. */
+ if (pad_has_groups(dev))
+ return LITEST_NOT_APPLICABLE;
+
ngroups = libinput_device_tablet_pad_get_num_mode_groups(device);
litest_assert_int_eq(ngroups, 1);
b));
}
}
+#endif
}
END_TEST