[PATCH libinput 6/7] Implement tablet pad group mode support

Jason Gerecke killertofu at gmail.com
Wed Jun 8 01:33:28 UTC 2016


On Sun, Jun 5, 2016 at 11:50 PM, Peter Hutterer
<peter.hutterer at who-t.net> wrote:
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> ---
>  configure.ac                  |   2 +-
>  src/Makefile.am               |   1 +
>  src/evdev-tablet-pad-leds.c   | 621 ++++++++++++++++++++++++++++++++++++++++++
>  src/evdev-tablet-pad.c        |  76 +++++-
>  src/evdev-tablet-pad.h        |  12 +
>  src/evdev.h                   |  16 ++
>  src/libinput-private.h        |  22 +-
>  src/libinput-util.h           |   1 +
>  src/libinput.c                |  82 +++++-
>  tools/libinput-list-devices.c |   6 +-
>  10 files changed, 814 insertions(+), 25 deletions(-)
>  create mode 100644 src/evdev-tablet-pad-leds.c
>
> diff --git a/configure.ac b/configure.ac
> index 8c14efe..8278be2 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -203,7 +203,7 @@ AC_ARG_ENABLE(libwacom,
>               [use_libwacom="$enableval"],
>               [use_libwacom="yes"])
>  if test "x$use_libwacom" = "xyes"; then
> -       PKG_CHECK_MODULES(LIBWACOM, [libwacom >= 0.12], [HAVE_LIBWACOM="yes"])
> +       PKG_CHECK_MODULES(LIBWACOM, [libwacom >= 0.17], [HAVE_LIBWACOM="yes"])
>         AC_DEFINE(HAVE_LIBWACOM, 1, [Build with libwacom])
>  fi
>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 39c22c2..48e704a 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -22,6 +22,7 @@ libinput_la_SOURCES =                 \
>         evdev-tablet.h                  \
>         evdev-tablet-pad.c              \
>         evdev-tablet-pad.h              \
> +       evdev-tablet-pad-leds.c         \
>         filter.c                        \
>         filter.h                        \
>         filter-private.h                \
> diff --git a/src/evdev-tablet-pad-leds.c b/src/evdev-tablet-pad-leds.c
> new file mode 100644
> index 0000000..4aa1590
> --- /dev/null
> +++ b/src/evdev-tablet-pad-leds.c
> @@ -0,0 +1,621 @@
> +/*
> + * Copyright © 2016 Red Hat, Inc.
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and
> + * its documentation for any purpose is hereby granted without fee, provided
> + * that the above copyright notice appear in all copies and that both that
> + * copyright notice and this permission notice appear in supporting
> + * documentation, and that the name of the copyright holders not be used in
> + * advertising or publicity pertaining to distribution of the software
> + * without specific, written prior permission.  The copyright holders make
> + * no representations about the suitability of this software for any
> + * purpose.  It is provided "as is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
> + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
> + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
> + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
> + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
> + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include "config.h"
> +
> +#include <assert.h>
> +#include <limits.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +#include "evdev-tablet-pad.h"
> +
> +#if HAVE_LIBWACOM
> +#include <libwacom/libwacom.h>
> +#endif
> +
> +struct pad_led_group {
> +       struct libinput_tablet_pad_mode_group base;
> +
> +       /* /sys/devices/<hid device>/wacom_led/status_led0_select */
> +       int led_status_fd;
> +       /* /sys/devices/<hid device>/wacom_led/status0_luminance */
> +       int led_luminance_fd;
> +
> +       struct list toggle_button_list;
> +};
> +
> +struct pad_mode_toggle_button {
> +       struct list link;
> +       unsigned int button_index;
> +       unsigned int target_mode;
> +};
> +
> +static inline void
> +pad_led_group_set_brightness(struct pad_led_group *group,
> +                            double brightness)
> +{
> +       char buf[4] = {0};
> +       int b;
> +
> +       assert(brightness > 0.0);
> +
> +       /* FIXME: check what the range is on all models */
> +       b = 127 * brightness;
> +       if (sprintf(buf, "%d", b) > -1) {
> +               write(group->led_luminance_fd, buf, strlen(buf));
> +               fsync(group->led_luminance_fd);
> +       }
> +}
> +
> +static inline int
> +pad_led_group_get_mode(struct pad_led_group *group)
> +{
> +       char buf[4] = {0};
> +       int rc;
> +       unsigned int mode;
> +
> +       rc = read(group->led_status_fd, buf, sizeof(buf) - 1);
> +       if (rc == -1)
> +               return -errno;
> +
> +       rc = sscanf(buf, "%u\n", &mode);
> +       if (rc != 1)
> +               return -EINVAL;
> +
> +       return mode;
> +}
> +
> +static inline void
> +pad_led_group_set_mode(struct pad_led_group *group,
> +                      unsigned int mode)
> +{
> +       char buf[4] = {0};
> +       int rc;
> +
> +       rc = sprintf(buf, "%d", mode);
> +       if (rc == -1)
> +               return;
> +
> +       rc = write(group->led_status_fd, buf, strlen(buf));
> +       if (rc == -1)
> +               return;
> +
> +       fsync(group->led_status_fd);
> +       group->base.current_mode = mode;
> +}
> +
> +static inline void
> +pad_led_group_set_next_mode(struct pad_led_group *group)
> +{
> +       unsigned int next;
> +
> +       next = (group->base.current_mode + 1) % group->base.num_modes;
> +       pad_led_group_set_mode(group, next);
> +}
> +
> +static void
> +pad_led_group_destroy(struct libinput_tablet_pad_mode_group *g)
> +{
> +       struct pad_led_group *group = (struct pad_led_group *)g;
> +       if (group->led_status_fd != -1)
> +               close_restricted(g->device->seat->libinput,
> +                                group->led_status_fd);
> +       if (group->led_luminance_fd != -1)
> +               close_restricted(g->device->seat->libinput,
> +                                group->led_luminance_fd);
> +
> +       free(group);
> +}
> +static struct pad_led_group *
> +pad_group_new_basic(struct pad_dispatch *pad,
> +                   unsigned int group_index,
> +                   int nleds)
> +{
> +       struct pad_led_group *group;
> +
> +       group = zalloc(sizeof *group);
> +       if (!group)
> +               return NULL;
> +
> +       group->base.device = &pad->device->base;
> +       group->base.refcount = 1;
> +       group->base.index = group_index;
> +       group->base.current_mode = 0;
> +       group->base.num_modes = nleds;
> +       group->base.destroy = pad_led_group_destroy;
> +       group->led_status_fd = -1;
> +       group->led_luminance_fd = -1;
> +       list_init(&group->toggle_button_list);
> +
> +       return group;
> +}
> +
> +static inline struct pad_led_group *
> +pad_group_new(struct pad_dispatch *pad,
> +             unsigned int group_index,
> +             int nleds,
> +             const char *syspath)
> +{
> +       struct libinput *libinput = pad->device->base.seat->libinput;
> +       struct pad_led_group *group;
> +       int rc;
> +       char path[PATH_MAX];
> +
> +       group = pad_group_new_basic(pad, group_index, nleds);
> +       if (!group)
> +               return NULL;
> +
> +       rc = sprintf(path, "%s/status_led%d_select", syspath, group_index);
> +       if (rc == -1)
> +               goto error;
> +
> +       group->led_status_fd = open_restricted(libinput, path, O_RDWR);
> +       if (group->led_status_fd < 0)
> +               goto error;
> +
> +       rc = sprintf(path, "%s/status%d_luminance", syspath, group_index);
> +       if (rc == -1)
> +               goto error;
> +
> +       group->led_luminance_fd = open_restricted(libinput, path, O_RDWR);
> +       if (group->led_luminance_fd < 0)
> +               goto error;

Alas, the 24HDT has controls to select LEDs, but not set luminance.
This will need to be optional instead of an error.

> +
> +       rc = pad_led_group_get_mode(group);
> +       if (rc < 0) {
> +               errno = -rc;
> +               goto error;
> +       }
> +
> +       group->base.current_mode = rc;
> +
> +       return group;
> +
> +error:
> +       log_error(libinput, "Unable to init LED group: %s\n", strerror(errno));
> +
> +       close_restricted(libinput, group->led_status_fd);
> +       close_restricted(libinput, group->led_luminance_fd);
> +       free(group);
> +
> +       return NULL;
> +}
> +
> +static inline struct pad_mode_toggle_button *
> +pad_mode_toggle_button_new(struct pad_dispatch *pad,
> +                          struct libinput_tablet_pad_mode_group *group,
> +                          unsigned int button_index)
> +{
> +       struct pad_mode_toggle_button *button;
> +
> +       button = zalloc(sizeof *button);
> +       if (!button)
> +               return NULL;
> +
> +       button->button_index = button_index;
> +       button->target_mode = LIBINPUT_TABLET_PAD_MODE_GROUP_TARGET_NEXT;
> +
> +       return button;
> +}
> +
> +static inline void
> +pad_mode_toggle_button_destroy(struct pad_mode_toggle_button* button)
> +{
> +       list_remove(&button->link);
> +       free(button);
> +}
> +
> +static inline char *
> +pad_led_get_sysfs_base_path(struct evdev_device *device)
> +{
> +       struct udev_device *udev_device = device->udev_device,
> +                          *hid_device;
> +       const char *hid_sysfs_path;
> +       char path[PATH_MAX];
> +       char *base_path;
> +       int rc;
> +
> +       hid_device = udev_device_get_parent_with_subsystem_devtype(udev_device,
> +                                                                  "hid",
> +                                                                  NULL);
> +       if (!hid_device)
> +               return NULL;
> +
> +       hid_sysfs_path = udev_device_get_syspath(hid_device);
> +
> +       rc = xasprintf(&base_path,
> +                      "%s/wacom_led",
> +                      hid_sysfs_path);
> +       if (rc == -1)
> +               return NULL;
> +
> +       /* to check if the leds exist */
> +       rc = snprintf(path, sizeof(path), "%s/status_led0_select", base_path);
> +       if (rc == -1) {
> +               free(base_path);
> +               return NULL;
> +       }
> +
> +       rc = access(path, R_OK|W_OK);
> +       if (rc == 0)
> +               return base_path;
> +
> +       /* In theory we could return read-only LEDs here but let's make life
> +        * simple and just return NULL and pretend we don't have LEDs. We
> +        * can't change them anyway. Only the EKR has read-only LEDs but
> +        * they're in a different sysfs path.
> +        */

Seems reasonable for now, though I'd mark this as TODO :)

> +       if (errno != ENOENT)
> +               log_error(device->base.seat->libinput,
> +                         "Unable to access tablet LED syspath %s (%s)\n",
> +                         path,
> +                         strerror(errno));
> +       free(base_path);
> +       return NULL;
> +}
> +
> +#if HAVE_LIBWACOM
> +static int
> +pad_init_led_groups(struct pad_dispatch *pad,
> +                   struct evdev_device *device,
> +                   WacomDevice *wacom,
> +                   const char *syspath)
> +{
> +       struct libinput *libinput = device->base.seat->libinput;
> +       const WacomStatusLEDs *leds;
> +       int nleds, nmodes;
> +       int i;
> +       struct pad_led_group *group;
> +
> +       leds = libwacom_get_status_leds(wacom, &nleds);
> +       if (nleds == 0)
> +               return 0;
> +
> +       for (i = 0; i < nleds; i++) {
> +               switch(leds[i]) {
> +               case WACOM_STATUS_LED_UNAVAILABLE:
> +                       log_bug_libinput(libinput,
> +                                        "Invalid led type %d\n",
> +                                        leds[i]);
> +                       return 1;
> +               case WACOM_STATUS_LED_RING:
> +                       /* ring is always group 0 */

Actually...

For devices with two LEDs, the groups are likely reversed from what
you'd think. The sysfs docs state that if multiple groups of LEDs are
available, status_led0_select actually controls the *right* set of
LEDs and status_led1_select the *left*.

Git says you should blame Ping and Dmitry for that bit of weirdness.

It looks like GNOME relies on the order of items in StatusLEDs to
associate things correctly. For example, my 24HDT has "StatusLEDs =
Ring2,Ring" causing Ring2 to be associated with led0 and Ring to be
associated with led1. You could probably do the same thing here by
using 'i' instead of 0 or 1:

group = pad_group_new(pad, i, nmodes, syspath)

> +                       nmodes = libwacom_get_ring_num_modes(wacom);
> +                       group = pad_group_new(pad, 0, nmodes, syspath);
> +                       if (!group)
> +                               return 1;
> +                       list_insert(&pad->modes.mode_group_list, &group->base.link);
> +                       break;
> +               case WACOM_STATUS_LED_RING2:
> +                       /* ring2 is always group 1 */

Ditto :(

> +                       nmodes = libwacom_get_ring2_num_modes(wacom);
> +                       group = pad_group_new(pad, 1, nmodes, syspath);
> +                       if (!group)
> +                               return 1;
> +                       list_insert(&pad->modes.mode_group_list, &group->base.link);
> +                       break;
> +               case WACOM_STATUS_LED_TOUCHSTRIP:
> +                       nmodes = 1; /* something we know... */

Why not use libwacom_get_strips_num_modes here? IIRC the 21UX2 has nmodes = 4...

> +                       group = pad_group_new(pad, 0, nmodes, syspath);
> +                       if (!group)
> +                               return 1;
> +                       list_insert(&pad->modes.mode_group_list, &group->base.link);
> +                       break;
> +               case WACOM_STATUS_LED_TOUCHSTRIP2:
> +                       nmodes = 1; /* something we know... */

Ditto.

> +                       group = pad_group_new(pad, 1, nmodes, syspath);
> +                       if (!group)
> +                               return 1;
> +                       list_insert(&pad->modes.mode_group_list, &group->base.link);
> +                       break;
> +               default:

Should we log a warning here?

> +                       break;
> +               }
> +       }
> +
> +       return 0;
> +}
> +#endif
> +
> +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 void
> +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 ((int)group_idx == -1) {
> +                       /* This isn't a mode toggle button, fall back to
> +                        * Left/Right positioning instead */

Should we handle top/bottom as well? Also, it might be good idea to
(before this for loop) determine the directions actually used on the
tablet, so that a tablet without e.g. Left buttons doesn't break
things.

> +                       if (flags & WACOM_BUTTON_POSITION_LEFT)
> +                               group_idx = 0;
> +                       else if (flags & WACOM_BUTTON_POSITION_RIGHT)
> +                               group_idx = 1;
> +                       else
> +                               continue;
> +               }
> +
> +               group = pad_get_mode_group(pad, group_idx);
> +               group->button_mask |= 1 << i;
> +
> +               if (flags & WACOM_BUTTON_MODESWITCH) {
> +                       struct pad_mode_toggle_button *b;
> +                       struct pad_led_group *g;
> +
> +                       b = pad_mode_toggle_button_new(pad, group, i);
> +                       g = (struct pad_led_group*)group;
> +                       list_insert(&g->toggle_button_list, &b->link);
> +                       group->toggle_button_mask |= 1 << i;
> +               }
> +       }
> +}
> +
> +static void
> +pad_init_mode_rings(struct pad_dispatch *pad, WacomDevice *wacom)
> +{
> +       struct libinput_tablet_pad_mode_group *group;
> +
> +       if (libwacom_has_ring(wacom)) {
> +               /* ring is in group 0 */

See my comments above about tablets with two LEDs having their groups
reversed to make sure this still works right.

> +               group = pad_get_mode_group(pad, 0);
> +               group->ring_mask |= 0x1;
> +       }
> +
> +       if (libwacom_has_ring2(wacom)) {
> +               /* ring2 is in group 1 */

Ditto.

> +               group = pad_get_mode_group(pad, 1);
> +               group->ring_mask |= 0x2;
> +       }
> +}
> +
> +static void
> +pad_init_mode_strips(struct pad_dispatch *pad, WacomDevice *wacom)
> +{
> +       struct libinput_tablet_pad_mode_group *group;
> +       int nleds;
> +       int i;
> +
> +       libwacom_get_status_leds(wacom, &nleds);
> +       if (nleds == 0)
> +               return;
> +
> +       /* All devices with LEDs and strips have the strips in two different
> +        * groups (21UX2) */

Ditto.

> +       for (i = 0; i < libwacom_get_num_strips(wacom); i++) {
> +               group = pad_get_mode_group(pad, i);
> +               group->strip_mask |= 1 << i;
> +       }
> +}
> +
> +static int
> +pad_init_leds_from_libwacom(struct pad_dispatch *pad,
> +                           struct evdev_device *device)
> +{
> +       struct libinput *libinput = device->base.seat->libinput;
> +       WacomDeviceDatabase *db = NULL;
> +       WacomDevice *wacom = NULL;
> +       char *syspath = NULL;
> +       int rc = 1;
> +
> +       db = libwacom_database_new();
> +       if (!db) {
> +               log_info(libinput,
> +                        "Failed to initialize libwacom context.\n");
> +               goto out;
> +       }
> +
> +       wacom = libwacom_new_from_path(db,
> +                                      udev_device_get_devnode(device->udev_device),
> +                                      WFALLBACK_NONE,
> +                                      NULL);
> +       if (!wacom)
> +               goto out;
> +
> +       syspath = pad_led_get_sysfs_base_path(device);
> +       if (!syspath)
> +               goto out;
> +
> +       if (pad_init_led_groups(pad, device, wacom, syspath) != 0)
> +               goto out;
> +
> +       pad_init_mode_buttons(pad, wacom);
> +       pad_init_mode_rings(pad, wacom);
> +       pad_init_mode_strips(pad, wacom);
> +
> +       rc = 0;
> +out:
> +       if (syspath)
> +               free(syspath);
> +       if (wacom)
> +               libwacom_destroy(wacom);
> +       if (db)
> +               libwacom_database_destroy(db);
> +
> +       if (rc != 0)
> +               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_basic(pad, 0, 1);
> +       if (!group)
> +               return 1;
> +
> +       /* If we only have one group, all buttons/strips/rings are part of
> +        * that group. We ely on the other layers to filter out invalid

s/ely/rely/

> +        * indices */
> +       group->base.button_mask = -1;
> +       group->base.strip_mask = -1;
> +       group->base.ring_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)
> +{
> +       int rc = 1;
> +
> +       list_init(&pad->modes.mode_group_list);
> +
> +       if (pad->nbuttons > 32) {
> +               log_bug_libinput(device->base.seat->libinput,
> +                                "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);
> +#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, *tmpgrp;
> +
> +       list_for_each_safe(group, tmpgrp, &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;
> +       struct pad_mode_toggle_button *b;
> +       unsigned int mode;
> +
> +       if (state != LIBINPUT_BUTTON_STATE_PRESSED)
> +               return;
> +
> +       if (!libinput_tablet_pad_mode_group_button_is_toggle(g, button_index))
> +               return;
> +
> +       list_for_each(b, &group->toggle_button_list, link) {
> +               if (b->button_index != button_index)
> +                       continue;
> +
> +               mode = b->target_mode;
> +
> +               switch (mode) {
> +               case LIBINPUT_TABLET_PAD_MODE_GROUP_TARGET_NEXT:
> +                       pad_led_group_set_next_mode(group);
> +                       break;
> +               case LIBINPUT_TABLET_PAD_MODE_GROUP_TARGET_PREVIOUS:
> +                       assert(!"Ooops, not implemented");
> +                       break;
> +               default:
> +                       if (mode <=
> +                           LIBINPUT_TABLET_PAD_MODE_GROUP_TARGET_MAX_DIRECT)
> +                               pad_led_group_set_mode(group, mode);
> +               }
> +               break;
> +       }
> +}
> +
> +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;
> +
> +       return pad_get_mode_group(pad, index);
> +}
> +
> +unsigned int
> +evdev_device_tablet_pad_mode_group_get_button_target(struct libinput_tablet_pad_mode_group *g,
> +                                                    unsigned int button_index)
> +{
> +       struct pad_led_group *group = (struct pad_led_group*)g;
> +       struct pad_mode_toggle_button *button;
> +
> +       list_for_each(button, &group->toggle_button_list, link) {
> +               if (button->button_index != button_index)
> +                       continue;
> +
> +               return button->target_mode;
> +       }
> +
> +       return 0;
> +}
> diff --git a/src/evdev-tablet-pad.c b/src/evdev-tablet-pad.c
> index 5341657..7e4cd89 100644
> --- a/src/evdev-tablet-pad.c
> +++ b/src/evdev-tablet-pad.c
> @@ -209,12 +209,45 @@ pad_handle_strip(struct pad_dispatch *pad,
>         return pos;
>  }
>
> +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) {
> +               if (libinput_tablet_pad_mode_group_has_ring(group, ring))
> +                       return group;
> +       }
> +
> +       assert(!"Unable to find ring mode group");
> +
> +       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) {
> +               if (libinput_tablet_pad_mode_group_has_strip(group, strip))
> +                       return group;
> +       }
> +
> +       assert(!"Unable to find strip mode group");
> +
> +       return NULL;
> +}
> +
>  static void
>  pad_check_notify_axes(struct pad_dispatch *pad,
>                       struct evdev_device *device,
>                       uint64_t time)
>  {
>         struct libinput_device *base = &device->base;
> +       struct libinput_tablet_pad_mode_group *group;
>         double value;
>         bool send_finger_up = false;
>
> @@ -229,11 +262,13 @@ pad_check_notify_axes(struct pad_dispatch *pad,
>                 if (send_finger_up)
>                         value = -1.0;
>
> +               group = pad_ring_get_mode_group(pad, 0);
>                 tablet_pad_notify_ring(base,
>                                        time,
>                                        0,
>                                        value,
> -                                      LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
> +                                      LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
> +                                      group);
>         }
>
>         if (pad->changed_axes & PAD_AXIS_RING2) {
> @@ -241,11 +276,13 @@ pad_check_notify_axes(struct pad_dispatch *pad,
>                 if (send_finger_up)
>                         value = -1.0;
>
> +               group = pad_ring_get_mode_group(pad, 1);
>                 tablet_pad_notify_ring(base,
>                                        time,
>                                        1,
>                                        value,
> -                                      LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
> +                                      LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
> +                                      group);
>         }
>
>         if (pad->changed_axes & PAD_AXIS_STRIP1) {
> @@ -253,11 +290,13 @@ pad_check_notify_axes(struct pad_dispatch *pad,
>                 if (send_finger_up)
>                         value = -1.0;
>
> +               group = pad_strip_get_mode_group(pad, 0);
>                 tablet_pad_notify_strip(base,
>                                         time,
>                                         0,
>                                         value,
> -                                       LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
> +                                       LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
> +                                       group);
>         }
>
>         if (pad->changed_axes & PAD_AXIS_STRIP2) {
> @@ -265,11 +304,13 @@ pad_check_notify_axes(struct pad_dispatch *pad,
>                 if (send_finger_up)
>                         value = -1.0;
>
> +               group = pad_strip_get_mode_group(pad, 1);
>                 tablet_pad_notify_strip(base,
>                                         time,
>                                         1,
>                                         value,
> -                                       LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
> +                                       LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
> +                                       group);
>         }
>
>         pad->changed_axes = PAD_AXIS_NONE;
> @@ -288,6 +329,22 @@ pad_process_key(struct pad_dispatch *pad,
>         pad_button_set_down(pad, button, is_press);
>  }
>
> +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;
> +}
> +
>  static void
>  pad_notify_button_mask(struct pad_dispatch *pad,
>                        struct evdev_device *device,
> @@ -296,6 +353,7 @@ pad_notify_button_mask(struct pad_dispatch *pad,
>                        enum libinput_button_state state)
>  {
>         struct libinput_device *base = &device->base;
> +       struct libinput_tablet_pad_mode_group *group;
>         int32_t code;
>         unsigned int i;
>
> @@ -315,8 +373,11 @@ pad_notify_button_mask(struct pad_dispatch *pad,
>                                 continue;
>
>                         map = pad->button_map[code - 1];
> -                       if (map != -1)
> -                               tablet_pad_notify_button(base, time, map, state);
> +                       if (map != -1) {
> +                               group = pad_button_get_mode_group(pad, map);
> +                               pad_button_update_mode(group, map, state);
> +                               tablet_pad_notify_button(base, time, map, state, group);
> +                       }
>                 }
>         }
>  }
> @@ -437,6 +498,7 @@ pad_destroy(struct evdev_dispatch *dispatch)
>  {
>         struct pad_dispatch *pad = (struct pad_dispatch*)dispatch;
>
> +       pad_destroy_leds(pad);
>         free(pad);
>  }
>
> @@ -500,6 +562,8 @@ pad_init(struct pad_dispatch *pad, struct evdev_device *device)
>
>         pad_init_buttons(pad, device);
>         pad_init_left_handed(device);
> +       if (pad_init_leds(pad, device) != 0)
> +               return 1;
>
>         return 0;
>  }
> diff --git a/src/evdev-tablet-pad.h b/src/evdev-tablet-pad.h
> index 828ded8..2c33eb1 100644
> --- a/src/evdev-tablet-pad.h
> +++ b/src/evdev-tablet-pad.h
> @@ -64,6 +64,18 @@ struct pad_dispatch {
>                 struct libinput_device_config_send_events config;
>                 enum libinput_config_send_events_mode current_mode;
>         } sendevents;
> +
> +       struct {
> +               struct list mode_group_list;
> +       } modes;
>  };
>
> +int
> +pad_init_leds(struct pad_dispatch *pad, struct evdev_device *device);
> +void
> +pad_destroy_leds(struct pad_dispatch *pad);
> +void
> +pad_button_update_mode(struct libinput_tablet_pad_mode_group *g,
> +                      unsigned int pressed_button,
> +                      enum libinput_button_state state);
>  #endif
> diff --git a/src/evdev.h b/src/evdev.h
> index 54d7ce7..087ceba 100644
> --- a/src/evdev.h
> +++ b/src/evdev.h
> @@ -392,6 +392,22 @@ evdev_device_tablet_pad_get_num_rings(struct evdev_device *device);
>  int
>  evdev_device_tablet_pad_get_num_strips(struct evdev_device *device);
>
> +int
> +evdev_device_tablet_pad_get_num_mode_groups(struct evdev_device *device);
> +
> +struct libinput_tablet_pad_mode_group *
> +evdev_device_tablet_pad_get_mode_group(struct evdev_device *device,
> +                                      unsigned int index);
> +
> +unsigned int
> +evdev_device_tablet_pad_mode_group_get_button_target(
> +                                    struct libinput_tablet_pad_mode_group *g,
> +                                    unsigned int button_index);
> +
> +struct libinput_tablet_pad_led *
> +evdev_device_tablet_pad_get_led(struct evdev_device *device,
> +                               unsigned int led);
> +
>  double
>  evdev_device_transform_x(struct evdev_device *device,
>                          double x,
> diff --git a/src/libinput-private.h b/src/libinput-private.h
> index 98cb419..b325707 100644
> --- a/src/libinput-private.h
> +++ b/src/libinput-private.h
> @@ -330,9 +330,22 @@ struct libinput_tablet_tool {
>  };
>
>  struct libinput_tablet_pad_mode_group {
> +       struct libinput_device *device;
>         struct list link;
>         int refcount;
>         void *user_data;
> +
> +       unsigned int index;
> +       unsigned int num_modes;
> +       unsigned int current_mode;
> +
> +       uint32_t button_mask;
> +       uint32_t ring_mask;
> +       uint32_t strip_mask;
> +
> +       uint32_t toggle_button_mask;
> +
> +       void (*destroy)(struct libinput_tablet_pad_mode_group *group);
>  };
>
>  struct libinput_event {
> @@ -572,19 +585,22 @@ void
>  tablet_pad_notify_button(struct libinput_device *device,
>                          uint64_t time,
>                          int32_t button,
> -                        enum libinput_button_state state);
> +                        enum libinput_button_state state,
> +                        struct libinput_tablet_pad_mode_group *group);
>  void
>  tablet_pad_notify_ring(struct libinput_device *device,
>                        uint64_t time,
>                        unsigned int number,
>                        double value,
> -                      enum libinput_tablet_pad_ring_axis_source source);
> +                      enum libinput_tablet_pad_ring_axis_source source,
> +                      struct libinput_tablet_pad_mode_group *group);
>  void
>  tablet_pad_notify_strip(struct libinput_device *device,
>                         uint64_t time,
>                         unsigned int number,
>                         double value,
> -                       enum libinput_tablet_pad_strip_axis_source source);
> +                       enum libinput_tablet_pad_strip_axis_source source,
> +                       struct libinput_tablet_pad_mode_group *group);
>
>  static inline uint64_t
>  libinput_now(struct libinput *libinput)
> diff --git a/src/libinput-util.h b/src/libinput-util.h
> index 701fe07..3ccd61b 100644
> --- a/src/libinput-util.h
> +++ b/src/libinput-util.h
> @@ -86,6 +86,7 @@ int list_empty(const struct list *list);
>              pos = tmp,                                                 \
>              tmp = container_of(pos->member.next, tmp, member))
>
> +#define NBITS(b) (b * 8)
>  #define LONG_BITS (sizeof(long) * 8)
>  #define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
>  #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
> diff --git a/src/libinput.c b/src/libinput.c
> index e174aae..ee18b9c 100644
> --- a/src/libinput.c
> +++ b/src/libinput.c
> @@ -1668,6 +1668,12 @@ libinput_event_tablet_tool_destroy(struct libinput_event_tablet_tool *event)
>         libinput_tablet_tool_unref(event->tool);
>  }
>
> +static void
> +libinput_event_tablet_pad_destroy(struct libinput_event_tablet_pad *event)
> +{
> +       libinput_tablet_pad_mode_group_unref(event->mode_group);
> +}
> +
>  LIBINPUT_EXPORT void
>  libinput_event_destroy(struct libinput_event *event)
>  {
> @@ -1682,6 +1688,13 @@ libinput_event_destroy(struct libinput_event *event)
>                 libinput_event_tablet_tool_destroy(
>                    libinput_event_get_tablet_tool_event(event));
>                 break;
> +       case LIBINPUT_EVENT_TABLET_PAD_RING:
> +       case LIBINPUT_EVENT_TABLET_PAD_STRIP:
> +       case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
> +       case LIBINPUT_EVENT_TABLET_PAD_MODE:
> +               libinput_event_tablet_pad_destroy(
> +                  libinput_event_get_tablet_pad_event(event));
> +               break;
>         default:
>                 break;
>         }
> @@ -2402,18 +2415,24 @@ void
>  tablet_pad_notify_button(struct libinput_device *device,
>                          uint64_t time,
>                          int32_t button,
> -                        enum libinput_button_state state)
> +                        enum libinput_button_state state,
> +                        struct libinput_tablet_pad_mode_group *group)
>  {
>         struct libinput_event_tablet_pad *button_event;
> +       unsigned int mode;
>
>         button_event = zalloc(sizeof *button_event);
>         if (!button_event)
>                 return;
>
> +       mode = libinput_tablet_pad_mode_group_get_mode(group);
> +
>         *button_event = (struct libinput_event_tablet_pad) {
>                 .time = time,
>                 .button.button = button,
>                 .button.state = state,
> +               .mode_group = libinput_tablet_pad_mode_group_ref(group),
> +               .mode = mode,
>         };
>
>         post_device_event(device,
> @@ -2427,19 +2446,25 @@ tablet_pad_notify_ring(struct libinput_device *device,
>                        uint64_t time,
>                        unsigned int number,
>                        double value,
> -                      enum libinput_tablet_pad_ring_axis_source source)
> +                      enum libinput_tablet_pad_ring_axis_source source,
> +                      struct libinput_tablet_pad_mode_group *group)
>  {
>         struct libinput_event_tablet_pad *ring_event;
> +       unsigned int mode;
>
>         ring_event = zalloc(sizeof *ring_event);
>         if (!ring_event)
>                 return;
>
> +       mode = libinput_tablet_pad_mode_group_get_mode(group);
> +
>         *ring_event = (struct libinput_event_tablet_pad) {
>                 .time = time,
>                 .ring.number = number,
>                 .ring.position = value,
>                 .ring.source = source,
> +               .mode_group = libinput_tablet_pad_mode_group_ref(group),
> +               .mode = mode,
>         };
>
>         post_device_event(device,
> @@ -2453,19 +2478,25 @@ tablet_pad_notify_strip(struct libinput_device *device,
>                         uint64_t time,
>                         unsigned int number,
>                         double value,
> -                       enum libinput_tablet_pad_strip_axis_source source)
> +                       enum libinput_tablet_pad_strip_axis_source source,
> +                       struct libinput_tablet_pad_mode_group *group)
>  {
>         struct libinput_event_tablet_pad *strip_event;
> +       unsigned int mode;
>
>         strip_event = zalloc(sizeof *strip_event);
>         if (!strip_event)
>                 return;
>
> +       mode = libinput_tablet_pad_mode_group_get_mode(group);
> +
>         *strip_event = (struct libinput_event_tablet_pad) {
>                 .time = time,
>                 .strip.number = number,
>                 .strip.position = value,
>                 .strip.source = source,
> +               .mode_group = libinput_tablet_pad_mode_group_ref(group),
> +               .mode = mode,
>         };
>
>         post_device_event(device,
> @@ -2835,14 +2866,15 @@ libinput_device_tablet_pad_get_num_strips(struct libinput_device *device)
>  LIBINPUT_EXPORT int
>  libinput_device_tablet_pad_get_num_mode_groups(struct libinput_device *device)
>  {
> -       return 0;
> +       return evdev_device_tablet_pad_get_num_mode_groups((struct evdev_device *)device);
>  }
>
>  LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group*
>  libinput_device_tablet_pad_get_mode_group(struct libinput_device *device,
>                                           unsigned int index)
>  {
> -       return NULL;
> +       return evdev_device_tablet_pad_get_mode_group((struct evdev_device *)device,
> +                                                     index);
>  }
>
>  LIBINPUT_EXPORT unsigned int
> @@ -2855,42 +2887,68 @@ libinput_tablet_pad_mode_group_get_num_modes(
>  LIBINPUT_EXPORT unsigned int
>  libinput_tablet_pad_mode_group_get_mode(struct libinput_tablet_pad_mode_group *group)
>  {
> -       return 1;
> +       return group->current_mode;
> +}
> +
> +LIBINPUT_EXPORT unsigned int
> +libinput_tablet_pad_mode_group_get_index(struct libinput_tablet_pad_mode_group *group)
> +{
> +       return group->index;
>  }
>
>  LIBINPUT_EXPORT int
>  libinput_tablet_pad_mode_group_has_button(struct libinput_tablet_pad_mode_group *group,
>                                           unsigned int button)
>  {
> -       return 1;
> +       if ((int)button >=
> +           libinput_device_tablet_pad_get_num_buttons(group->device))
> +               return 0;
> +
> +       return !!(group->button_mask & (1 << button));
>  }
>
>  LIBINPUT_EXPORT int
>  libinput_tablet_pad_mode_group_has_ring(struct libinput_tablet_pad_mode_group *group,
>                                         unsigned int ring)
>  {
> -       return 1;
> +       if ((int)ring >=
> +           libinput_device_tablet_pad_get_num_rings(group->device))
> +               return 0;
> +
> +       return !!(group->ring_mask & (1 << ring));
>  }
>
>  LIBINPUT_EXPORT int
>  libinput_tablet_pad_mode_group_has_strip(struct libinput_tablet_pad_mode_group *group,
>                                          unsigned int strip)
>  {
> -       return 1;
> +       if ((int)strip >=
> +           libinput_device_tablet_pad_get_num_strips(group->device))
> +               return 0;
> +
> +       return !!(group->strip_mask & (1 << strip));
>  }
>
>  LIBINPUT_EXPORT int
>  libinput_tablet_pad_mode_group_button_is_toggle(struct libinput_tablet_pad_mode_group *group,
>                                                 unsigned int button)
>  {
> -       return 0;
> +       if ((int)button >=
> +           libinput_device_tablet_pad_get_num_buttons(group->device))
> +               return 0;
> +
> +       return !!(group->toggle_button_mask & (1 << button));
>  }
>
>  LIBINPUT_EXPORT unsigned int
>  libinput_tablet_pad_mode_group_button_get_target_mode(struct libinput_tablet_pad_mode_group *group,
>                                                       unsigned int button)
>  {
> -       return 0;
> +       if (!libinput_tablet_pad_mode_group_button_is_toggle(group, button))
> +               return 0;
> +
> +       return evdev_device_tablet_pad_mode_group_get_button_target(group,
> +                                                                   button);
>  }
>
>  LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group *
> @@ -2912,7 +2970,7 @@ libinput_tablet_pad_mode_group_unref(
>                 return group;
>
>         list_remove(&group->link);
> -       free(group);
> +       group->destroy(group);
>         return NULL;
>  }
>
> diff --git a/tools/libinput-list-devices.c b/tools/libinput-list-devices.c
> index 2869dd7..17dabc1 100644
> --- a/tools/libinput-list-devices.c
> +++ b/tools/libinput-list-devices.c
> @@ -251,9 +251,9 @@ print_pad_info(struct libinput_device *device)
>         nstrips = libinput_device_tablet_pad_get_num_strips(device);
>
>         printf("Pad:\n");
> -       printf("        Rings:   %d\n", nrings);
> -       printf("        Strips:  %d\n", nstrips);
> -       printf("        Buttons: %d\n", nbuttons);
> +       printf("        Rings:   %d", nrings);
> +       printf("        Strips:  %d", nstrips);
> +       printf("        Buttons: %d", nbuttons);

Is this hunk intended to be here?

Jason
---
Now instead of four in the eights place /
you’ve got three, ‘Cause you added one  /
(That is to say, eight) to the two,     /
But you can’t take seven from three,    /
So you look at the sixty-fours....

>  }
>
>  static void
> --
> 2.7.4
>
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/wayland-devel


More information about the wayland-devel mailing list