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); }