From 8ff8ceeca271dc50c2bd3343638726e868572da5 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 4 Jul 2026 13:36:31 +0200 Subject: [PATCH] fix(backlight): also match the leds subsystem for keyboard backlights The backlight module only enumerated and monitored the udev "backlight" subsystem, so keyboard-backlight LEDs in the "leds" class (e.g. white:kbd_backlight, platform::kbd_backlight) were never discovered and the module fell back to the default when pointed at one. Enumerate and monitor the "leds" subsystem in addition to "backlight". Those LEDs expose the same brightness/max_brightness attributes, so the read path is unchanged. Each device now records its subsystem so the login1 SetBrightness call targets the correct one. Automatic device selection still prefers a "backlight" device and only falls back to a "leds" device when named explicitly or when no screen backlight exists. Fixes #2848. --- include/util/backlight_backend.hpp | 8 +++-- man/waybar-backlight-slider.5.scd | 3 +- man/waybar-backlight.5.scd | 3 +- src/util/backlight_backend.cpp | 50 ++++++++++++++++++++++++------ 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/include/util/backlight_backend.hpp b/include/util/backlight_backend.hpp index ba3ccca7..8a81505c 100644 --- a/include/util/backlight_backend.hpp +++ b/include/util/backlight_backend.hpp @@ -27,9 +27,11 @@ namespace waybar::util { class BacklightDevice { public: BacklightDevice() = default; - BacklightDevice(std::string name, int actual, int max, bool powered); + BacklightDevice(std::string name, int actual, int max, bool powered, + std::string subsystem = "backlight"); std::string name() const; + std::string subsystem() const; int get_actual() const; void set_actual(int actual); int get_max() const; @@ -45,6 +47,7 @@ class BacklightDevice { int actual_ = 1; int max_ = 1; bool powered_ = true; + std::string subsystem_ = "backlight"; }; class BacklightBackend { @@ -70,7 +73,8 @@ class BacklightBackend { std::mutex udev_thread_mutex_; private: - void set_brightness_internal(const std::string& device_name, int brightness, int max_brightness); + void set_brightness_internal(const std::string& device_name, int brightness, int max_brightness, + const std::string& subsystem = "backlight"); std::function on_updated_cb_; std::chrono::milliseconds polling_interval_; diff --git a/man/waybar-backlight-slider.5.scd b/man/waybar-backlight-slider.5.scd index d357ff80..05e48b26 100644 --- a/man/waybar-backlight-slider.5.scd +++ b/man/waybar-backlight-slider.5.scd @@ -29,7 +29,8 @@ The brightness can be controlled by dragging the slider across the bar or clicki *device*: ++ typeof: string ++ - The name of the preferred device to control. If left empty, a device will be chosen automatically. + The name of the preferred device to control. If left empty, a device will be chosen automatically. ++ + Both screen backlights (the udev *backlight* subsystem) and keyboard backlights (LEDs in the udev *leds* subsystem, e.g. *white:kbd_backlight*) are supported; name such an LED here to control it. When left empty, a screen backlight is always preferred for automatic selection. *interval*: ++ typeof: uint ++ diff --git a/man/waybar-backlight.5.scd b/man/waybar-backlight.5.scd index 5a4d30db..e3414c6d 100644 --- a/man/waybar-backlight.5.scd +++ b/man/waybar-backlight.5.scd @@ -17,7 +17,8 @@ The *backlight* module displays the current backlight level. *device*: ++ typeof: string ++ - The name of the preferred backlight device to display. If left empty, a device will be chosen automatically. + The name of the preferred backlight device to display. If left empty, a device will be chosen automatically. ++ + Both screen backlights (the udev *backlight* subsystem) and keyboard backlights (LEDs in the udev *leds* subsystem, e.g. *white:kbd_backlight*) are supported; name such an LED here to control it. When left empty, a screen backlight is always preferred for automatic selection. *format*: ++ typeof: string ++ diff --git a/src/util/backlight_backend.cpp b/src/util/backlight_backend.cpp index 61eb9b43..bf669f15 100644 --- a/src/util/backlight_backend.cpp +++ b/src/util/backlight_backend.cpp @@ -73,6 +73,7 @@ static void upsert_device(std::vector& devices, udev_device* de const char* actual = udev_device_get_sysattr_value(dev, actual_brightness_attr); const char* max = udev_device_get_sysattr_value(dev, "max_brightness"); const char* power = udev_device_get_sysattr_value(dev, "bl_power"); + const char* subsystem = udev_device_get_subsystem(dev); auto found = std::find_if(devices.begin(), devices.end(), [name](const BacklightDevice& device) { return device.name() == name; @@ -111,13 +112,18 @@ static void upsert_device(std::vector& devices, udev_device* de if (power != nullptr) power_bool = std::stoi(power) == 0; } catch (const std::exception&) { } - devices.emplace_back(name, actual_int, max_int, power_bool); + devices.emplace_back(name, actual_int, max_int, power_bool, + subsystem != nullptr ? subsystem : "backlight"); } } static void enumerate_devices(std::vector& devices, udev* udev) { std::unique_ptr enumerate{udev_enumerate_new(udev)}; udev_enumerate_add_match_subsystem(enumerate.get(), "backlight"); + // Also enumerate keyboard-backlight LEDs (e.g. "white:kbd_backlight"), which + // live in the "leds" subsystem but expose the same brightness/max_brightness + // attributes the read path uses. + udev_enumerate_add_match_subsystem(enumerate.get(), "leds"); udev_enumerate_scan_devices(enumerate.get()); udev_list_entry* enum_devices = udev_enumerate_get_list_entry(enumerate.get()); udev_list_entry* dev_list_entry; @@ -129,11 +135,18 @@ static void enumerate_devices(std::vector& devices, udev* udev) } } -BacklightDevice::BacklightDevice(std::string name, int actual, int max, bool powered) - : name_(std::move(name)), actual_(actual), max_(max), powered_(powered) {} +BacklightDevice::BacklightDevice(std::string name, int actual, int max, bool powered, + std::string subsystem) + : name_(std::move(name)), + actual_(actual), + max_(max), + powered_(powered), + subsystem_(std::move(subsystem)) {} std::string BacklightDevice::name() const { return name_; } +std::string BacklightDevice::subsystem() const { return subsystem_; } + int BacklightDevice::get_actual() const { return actual_; } void BacklightDevice::set_actual(int actual) { actual_ = actual; } @@ -178,6 +191,10 @@ BacklightBackend::BacklightBackend(std::chrono::milliseconds interval, check_nn(mon.get(), "udev monitor new failed"); check_gte(udev_monitor_filter_add_match_subsystem_devtype(mon.get(), "backlight", nullptr), 0, "udev failed to add monitor filter: "); + // Also monitor the "leds" subsystem so keyboard-backlight changes are + // reflected live, mirroring the enumeration above. + check_gte(udev_monitor_filter_add_match_subsystem_devtype(mon.get(), "leds", nullptr), 0, + "udev failed to add monitor filter: "); udev_monitor_enable_receiving(mon.get()); auto udev_fd = udev_monitor_get_fd(mon.get()); @@ -235,9 +252,22 @@ const BacklightDevice* BacklightBackend::best_device(const std::vectorget_max(); const auto abs_val = static_cast(std::round(brightness * max / 100.0F)); - set_brightness_internal(best->name(), abs_val, best->get_max()); + set_brightness_internal(best->name(), abs_val, best->get_max(), best->subsystem()); } } @@ -275,12 +305,12 @@ void BacklightBackend::set_brightness(const std::string& preferred_device, Chang const int new_brightness = change_type == ChangeType::Increase ? best->get_actual() + abs_step : best->get_actual() - abs_step; - set_brightness_internal(best->name(), new_brightness, max); + set_brightness_internal(best->name(), new_brightness, max, best->subsystem()); } } void BacklightBackend::set_brightness_internal(const std::string& device_name, int brightness, - int max_brightness) { + int max_brightness, const std::string& subsystem) { if (!login_proxy_) { spdlog::error("Login proxy not available, cannot set brightness"); return; @@ -289,7 +319,7 @@ void BacklightBackend::set_brightness_internal(const std::string& device_name, i brightness = std::clamp(brightness, 0, max_brightness); auto call_args = Glib::VariantContainerBase( - g_variant_new("(ssu)", "backlight", device_name.c_str(), brightness)); + g_variant_new("(ssu)", subsystem.c_str(), device_name.c_str(), brightness)); login_proxy_->call_sync("SetBrightness", call_args); }