From b2d15d1e3ab5508cf558b26369eed335424a5643 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 4 Jul 2026 12:36:32 +0200 Subject: [PATCH] fix(keyboard-state): read lock LEDs from all devices, not just the first When no explicit device-path is configured, a multi-node keyboard can expose several event devices where only one actually toggles the lock LEDs. Previously update() read state from a single arbitrary device (libinput_devices_.begin()), which often picked a node whose EV_LED values never change, leaving the indicator stuck. Iterate all devices and OR their NUML/CAPSL/SCROLLL values together so a lock is reported on if any device reports it on. The single-device path is preserved when device-path is set. Fixes #2215. --- src/modules/keyboard_state.cpp | 55 ++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 458cf0af..87a843c4 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -297,31 +297,40 @@ auto waybar::modules::KeyboardState::update() -> void { sleep(0); // Wait for keyboard status change int numl = 0, capsl = 0, scrolll = 0; - try { - std::string dev_path; - { - std::lock_guard lock(devices_mutex_); - if (libinput_devices_.empty()) { - return; - } - if (config_["device-path"].isString() && - libinput_devices_.find(config_["device-path"].asString()) != libinput_devices_.end()) { - dev_path = config_["device-path"].asString(); - } else { - dev_path = libinput_devices_.begin()->first; + std::vector dev_paths; + { + std::lock_guard lock(devices_mutex_); + if (libinput_devices_.empty()) { + return; + } + if (config_["device-path"].isString() && + libinput_devices_.find(config_["device-path"].asString()) != libinput_devices_.end()) { + // An explicit device was configured: read lock state from just that device. + dev_paths.push_back(config_["device-path"].asString()); + } else { + // No explicit device: a multi-node keyboard may expose several event + // devices where only one of them actually toggles the lock LEDs. OR the + // LED values across all devices so a lock is reported on if any device + // reports it on. + for (const auto& [dev_path, _] : libinput_devices_) { + dev_paths.push_back(dev_path); } } - int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY); - auto dev = openDevice(fd); - numl = libevdev_get_event_value(dev, EV_LED, LED_NUML); - capsl = libevdev_get_event_value(dev, EV_LED, LED_CAPSL); - scrolll = libevdev_get_event_value(dev, EV_LED, LED_SCROLLL); - libevdev_free(dev); - closeFile(fd); - } catch (const errno_error& e) { - // ENOTTY just means the device isn't an evdev device, skip it - if (e.code != ENOTTY) { - spdlog::warn(e.what()); + } + for (const auto& dev_path : dev_paths) { + try { + int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY); + auto dev = openDevice(fd); + numl |= libevdev_get_event_value(dev, EV_LED, LED_NUML); + capsl |= libevdev_get_event_value(dev, EV_LED, LED_CAPSL); + scrolll |= libevdev_get_event_value(dev, EV_LED, LED_SCROLLL); + libevdev_free(dev); + closeFile(fd); + } catch (const errno_error& e) { + // ENOTTY just means the device isn't an evdev device, skip it + if (e.code != ENOTTY) { + spdlog::warn(e.what()); + } } }