From f3ae13e44600f3585b32bf9b50258b3c3f19a1dc Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Tue, 13 May 2025 21:40:39 +0200 Subject: [PATCH 1/6] cpu: add load{1,5,15} format variables --- man/waybar-cpu.5.scd | 6 ++++++ src/modules/cpu.cpp | 3 +++ 2 files changed, 9 insertions(+) diff --git a/man/waybar-cpu.5.scd b/man/waybar-cpu.5.scd index 287bf123..3e06f35c 100644 --- a/man/waybar-cpu.5.scd +++ b/man/waybar-cpu.5.scd @@ -91,6 +91,12 @@ The *cpu* module displays the current CPU utilization. *{load}*: Current CPU load. +*{load1}*: CPU load average over the last minute. + +*{load5}*: CPU load average over the last 5 minutes. + +*{load15}*: CPU load average over the last 15 minutes. + *{usage}*: Current overall CPU usage. *{usage*{n}*}*: Current CPU core n usage. Cores are numbered from zero, so first core will be {usage0} and 4th will be {usage3}. diff --git a/src/modules/cpu.cpp b/src/modules/cpu.cpp index 0703eaf7..45035a81 100644 --- a/src/modules/cpu.cpp +++ b/src/modules/cpu.cpp @@ -43,6 +43,9 @@ auto waybar::modules::Cpu::update() -> void { auto icons = std::vector{state}; fmt::dynamic_format_arg_store store; store.push_back(fmt::arg("load", load1)); + store.push_back(fmt::arg("load1", load1)); + store.push_back(fmt::arg("load5", load5)); + store.push_back(fmt::arg("load15", load15)); store.push_back(fmt::arg("usage", total_usage)); store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); store.push_back(fmt::arg("max_frequency", max_frequency)); From ce029c341939b88a91ec167ef1968bba2876158c Mon Sep 17 00:00:00 2001 From: Kaosu Date: Sat, 27 Dec 2025 08:16:03 +0100 Subject: [PATCH 2/6] temperature: add hwmon-name option for stable sensor selection Allow selecting a hwmon device for the temperature module by its name instead of a fixed hwmon path. This avoids fragile configurations that break when new hwmon devices are added and the kernel changes hwmon ordering. The hwmon-name option is mutually exclusive with hwmon-path and hwmon-path-abs to prevent ambiguous configurations. --- man/waybar-temperature.5.scd | 6 ++++++ src/modules/temperature.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index 1e19ec82..672a2518 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -27,6 +27,12 @@ Addressed by *temperature* The path of the hwmon-directory of the device, e.g. */sys/devices/pci0000:00/0000:00:18.3/hwmon*. (Note that the subdirectory *hwmon/hwmon#*, where *#* is a number is not part of the path!) Has to be used together with *input-filename*. This can also be an array of strings, for which, it just works like *hwmon-path*. +*hwmon-name*: ++ + typeof: string ++ + Select a hwmon device by its name (from /sys/class/hwmon/*/name), e.g. *amdgpu* + Requires *input-filename* to be set. + Cannot be used together with *hwmon-path* or *hwmon-path-abs*. + *input-filename*: ++ typeof: string ++ The temperature filename of your *hwmon-path-abs*, e.g. *temp1_input* diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index fa23ef56..d8108577 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -20,6 +20,24 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val if (check_set_path(item.asString())) break; }; + auto find_hwmon_by_name = [](const std::string& name) -> std::optional { + for (const auto& entry : std::filesystem::directory_iterator("/sys/class/hwmon")) { + std::ifstream f(entry.path() / "name"); + std::string hwname; + if (f >> hwname && hwname == name) { + return entry.path(); + } + } + return std::nullopt; + }; + + // ensure either hwmon-name OR old paths are used, not both + if (config_["hwmon-name"].isString() && + (!config_["hwmon-path"].isNull() || !config_["hwmon-path-abs"].isNull())) { + throw std::runtime_error( + "hwmon-name cannot be used together with hwmon-path or hwmon-path-abs"); + } + // if hwmon_path is an array, loop to find first valid item traverseAsArray(config_["hwmon-path"], [this](const std::string& path) { if (!std::filesystem::exists(path)) return false; @@ -40,6 +58,20 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val }); } + if (file_path_.empty() && config_["hwmon-name"].isString()) { + if (!config_["input-filename"].isString()) { + throw std::runtime_error("hwmon-name requires input-filename to be set"); + } + + auto hwmon = find_hwmon_by_name(config_["hwmon-name"].asString()); + + if (!hwmon) { + throw std::runtime_error("hwmon-name '" + config_["hwmon-name"].asString() + "' not found"); + } + + file_path_ = hwmon->string() + "/" + config_["input-filename"].asString(); + } + if (file_path_.empty()) { auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone); From 61af3e95faa77afc699aa142846e9feee793a862 Mon Sep 17 00:00:00 2001 From: Kaosu Date: Sat, 27 Dec 2025 08:38:14 +0100 Subject: [PATCH 3/6] fix the doc --- man/waybar-temperature.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index 672a2518..0783331e 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -29,7 +29,7 @@ Addressed by *temperature* *hwmon-name*: ++ typeof: string ++ - Select a hwmon device by its name (from /sys/class/hwmon/*/name), e.g. *amdgpu* + Select a hwmon device by its name (from /sys/class/hwmon/\*/name), e.g. *amdgpu*. Requires *input-filename* to be set. Cannot be used together with *hwmon-path* or *hwmon-path-abs*. From 0351ac8fb2d0f204f67f9b2ddbe7a1c8d54ba413 Mon Sep 17 00:00:00 2001 From: finitemonkey <56678540+finitemonkey@users.noreply.github.com> Date: Sat, 16 May 2026 20:40:01 +0100 Subject: [PATCH 4/6] Additional options when muted for the PulseAudio-Slider module: - Add "muted" class for styling the muted state. - Add "zero-on-mute" boolean option to control slider position when muted. - Add "unmute-on-volume-change" boolean option to control whether to automatically unmute when the volume changes. --- include/modules/pulseaudio_slider.hpp | 16 +++-- include/util/audio_backend.hpp | 11 +++- man/waybar-pulseaudio-slider.5.scd | 28 +++++++-- src/modules/pulseaudio_slider.cpp | 84 ++++++++++----------------- src/util/audio_backend.cpp | 24 ++++++++ 5 files changed, 98 insertions(+), 65 deletions(-) diff --git a/include/modules/pulseaudio_slider.hpp b/include/modules/pulseaudio_slider.hpp index 3ef44684..c9f82341 100644 --- a/include/modules/pulseaudio_slider.hpp +++ b/include/modules/pulseaudio_slider.hpp @@ -6,10 +6,6 @@ #include "util/audio_backend.hpp" namespace waybar::modules { -enum class PulseaudioSliderTarget { - Sink, - Source, -}; class PulseaudioSlider : public ASlider { public: @@ -21,7 +17,15 @@ class PulseaudioSlider : public ASlider { private: std::shared_ptr backend = nullptr; - PulseaudioSliderTarget target = PulseaudioSliderTarget::Sink; + util::PulseaudioTarget target = util::PulseaudioTarget::Sink; + + bool zero_on_mute = true; + bool unmute_on_volume_change = true; + // zero_on_mute and unmute_on_volume_change default to true + // in order to maintain previous behaviour when using a + // config in which these values are undefined + + bool previously_muted = false; }; -} // namespace waybar::modules \ No newline at end of file +} // namespace waybar::modules diff --git a/include/util/audio_backend.hpp b/include/util/audio_backend.hpp index 3737ae26..f5904b43 100644 --- a/include/util/audio_backend.hpp +++ b/include/util/audio_backend.hpp @@ -14,6 +14,11 @@ namespace waybar::util { +enum class PulseaudioTarget { + Sink, + Source, +}; + class AudioBackend { private: static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*); @@ -92,7 +97,11 @@ class AudioBackend { void toggleSourceMute(); void toggleSourceMute(bool); + uint16_t getVolume(PulseaudioTarget) const; + bool getMuted(PulseaudioTarget) const; + void unmute(PulseaudioTarget); + bool isBluetooth(); }; -} // namespace waybar::util \ No newline at end of file +} // namespace waybar::util diff --git a/man/waybar-pulseaudio-slider.5.scd b/man/waybar-pulseaudio-slider.5.scd index 0271e7c5..3f56a80c 100644 --- a/man/waybar-pulseaudio-slider.5.scd +++ b/man/waybar-pulseaudio-slider.5.scd @@ -28,9 +28,20 @@ The volume can be controlled by dragging the slider across the bar or clicking o The orientation of the slider. Can be either `horizontal` or `vertical`. *expand*: ++ - typeof: bool ++ - default: false ++ - Enables this module to consume all left over space dynamically. + typeof: bool ++ + default: false ++ + Enables this module to consume all left over space dynamically. + +*zero-on-mute*: ++ + typeof: bool ++ + default: true ++ + `true` = The slider will be set to `min` when the source/sink is muted. ++ + `false` = The slider will continue to show the unmuted volume level when the source/sink is muted. + +*unmute-on-volume-change*: ++ + typeof: bool ++ + default: true ++ + Specifies whether to unmute a muted souce/sink when its volume is changed by the user moving the slider. # EXAMPLES @@ -41,7 +52,9 @@ The volume can be controlled by dragging the slider across the bar or clicking o "pulseaudio/slider": { "min": 0, "max": 100, - "orientation": "horizontal" + "orientation": "horizontal", + "zero-on-mute": false, + "unmute-on-volume-change": false } ``` @@ -52,6 +65,9 @@ The slider is a component with multiple CSS Nodes, of which the following are ex *#pulseaudio-slider*: ++ Controls the style of the box *around* the slider and bar. +*#pulseaudio-slider.muted*: ++ + Controls the style when the audio source/sink is muted. + *#pulseaudio-slider slider*: ++ Controls the style of the slider handle. @@ -85,4 +101,8 @@ The slider is a component with multiple CSS Nodes, of which the following are ex border-radius: 5px; background: green; } + +#pulseaudio-slider.muted highlight { + background-color: orange; +} ``` diff --git a/src/modules/pulseaudio_slider.cpp b/src/modules/pulseaudio_slider.cpp index bf85584e..aeddeb24 100644 --- a/src/modules/pulseaudio_slider.cpp +++ b/src/modules/pulseaudio_slider.cpp @@ -10,73 +10,49 @@ PulseaudioSlider::PulseaudioSlider(const std::string& id, const Json::Value& con if (config_["target"].isString()) { std::string target = config_["target"].asString(); if (target == "sink") { - this->target = PulseaudioSliderTarget::Sink; + this->target = util::PulseaudioTarget::Sink; } else if (target == "source") { - this->target = PulseaudioSliderTarget::Source; + this->target = util::PulseaudioTarget::Source; } } + + if (config_["zero-on-mute"].isBool()) { + zero_on_mute = config_["zero-on-mute"].asBool(); + } + + if (config_["unmute-on-volume-change"].isBool()) { + unmute_on_volume_change = config_["unmute-on-volume-change"].asBool(); + } } void PulseaudioSlider::update() { - switch (target) { - case PulseaudioSliderTarget::Sink: - if (backend->getSinkMuted()) { - scale_.set_value(min_); - } else { - scale_.set_value(backend->getSinkVolume()); - } - break; + uint16_t display_value = backend->getVolume(target); + bool is_muted = backend->getMuted(target); - case PulseaudioSliderTarget::Source: - if (backend->getSourceMuted()) { - scale_.set_value(min_); - } else { - scale_.set_value(backend->getSourceVolume()); - } - break; + if (is_muted && !previously_muted) { + if (zero_on_mute) { + display_value = min_; + } + scale_.get_style_context()->add_class("muted"); + } else if (previously_muted && !is_muted) { + scale_.get_style_context()->remove_class("muted"); } + + scale_.set_value(display_value); + + previously_muted = is_muted; } void PulseaudioSlider::onValueChanged() { - bool is_mute = false; + uint16_t slider_value = scale_.get_value(); - switch (target) { - case PulseaudioSliderTarget::Sink: - if (backend->getSinkMuted()) { - is_mute = true; - } - break; - - case PulseaudioSliderTarget::Source: - if (backend->getSourceMuted()) { - is_mute = true; - } - break; - } - - uint16_t volume = scale_.get_value(); - - if (is_mute) { - // Avoid setting sink/source to volume 0 if the user muted if via another mean. - if (volume == 0) { - return; - } - - // If the sink/source is mute, but the user clicked the slider, unmute it! - else { - switch (target) { - case PulseaudioSliderTarget::Sink: - backend->toggleSinkMute(false); - break; - - case PulseaudioSliderTarget::Source: - backend->toggleSourceMute(false); - break; - } + // Avoid setting sink/source to volume 0 if the user muted it via other means. + if (!backend->getMuted(target) || slider_value != 0) { + if (unmute_on_volume_change) { + backend->unmute(target); } + backend->changeVolume(slider_value, min_, max_); } - - backend->changeVolume(volume, min_, max_); } -} // namespace waybar::modules \ No newline at end of file +} // namespace waybar::modules diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index 4087e096..d617813d 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -259,6 +259,14 @@ void AudioBackend::serverInfoCb(pa_context* context, const pa_server_info* i, vo pa_context_get_source_info_list(context, sourceInfoCb, data); } +uint16_t AudioBackend::getVolume(PulseaudioTarget target) const { + if (target == PulseaudioTarget::Source) { + return source_volume_; + } else { + return volume_; + } +} + void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t max_volume) { // Early return if context is not ready if ((context_ == nullptr) || pa_context_get_state(context_) != PA_CONTEXT_READY) { @@ -368,6 +376,14 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma pa_threaded_mainloop_unlock(mainloop_); } +bool AudioBackend::getMuted(PulseaudioTarget target) const { + if (target == PulseaudioTarget::Source) { + return source_muted_; + } else { + return muted_; + } +} + void AudioBackend::toggleSinkMute() { if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return; muted_ = !muted_; @@ -404,6 +420,14 @@ void AudioBackend::toggleSourceMute(bool mute) { pa_threaded_mainloop_unlock(mainloop_); } +void AudioBackend::unmute(PulseaudioTarget target) { + if (target == PulseaudioTarget::Source) { + toggleSourceMute(false); + } else { + toggleSinkMute(false); + } +} + bool AudioBackend::isBluetooth() { return monitor_.find("a2dp_sink") != std::string::npos || // PulseAudio monitor_.find("a2dp-sink") != std::string::npos || // PipeWire From cbc70d10f81f85ab51f5c18d04fecbce340c7f53 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 3 Jul 2026 21:49:54 +0200 Subject: [PATCH 5/6] pulseaudio_slider: keep zero-on-mute applied on every update while muted The zero-on-mute display_value = min_ assignment was nested inside the !previously_muted transition branch, so subsequent updates while still muted re-displayed the real volume. Apply it on every update as long as the target is muted, keeping the CSS class add/remove on transitions only. --- src/modules/pulseaudio_slider.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/modules/pulseaudio_slider.cpp b/src/modules/pulseaudio_slider.cpp index aeddeb24..d0e54459 100644 --- a/src/modules/pulseaudio_slider.cpp +++ b/src/modules/pulseaudio_slider.cpp @@ -29,12 +29,14 @@ void PulseaudioSlider::update() { uint16_t display_value = backend->getVolume(target); bool is_muted = backend->getMuted(target); - if (is_muted && !previously_muted) { + if (is_muted) { if (zero_on_mute) { display_value = min_; } - scale_.get_style_context()->add_class("muted"); - } else if (previously_muted && !is_muted) { + if (!previously_muted) { + scale_.get_style_context()->add_class("muted"); + } + } else if (previously_muted) { scale_.get_style_context()->remove_class("muted"); } From 4bf82b88a06a8ee10f4468616d177087b904eae6 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 3 Jul 2026 22:01:20 +0200 Subject: [PATCH 6/6] style(pulseaudio): clang-format audio_backend helpers --- include/modules/pulseaudio_slider.hpp | 1 - src/util/audio_backend.cpp | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/modules/pulseaudio_slider.hpp b/include/modules/pulseaudio_slider.hpp index c9f82341..597b3cc8 100644 --- a/include/modules/pulseaudio_slider.hpp +++ b/include/modules/pulseaudio_slider.hpp @@ -6,7 +6,6 @@ #include "util/audio_backend.hpp" namespace waybar::modules { - class PulseaudioSlider : public ASlider { public: PulseaudioSlider(const std::string&, const Json::Value&); diff --git a/src/util/audio_backend.cpp b/src/util/audio_backend.cpp index 601a9cc0..96a5bdb4 100644 --- a/src/util/audio_backend.cpp +++ b/src/util/audio_backend.cpp @@ -263,9 +263,9 @@ void AudioBackend::serverInfoCb(pa_context* context, const pa_server_info* i, vo uint16_t AudioBackend::getVolume(PulseaudioTarget target) const { if (target == PulseaudioTarget::Source) { - return source_volume_; + return source_volume_; } else { - return volume_; + return volume_; } } @@ -380,9 +380,9 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma bool AudioBackend::getMuted(PulseaudioTarget target) const { if (target == PulseaudioTarget::Source) { - return source_muted_; + return source_muted_; } else { - return muted_; + return muted_; } } @@ -424,9 +424,9 @@ void AudioBackend::toggleSourceMute(bool mute) { void AudioBackend::unmute(PulseaudioTarget target) { if (target == PulseaudioTarget::Source) { - toggleSourceMute(false); + toggleSourceMute(false); } else { - toggleSinkMute(false); + toggleSinkMute(false); } }