2018-08-09 21:55:38 +00:00
|
|
|
#include "modules/pulseaudio.hpp"
|
|
|
|
|
|
|
|
waybar::modules::Pulseaudio::Pulseaudio(Json::Value config)
|
|
|
|
: _config(config), _mainloop(nullptr), _mainloop_api(nullptr),
|
2018-08-09 22:34:13 +00:00
|
|
|
_context(nullptr), _sinkIdx(0), _volume(0), _muted(false)
|
2018-08-09 21:55:38 +00:00
|
|
|
{
|
|
|
|
_label.get_style_context()->add_class("pulseaudio");
|
2018-08-09 22:34:13 +00:00
|
|
|
_mainloop = pa_threaded_mainloop_new();
|
2018-08-09 21:55:38 +00:00
|
|
|
if (!_mainloop)
|
|
|
|
throw std::runtime_error("pa_mainloop_new() failed.");
|
2018-08-09 22:34:13 +00:00
|
|
|
pa_threaded_mainloop_lock(_mainloop);
|
|
|
|
_mainloop_api = pa_threaded_mainloop_get_api(_mainloop);
|
2018-08-09 21:55:38 +00:00
|
|
|
if (pa_signal_init(_mainloop_api) != 0)
|
|
|
|
throw std::runtime_error("pa_signal_init() failed.");
|
|
|
|
_context = pa_context_new(_mainloop_api, "waybar");
|
|
|
|
if (!_context)
|
|
|
|
throw std::runtime_error("pa_context_new() failed.");
|
|
|
|
if (pa_context_connect(_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
|
|
|
|
throw std::runtime_error(fmt::format("pa_context_connect() failed: {}",
|
|
|
|
pa_strerror(pa_context_errno(_context))));
|
|
|
|
pa_context_set_state_callback(_context, _contextStateCb, this);
|
2018-08-09 22:34:13 +00:00
|
|
|
if (pa_threaded_mainloop_start(_mainloop) < 0)
|
|
|
|
throw std::runtime_error("pa_mainloop_run() failed.");
|
|
|
|
pa_threaded_mainloop_unlock(_mainloop);
|
2018-08-09 21:55:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
void waybar::modules::Pulseaudio::_contextStateCb(pa_context *c, void *data)
|
|
|
|
{
|
|
|
|
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
|
|
|
|
switch (pa_context_get_state(c)) {
|
|
|
|
case PA_CONTEXT_TERMINATED:
|
|
|
|
pa->_mainloop_api->quit(pa->_mainloop_api, 0);
|
|
|
|
break;
|
|
|
|
case PA_CONTEXT_READY:
|
|
|
|
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);
|
|
|
|
break;
|
|
|
|
case PA_CONTEXT_CONNECTING:
|
|
|
|
case PA_CONTEXT_AUTHORIZING:
|
|
|
|
case PA_CONTEXT_SETTING_NAME:
|
|
|
|
break;
|
|
|
|
case PA_CONTEXT_FAILED:
|
|
|
|
default:
|
|
|
|
pa->_mainloop_api->quit(pa->_mainloop_api, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called when an event we subscribed to occurs.
|
|
|
|
*/
|
|
|
|
void waybar::modules::Pulseaudio::_subscribeCb(pa_context *context,
|
|
|
|
pa_subscription_event_type_t type, uint32_t idx, void *data)
|
|
|
|
{
|
|
|
|
unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
|
|
|
|
pa_operation *op = nullptr;
|
|
|
|
|
|
|
|
switch (facility) {
|
|
|
|
case PA_SUBSCRIPTION_EVENT_SINK:
|
|
|
|
pa_context_get_sink_info_by_index(context, idx, _sinkInfoCb, data);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (op)
|
|
|
|
pa_operation_unref(op);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called when the requested sink information is ready.
|
|
|
|
*/
|
|
|
|
void waybar::modules::Pulseaudio::_sinkInfoCb(pa_context *context,
|
|
|
|
const pa_sink_info *i, int eol, void *data)
|
|
|
|
{
|
|
|
|
if (i) {
|
|
|
|
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
|
|
|
|
float volume = (float)pa_cvolume_avg(&(i->volume)) / (float)PA_VOLUME_NORM;
|
2018-08-09 22:34:13 +00:00
|
|
|
pa->_sinkIdx = i->index;
|
2018-08-09 21:55:38 +00:00
|
|
|
pa->_volume = volume * 100.0f;
|
|
|
|
pa->_muted = i->mute;
|
2018-08-09 22:34:13 +00:00
|
|
|
pa->_desc = i->description;
|
2018-08-09 21:55:38 +00:00
|
|
|
pa->update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called when the requested information on the server is ready. This is
|
|
|
|
* used to find the default PulseAudio sink.
|
|
|
|
*/
|
|
|
|
void waybar::modules::Pulseaudio::_serverInfoCb(pa_context *context,
|
|
|
|
const pa_server_info *i, void *data)
|
|
|
|
{
|
|
|
|
pa_context_get_sink_info_by_name(context, i->default_sink_name, _sinkInfoCb,
|
|
|
|
data);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto waybar::modules::Pulseaudio::update() -> void
|
|
|
|
{
|
|
|
|
auto format = _config["format"] ? _config["format"].asString() : "{}%";
|
2018-08-09 22:02:50 +00:00
|
|
|
if (_muted) {
|
2018-08-09 21:55:38 +00:00
|
|
|
format =
|
|
|
|
_config["format-muted"] ? _config["format-muted"].asString() : format;
|
2018-08-09 22:02:50 +00:00
|
|
|
if (!_label.get_style_context()->has_class("muted"))
|
|
|
|
_label.get_style_context()->add_class("muted");
|
|
|
|
} else if (_label.get_style_context()->has_class("muted"))
|
|
|
|
_label.get_style_context()->remove_class("muted");
|
2018-08-09 22:34:13 +00:00
|
|
|
_label.set_label(fmt::format(format, _volume));
|
|
|
|
_label.set_tooltip_text(_desc);
|
2018-08-09 21:55:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
waybar::modules::Pulseaudio::operator Gtk::Widget &() {
|
|
|
|
return _label;
|
|
|
|
}
|