For each device open the various led devices (brightness only) and map the one
nonzero brightness to the current mode.

Signed-off-by: Peter Hutterer <[email protected]>
---
Ok, now that 4.9 is out and commonplace enough, let's get this merged.
This implements the background bits for the tabled pad mode support. Since
the kernel does almost everything, 

 configure.ac                |   2 +-
 src/evdev-tablet-pad-leds.c | 477 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 470 insertions(+), 9 deletions(-)

diff --git a/configure.ac b/configure.ac
index 25e6562..8337467 100644
--- a/configure.ac
+++ b/configure.ac
@@ -232,7 +232,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.20], [HAVE_LIBWACOM="yes"])
        AC_DEFINE(HAVE_LIBWACOM, 1, [Build with libwacom])
 
        OLD_LIBS=$LIBS
diff --git a/src/evdev-tablet-pad-leds.c b/src/evdev-tablet-pad-leds.c
index 8b162a6..8ac00f5 100644
--- a/src/evdev-tablet-pad-leds.c
+++ b/src/evdev-tablet-pad-leds.c
@@ -29,14 +29,143 @@
 
 #include "evdev-tablet-pad.h"
 
+#if HAVE_LIBWACOM
+#include <libwacom/libwacom.h>
+#endif
+
 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;
+};
+
+struct pad_mode_led {
+       struct list link;
+       /* /sys/devices/..../input1235/input1235::wacom-led_0.1/brightness */
+       int brightness_fd;
+       int mode_idx;
+};
+
+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;
+
+       return button;
+}
+
+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;
+       }
+
+       return -EINVAL;
+}
+
+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);
+}
+
+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;
+
+       led = zalloc(sizeof *led);
+       if (!led)
+               return NULL;
+
+       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);
+       if (fd < 0) {
+               errno = -fd;
+               goto error;
+       }
+
+       led->brightness_fd = fd;
+
+       return led;
+
+error:
+       pad_led_destroy(libinput, led);
+       return NULL;
+}
+
 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, *tmp;
+       struct pad_mode_led *led, *tmpled;
+
+       list_for_each_safe(button, tmp, &group->toggle_button_list, link)
+               pad_mode_toggle_button_destroy(button);
+
+       list_for_each_safe(led, tmpled, &group->led_list, link)
+               pad_led_destroy(g->device->seat->libinput, led);
 
        free(group);
 }
@@ -58,10 +187,154 @@ pad_group_new_basic(struct pad_dispatch *pad,
        group->base.current_mode = 0;
        group->base.num_modes = nleds;
        group->base.destroy = pad_led_group_destroy;
+       list_init(&group->toggle_button_list);
+       list_init(&group->led_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;
+
+       group = pad_group_new_basic(pad, group_index, nleds);
+       if (!group)
+               return NULL;
+
+       while (nleds--) {
+               struct pad_mode_led *led;
+
+               led = pad_led_new(libinput, syspath, group_index, nleds);
+               if (!led)
+                       goto error;
+
+               list_insert(&group->led_list, &led->link);
+       }
+
+       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));
+       pad_led_group_destroy(&group->base);
+
+       return NULL;
+}
+
+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;
+}
+
+#if HAVE_LIBWACOM
+static int
+pad_init_led_groups(struct pad_dispatch *pad,
+                   struct evdev_device *device,
+                   WacomDevice *wacom)
+{
+       struct libinput *libinput = device->base.seat->libinput;
+       const WacomStatusLEDs *leds;
+       int nleds, nmodes;
+       int i;
+       struct pad_led_group *group;
+       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;
+
+       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:
+                       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;
+               }
+       }
+
+       return 0;
+}
+
+#endif
+
 static inline struct libinput_tablet_pad_mode_group *
 pad_get_mode_group(struct pad_dispatch *pad, unsigned int index)
 {
@@ -75,6 +348,192 @@ pad_get_mode_group(struct pad_dispatch *pad, unsigned int 
index)
        return NULL;
 }
 
+#if HAVE_LIBWACOM
+
+static inline int
+pad_find_button_group(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 libwacom_get_button_led_group(wacom, 'A' + i);
+       }
+
+       return -1;
+}
+
+static int
+pad_init_mode_buttons(struct pad_dispatch *pad,
+                     WacomDevice *wacom)
+{
+       struct libinput *libinput = pad_libinput_context(pad);
+       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) {
+                       log_bug_libinput(libinput,
+                                        "%s: unhandled position for button 
%i\n",
+                                        pad->device->devname,
+                                        i);
+                       return 1;
+               }
+
+               group = pad_get_mode_group(pad, group_idx);
+               if (!group) {
+                       log_bug_libinput(libinput,
+                                        "%s: Failed to find group %d for 
button %i\n",
+                                        pad->device->devname,
+                                        group_idx,
+                                        i);
+                       return 1;
+               }
+
+               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);
+                       if (!b)
+                               return 1;
+                       g = (struct pad_led_group*)group;
+                       list_insert(&g->toggle_button_list, &b->link);
+                       group->toggle_button_mask |= 1 << 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 *libinput = device->base.seat->libinput;
+       WacomDeviceDatabase *db = NULL;
+       WacomDevice *wacom = 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;
+
+       rc = pad_init_led_groups(pad, device, wacom);
+       if (rc != 0)
+               goto out;
+
+       if ((rc = pad_init_mode_buttons(pad, wacom)) != 0)
+               goto out;
+
+       pad_init_mode_rings(pad, wacom);
+       pad_init_mode_strips(pad, wacom);
+
+out:
+       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)
 {
@@ -112,11 +571,12 @@ pad_init_leds(struct pad_dispatch *pad,
                return rc;
        }
 
-       /* Eventually we slot the libwacom-based led detection in here. That
-        * requires getting the kernel ready first. For now we just init the
-        * fallback single-mode group.
-        */
-       rc = pad_init_fallback_group(pad);
+       /* 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;
 }
@@ -136,6 +596,7 @@ pad_button_update_mode(struct 
libinput_tablet_pad_mode_group *g,
                       enum libinput_button_state state)
 {
        struct pad_led_group *group = (struct pad_led_group*)g;
+       int rc;
 
        if (state != LIBINPUT_BUTTON_STATE_PRESSED)
                return;
@@ -143,9 +604,9 @@ pad_button_update_mode(struct 
libinput_tablet_pad_mode_group *g,
        if (!libinput_tablet_pad_mode_group_button_is_toggle(g, button_index))
                return;
 
-       log_bug_libinput(group->base.device->seat->libinput,
-                        "Button %d should not be a toggle button",
-                        button_index);
+       rc = pad_led_group_get_mode(group);
+       if (rc >= 0)
+               group->base.current_mode = rc;
 }
 
 int
-- 
2.9.3

_______________________________________________
wayland-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to