Waybar/src/modules/pulseaudio.cpp

149 lines
4.4 KiB
C++
Raw Normal View History

2018-08-09 21:55:38 +00:00
#include "modules/pulseaudio.hpp"
2018-08-20 12:50:45 +00:00
waybar::modules::Pulseaudio::Pulseaudio(const Json::Value& config)
2018-08-26 23:36:25 +00:00
: ALabel(config, "{volume}%"), mainloop_(nullptr), mainloop_api_(nullptr),
2018-08-16 12:29:41 +00:00
context_(nullptr), sink_idx_(0), volume_(0), muted_(false)
2018-08-09 21:55:38 +00:00
{
2018-08-16 12:29:41 +00:00
label_.set_name("pulseaudio");
mainloop_ = pa_threaded_mainloop_new();
if (mainloop_ == nullptr) {
2018-08-09 21:55:38 +00:00
throw std::runtime_error("pa_mainloop_new() failed.");
2018-08-16 12:29:41 +00:00
}
pa_threaded_mainloop_lock(mainloop_);
mainloop_api_ = pa_threaded_mainloop_get_api(mainloop_);
context_ = pa_context_new(mainloop_api_, "waybar");
if (context_ == nullptr) {
2018-08-09 21:55:38 +00:00
throw std::runtime_error("pa_context_new() failed.");
2018-08-16 12:29:41 +00:00
}
if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOAUTOSPAWN,
nullptr) < 0) {
auto err = fmt::format("pa_context_connect() failed: {}",
pa_strerror(pa_context_errno(context_)));
throw std::runtime_error(err);
}
pa_context_set_state_callback(context_, contextStateCb, this);
if (pa_threaded_mainloop_start(mainloop_) < 0) {
throw std::runtime_error("pa_mainloop_run() failed.");
2018-08-16 12:29:41 +00:00
}
pa_threaded_mainloop_unlock(mainloop_);
}
2018-08-09 21:55:38 +00:00
2018-08-19 11:39:57 +00:00
waybar::modules::Pulseaudio::~Pulseaudio()
{
mainloop_api_->quit(mainloop_api_, 0);
pa_threaded_mainloop_stop(mainloop_);
pa_threaded_mainloop_free(mainloop_);
}
2018-08-16 12:29:41 +00:00
void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data)
2018-08-09 21:55:38 +00:00
{
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
switch (pa_context_get_state(c)) {
case PA_CONTEXT_TERMINATED:
2018-08-16 12:29:41 +00:00
pa->mainloop_api_->quit(pa->mainloop_api_, 0);
2018-08-09 21:55:38 +00:00
break;
case PA_CONTEXT_READY:
2018-08-16 12:29:41 +00:00
pa_context_get_server_info(c, serverInfoCb, data);
pa_context_set_subscribe_callback(c, subscribeCb, data);
pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK, nullptr, nullptr);
2018-08-09 21:55:38 +00:00
break;
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
case PA_CONTEXT_FAILED:
default:
2018-08-16 12:29:41 +00:00
pa->mainloop_api_->quit(pa->mainloop_api_, 1);
2018-08-09 21:55:38 +00:00
break;
}
}
/*
* Called when an event we subscribed to occurs.
*/
2018-08-16 12:29:41 +00:00
void waybar::modules::Pulseaudio::subscribeCb(pa_context* context,
pa_subscription_event_type_t type, uint32_t idx, void* data)
2018-08-09 21:55:38 +00:00
{
unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
switch (facility) {
2018-08-16 12:29:41 +00:00
case PA_SUBSCRIPTION_EVENT_SINK:
pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data);
break;
default:
break;
2018-08-09 21:55:38 +00:00
}
}
/*
* Called when the requested sink information is ready.
*/
2018-08-16 12:29:41 +00:00
void waybar::modules::Pulseaudio::sinkInfoCb(pa_context* /*context*/,
const pa_sink_info* i, int /*eol*/, void* data)
2018-08-09 21:55:38 +00:00
{
2018-08-16 12:29:41 +00:00
if (i != nullptr) {
2018-08-09 21:55:38 +00:00
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
2018-08-16 12:29:41 +00:00
float volume = static_cast<float>(pa_cvolume_avg(&(i->volume)))
/ float{PA_VOLUME_NORM};
pa->sink_idx_ = i->index;
2018-10-25 11:57:35 +00:00
pa->volume_ = std::round(volume * 100.0f);
2018-08-16 12:29:41 +00:00
pa->muted_ = i->mute != 0;
pa->desc_ = i->description;
pa->port_name_ = i->active_port ? i->active_port->name : "Unknown";
2018-08-20 12:50:45 +00:00
pa->dp.emit();
2018-08-09 21:55:38 +00:00
}
}
/*
* Called when the requested information on the server is ready. This is
* used to find the default PulseAudio sink.
*/
2018-08-16 12:29:41 +00:00
void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context,
2018-08-09 21:55:38 +00:00
const pa_server_info *i, void *data)
{
2018-08-26 23:36:25 +00:00
pa_context_get_sink_info_by_name(context, i->default_sink_name,
sinkInfoCb, data);
2018-08-09 21:55:38 +00:00
}
2018-08-29 21:50:41 +00:00
const std::string waybar::modules::Pulseaudio::getPortIcon() const
{
std::vector<std::string> ports = {
"headphones",
"speaker",
"hdmi",
"headset",
"handsfree",
"portable",
"car",
"hifi",
"phone",
};
2018-09-04 21:50:08 +00:00
for (auto const& port : ports) {
2018-08-29 21:50:41 +00:00
if (port_name_.find(port) != std::string::npos) {
return port;
}
}
return "";
}
2018-08-09 21:55:38 +00:00
auto waybar::modules::Pulseaudio::update() -> void
{
2018-08-26 23:36:25 +00:00
auto format = format_;
if (muted_) {
format =
2018-10-26 07:27:16 +00:00
config_["format-muted"].isString() ? config_["format-muted"].asString() : format;
2018-08-26 23:36:25 +00:00
label_.get_style_context()->add_class("muted");
2018-08-29 21:50:41 +00:00
} else if (port_name_.find("a2dp_sink") != std::string::npos) {
2018-10-26 07:27:16 +00:00
format = config_["format-bluetooth"].isString()
2018-08-29 21:50:41 +00:00
? config_["format-bluetooth"].asString() : format;
label_.get_style_context()->add_class("bluetooth");
2018-08-26 23:36:25 +00:00
} else {
label_.get_style_context()->remove_class("muted");
2018-08-29 21:50:41 +00:00
label_.get_style_context()->add_class("bluetooth");
2018-08-16 12:29:41 +00:00
}
2018-08-26 23:36:25 +00:00
label_.set_label(fmt::format(format,
fmt::arg("volume", volume_),
2018-08-29 21:50:41 +00:00
fmt::arg("icon", getIcon(volume_, getPortIcon()))));
2018-08-26 23:36:25 +00:00
label_.set_tooltip_text(desc_);
2018-08-13 20:33:07 +00:00
}