Files
Waybar/src/modules/pulseaudio.cpp
Philipp Hentschel dedee8cd14 pulseaudio: show correct sink volume on default output changes
on sinkInfo callbacks, the default sink now has highest priority.
That fixes an issue that the volume indicator is not updated when
the changes the default output to another devices.

added PA_SINK_IDLE as valid state. PA_SINK_RUNNING is only true
if any sound output is happening on sink switch. Indicator should
also update when no sound is being played.
2024-09-21 13:25:36 +02:00

155 lines
5.5 KiB
C++

#include "modules/pulseaudio.hpp"
#include <spdlog/spdlog.h>
#include <numeric>
waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config)
: ALabel(config, "pulseaudio", id, "{volume}%") {
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Pulseaudio::handleScroll));
backend = util::AudioBackend::getInstance([this] { this->dp.emit(); });
backend->setIgnoredSinks(config_["ignored-sinks"]);
}
bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
// change the pulse volume only when no user provided
// events are configured
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
return AModule::handleScroll(e);
}
auto dir = AModule::getScrollDir(e);
if (dir == SCROLL_DIR::NONE) {
return true;
}
int max_volume = 100;
double step = 1;
// isDouble returns true for integers as well, just in case
if (config_["scroll-step"].isDouble()) {
step = config_["scroll-step"].asDouble();
}
if (config_["max-volume"].isInt()) {
max_volume = config_["max-volume"].asInt();
}
auto change_type = (dir == SCROLL_DIR::UP || dir == SCROLL_DIR::RIGHT)
? util::ChangeType::Increase
: util::ChangeType::Decrease;
backend->changeVolume(change_type, step, max_volume);
return true;
}
static const std::array<std::string, 9> ports = {
"headphone", "speaker", "hdmi", "headset", "hands-free", "portable", "car", "hifi", "phone",
};
const std::vector<std::string> waybar::modules::Pulseaudio::getPulseIcon() const {
std::vector<std::string> res;
auto sink_muted = backend->getSinkMuted();
if (sink_muted) {
res.emplace_back(backend->getCurrentSinkName() + "-muted");
}
res.push_back(backend->getCurrentSinkName());
res.push_back(backend->getDefaultSourceName());
std::string nameLC = backend->getSinkPortName() + backend->getFormFactor();
std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower);
for (auto const &port : ports) {
spdlog::trace("Port: {}", nameLC);
if (nameLC.find(port) != std::string::npos) {
if (sink_muted) {
res.emplace_back(port + "-muted");
}
res.push_back(port);
break;
}
}
if (sink_muted) {
res.emplace_back("default-muted");
}
spdlog::trace("Ports:");
for (auto const &item : res) {
spdlog::trace(" {}", item);
}
return res;
}
auto waybar::modules::Pulseaudio::update() -> void {
auto format = format_;
std::string tooltip_format;
auto sink_volume = backend->getSinkVolume();
if (!alt_) {
std::string format_name = "format";
if (backend->isBluetooth()) {
format_name = format_name + "-bluetooth";
label_.get_style_context()->add_class("bluetooth");
} else {
label_.get_style_context()->remove_class("bluetooth");
}
if (backend->getSinkMuted()) {
// Check muted bluetooth format exist, otherwise fallback to default muted format
if (format_name != "format" && !config_[format_name + "-muted"].isString()) {
format_name = "format";
}
format_name = format_name + "-muted";
label_.get_style_context()->add_class("muted");
label_.get_style_context()->add_class("sink-muted");
} else {
label_.get_style_context()->remove_class("muted");
label_.get_style_context()->remove_class("sink-muted");
}
auto state = getState(sink_volume, true);
if (!state.empty() && config_[format_name + "-" + state].isString()) {
format = config_[format_name + "-" + state].asString();
} else if (config_[format_name].isString()) {
format = config_[format_name].asString();
}
}
// TODO: find a better way to split source/sink
std::string format_source = "{volume}%";
if (backend->getSourceMuted()) {
label_.get_style_context()->add_class("source-muted");
if (config_["format-source-muted"].isString()) {
format_source = config_["format-source-muted"].asString();
}
} else {
label_.get_style_context()->remove_class("source-muted");
if (config_["format-source"].isString()) {
format_source = config_["format-source"].asString();
}
}
auto source_volume = backend->getSourceVolume();
auto sink_desc = backend->getSinkDesc();
auto source_desc = backend->getSourceDesc();
format_source = fmt::format(fmt::runtime(format_source), fmt::arg("volume", source_volume));
auto text = fmt::format(
fmt::runtime(format), fmt::arg("desc", sink_desc), fmt::arg("volume", sink_volume),
fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume),
fmt::arg("source_desc", source_desc), fmt::arg("icon", getIcon(sink_volume, getPulseIcon())));
if (text.empty()) {
label_.hide();
} else {
label_.set_markup(text);
label_.show();
}
if (tooltipEnabled()) {
if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString();
}
if (!tooltip_format.empty()) {
label_.set_tooltip_text(fmt::format(
fmt::runtime(tooltip_format), fmt::arg("desc", sink_desc),
fmt::arg("volume", sink_volume), fmt::arg("format_source", format_source),
fmt::arg("source_volume", source_volume), fmt::arg("source_desc", source_desc),
fmt::arg("icon", getIcon(sink_volume, getPulseIcon()))));
} else {
label_.set_tooltip_text(sink_desc);
}
}
// Call parent update
ALabel::update();
}