Merge remote-tracking branch 'origin/master' into feature-clock-calendar-css-classes

This commit is contained in:
Alex
2026-07-03 22:20:49 +02:00
9 changed files with 147 additions and 66 deletions
+10 -7
View File
@@ -6,11 +6,6 @@
#include "util/audio_backend.hpp" #include "util/audio_backend.hpp"
namespace waybar::modules { namespace waybar::modules {
enum class PulseaudioSliderTarget {
Sink,
Source,
};
class PulseaudioSlider : public ASlider { class PulseaudioSlider : public ASlider {
public: public:
PulseaudioSlider(const std::string&, const Json::Value&); PulseaudioSlider(const std::string&, const Json::Value&);
@@ -21,7 +16,15 @@ class PulseaudioSlider : public ASlider {
private: private:
std::shared_ptr<util::AudioBackend> backend = nullptr; std::shared_ptr<util::AudioBackend> 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 } // namespace waybar::modules
+10 -1
View File
@@ -14,6 +14,11 @@
namespace waybar::util { namespace waybar::util {
enum class PulseaudioTarget {
Sink,
Source,
};
class AudioBackend { class AudioBackend {
private: private:
static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*); static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*);
@@ -92,7 +97,11 @@ class AudioBackend {
void toggleSourceMute(); void toggleSourceMute();
void toggleSourceMute(bool); void toggleSourceMute(bool);
uint16_t getVolume(PulseaudioTarget) const;
bool getMuted(PulseaudioTarget) const;
void unmute(PulseaudioTarget);
bool isBluetooth(); bool isBluetooth();
}; };
} // namespace waybar::util } // namespace waybar::util
+6
View File
@@ -92,6 +92,12 @@ The *cpu* module displays the current CPU utilization.
*{load}*: Current CPU load. *{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}*: 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}. *{usage*{n}*}*: Current CPU core n usage. Cores are numbered from zero, so first core will be {usage0} and 4th will be {usage3}.
+24 -4
View File
@@ -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`. The orientation of the slider. Can be either `horizontal` or `vertical`.
*expand*: ++ *expand*: ++
typeof: bool ++ typeof: bool ++
default: false ++ default: false ++
Enables this module to consume all left over space dynamically. 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 # EXAMPLES
@@ -41,7 +52,9 @@ The volume can be controlled by dragging the slider across the bar or clicking o
"pulseaudio/slider": { "pulseaudio/slider": {
"min": 0, "min": 0,
"max": 100, "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*: ++ *#pulseaudio-slider*: ++
Controls the style of the box *around* the slider and bar. 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*: ++ *#pulseaudio-slider slider*: ++
Controls the style of the slider handle. 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; border-radius: 5px;
background: green; background: green;
} }
#pulseaudio-slider.muted highlight {
background-color: orange;
}
``` ```
+6
View File
@@ -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*. 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*. 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*: ++ *input-filename*: ++
typeof: string ++ typeof: string ++
The temperature filename of your *hwmon-path-abs*, e.g. *temp1_input* The temperature filename of your *hwmon-path-abs*, e.g. *temp1_input*
+3
View File
@@ -41,6 +41,9 @@ auto waybar::modules::Cpu::update() -> void {
auto icons = std::vector<std::string>{state}; auto icons = std::vector<std::string>{state};
fmt::dynamic_format_arg_store<fmt::format_context> store; fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(fmt::arg("load", load1)); 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("usage", total_usage));
store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); store.push_back(fmt::arg("icon", getIcon(total_usage, icons)));
store.push_back(fmt::arg("max_frequency", max_frequency)); store.push_back(fmt::arg("max_frequency", max_frequency));
+32 -54
View File
@@ -10,73 +10,51 @@ PulseaudioSlider::PulseaudioSlider(const std::string& id, const Json::Value& con
if (config_["target"].isString()) { if (config_["target"].isString()) {
std::string target = config_["target"].asString(); std::string target = config_["target"].asString();
if (target == "sink") { if (target == "sink") {
this->target = PulseaudioSliderTarget::Sink; this->target = util::PulseaudioTarget::Sink;
} else if (target == "source") { } 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() { void PulseaudioSlider::update() {
switch (target) { uint16_t display_value = backend->getVolume(target);
case PulseaudioSliderTarget::Sink: bool is_muted = backend->getMuted(target);
if (backend->getSinkMuted()) {
scale_.set_value(min_);
} else {
scale_.set_value(backend->getSinkVolume());
}
break;
case PulseaudioSliderTarget::Source: if (is_muted) {
if (backend->getSourceMuted()) { if (zero_on_mute) {
scale_.set_value(min_); display_value = min_;
} else { }
scale_.set_value(backend->getSourceVolume()); if (!previously_muted) {
} scale_.get_style_context()->add_class("muted");
break; }
} else if (previously_muted) {
scale_.get_style_context()->remove_class("muted");
} }
scale_.set_value(display_value);
previously_muted = is_muted;
} }
void PulseaudioSlider::onValueChanged() { void PulseaudioSlider::onValueChanged() {
bool is_mute = false; uint16_t slider_value = scale_.get_value();
switch (target) { // Avoid setting sink/source to volume 0 if the user muted it via other means.
case PulseaudioSliderTarget::Sink: if (!backend->getMuted(target) || slider_value != 0) {
if (backend->getSinkMuted()) { if (unmute_on_volume_change) {
is_mute = true; backend->unmute(target);
}
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;
}
} }
backend->changeVolume(slider_value, min_, max_);
} }
backend->changeVolume(volume, min_, max_);
} }
} // namespace waybar::modules } // namespace waybar::modules
+32
View File
@@ -20,6 +20,24 @@ waybar::modules::Temperature::Temperature(const std::string& id, const Json::Val
if (check_set_path(item.asString())) break; if (check_set_path(item.asString())) break;
}; };
auto find_hwmon_by_name = [](const std::string& name) -> std::optional<std::filesystem::path> {
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 // if hwmon_path is an array, loop to find first valid item
traverseAsArray(config_["hwmon-path"], [this](const std::string& path) { traverseAsArray(config_["hwmon-path"], [this](const std::string& path) {
if (!std::filesystem::exists(path)) return false; 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()) { if (file_path_.empty()) {
auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0; auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0;
file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone); file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone);
+24
View File
@@ -261,6 +261,14 @@ void AudioBackend::serverInfoCb(pa_context* context, const pa_server_info* i, vo
pa_context_get_source_info_list(context, sourceInfoCb, data); 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) { void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t max_volume) {
// Early return if context is not ready // Early return if context is not ready
if ((context_ == nullptr) || pa_context_get_state(context_) != PA_CONTEXT_READY) { if ((context_ == nullptr) || pa_context_get_state(context_) != PA_CONTEXT_READY) {
@@ -370,6 +378,14 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma
pa_threaded_mainloop_unlock(mainloop_); pa_threaded_mainloop_unlock(mainloop_);
} }
bool AudioBackend::getMuted(PulseaudioTarget target) const {
if (target == PulseaudioTarget::Source) {
return source_muted_;
} else {
return muted_;
}
}
void AudioBackend::toggleSinkMute() { void AudioBackend::toggleSinkMute() {
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return; if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
muted_ = !muted_; muted_ = !muted_;
@@ -406,6 +422,14 @@ void AudioBackend::toggleSourceMute(bool mute) {
pa_threaded_mainloop_unlock(mainloop_); pa_threaded_mainloop_unlock(mainloop_);
} }
void AudioBackend::unmute(PulseaudioTarget target) {
if (target == PulseaudioTarget::Source) {
toggleSourceMute(false);
} else {
toggleSinkMute(false);
}
}
bool AudioBackend::isBluetooth() { bool AudioBackend::isBluetooth() {
return monitor_.find("a2dp_sink") != std::string::npos || // PulseAudio return monitor_.find("a2dp_sink") != std::string::npos || // PulseAudio
monitor_.find("a2dp-sink") != std::string::npos || // PipeWire monitor_.find("a2dp-sink") != std::string::npos || // PipeWire