feat: add pulseaudio module
parent
11fab436b9
commit
7ccb76935d
|
@ -7,6 +7,7 @@
|
||||||
#include "modules/memory.hpp"
|
#include "modules/memory.hpp"
|
||||||
#include "modules/cpu.hpp"
|
#include "modules/cpu.hpp"
|
||||||
#include "modules/network.hpp"
|
#include "modules/network.hpp"
|
||||||
|
#include "modules/pulseaudio.hpp"
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
|
|
|
@ -20,20 +20,6 @@ namespace waybar::modules {
|
||||||
Network(Json::Value config);
|
Network(Json::Value config);
|
||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
operator Gtk::Widget &();
|
operator Gtk::Widget &();
|
||||||
typedef struct {
|
|
||||||
int flags;
|
|
||||||
char essid[IW_ESSID_MAX_SIZE + 1];
|
|
||||||
uint8_t bssid[ETH_ALEN];
|
|
||||||
int quality;
|
|
||||||
int quality_max;
|
|
||||||
int quality_average;
|
|
||||||
int signal_level;
|
|
||||||
int signal_level_max;
|
|
||||||
int noise_level;
|
|
||||||
int noise_level_max;
|
|
||||||
int bitrate;
|
|
||||||
double frequency;
|
|
||||||
} wireless_info_t;
|
|
||||||
private:
|
private:
|
||||||
void _parseEssid(struct nlattr **bss);
|
void _parseEssid(struct nlattr **bss);
|
||||||
void _parseSignal(struct nlattr **bss);
|
void _parseSignal(struct nlattr **bss);
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pulse/pulseaudio.h>
|
||||||
|
#include <json/json.h>
|
||||||
|
#include <gtkmm.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <thread>
|
||||||
|
#include "util/chrono.hpp"
|
||||||
|
#include "IModule.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class Pulseaudio : public IModule {
|
||||||
|
public:
|
||||||
|
Pulseaudio(Json::Value config);
|
||||||
|
auto update() -> void;
|
||||||
|
operator Gtk::Widget &();
|
||||||
|
private:
|
||||||
|
static void _subscribeCb(pa_context *context,
|
||||||
|
pa_subscription_event_type_t type, uint32_t idx, void *data);
|
||||||
|
static void _contextStateCb(pa_context *c, void *data);
|
||||||
|
static void _sinkInfoCb(pa_context *context, const pa_sink_info *i,
|
||||||
|
int eol, void *data);
|
||||||
|
static void _serverInfoCb(pa_context *context, const pa_server_info *i,
|
||||||
|
void *data);
|
||||||
|
Gtk::Label _label;
|
||||||
|
std::thread _thread;
|
||||||
|
Json::Value _config;
|
||||||
|
pa_mainloop *_mainloop;
|
||||||
|
pa_mainloop_api *_mainloop_api;
|
||||||
|
pa_context *_context;
|
||||||
|
int _volume;
|
||||||
|
bool _muted;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ jsoncpp = dependency('jsoncpp')
|
||||||
sigcpp = dependency('sigc++-2.0')
|
sigcpp = dependency('sigc++-2.0')
|
||||||
libnl = dependency('libnl-3.0')
|
libnl = dependency('libnl-3.0')
|
||||||
libnlgen = dependency('libnl-genl-3.0')
|
libnlgen = dependency('libnl-genl-3.0')
|
||||||
|
libpulse = dependency('libpulse')
|
||||||
|
|
||||||
subdir('protocol')
|
subdir('protocol')
|
||||||
|
|
||||||
|
@ -47,6 +48,7 @@ executable(
|
||||||
gtkmm,
|
gtkmm,
|
||||||
libnl,
|
libnl,
|
||||||
libnlgen,
|
libnlgen,
|
||||||
|
libpulse,
|
||||||
],
|
],
|
||||||
include_directories: [include_directories('include')],
|
include_directories: [include_directories('include')],
|
||||||
install: true,
|
install: true,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"modules-left": ["workspaces"],
|
"modules-left": ["workspaces"],
|
||||||
"modules-right": ["network", "cpu", "memory", "battery", "clock"],
|
"modules-right": ["pulseaudio", "network", "cpu", "memory", "battery", "clock"],
|
||||||
"cpu": {
|
"cpu": {
|
||||||
"format": "{}% "
|
"format": "{}% "
|
||||||
},
|
},
|
||||||
|
@ -13,5 +13,9 @@
|
||||||
"network": {
|
"network": {
|
||||||
"interface": "wlp2s0",
|
"interface": "wlp2s0",
|
||||||
"format": "{essid} ({signalStrength}dBm) "
|
"format": "{essid} ({signalStrength}dBm) "
|
||||||
|
},
|
||||||
|
"pulseaudio": {
|
||||||
|
"format": "{}% ",
|
||||||
|
"format-muted": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ window {
|
||||||
border-bottom: 3px solid white;
|
border-bottom: 3px solid white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clock, .battery, .cpu, .memory, .network {
|
.clock, .battery, .cpu, .memory, .network, .pulseaudio {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
}
|
}
|
||||||
|
@ -54,3 +54,8 @@ window {
|
||||||
.network {
|
.network {
|
||||||
background: #2980b9;
|
background: #2980b9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pulseaudio {
|
||||||
|
background: #f1c40f;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
|
@ -18,5 +18,7 @@ waybar::IModule &waybar::Factory::makeModule(std::string name)
|
||||||
return *new waybar::modules::Clock(_config[name]);
|
return *new waybar::modules::Clock(_config[name]);
|
||||||
if (name == "network")
|
if (name == "network")
|
||||||
return *new waybar::modules::Network(_config[name]);
|
return *new waybar::modules::Network(_config[name]);
|
||||||
|
if (name == "pulseaudio")
|
||||||
|
return *new waybar::modules::Pulseaudio(_config[name]);
|
||||||
throw std::runtime_error("Unknown module: " + name);
|
throw std::runtime_error("Unknown module: " + name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include "modules/memory.hpp"
|
#include "modules/memory.hpp"
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
waybar::modules::Memory::Memory(Json::Value config)
|
waybar::modules::Memory::Memory(Json::Value config)
|
||||||
: _config(config)
|
: _config(config)
|
||||||
|
@ -16,10 +15,12 @@ auto waybar::modules::Memory::update() -> void
|
||||||
{
|
{
|
||||||
struct sysinfo info;
|
struct sysinfo info;
|
||||||
if (!sysinfo(&info)) {
|
if (!sysinfo(&info)) {
|
||||||
int used_ram_percentage = 100 * (info.totalram - info.freeram) / info.totalram;
|
auto total = info.totalram * info.mem_unit;
|
||||||
|
auto freeram = info.freeram * info.mem_unit;
|
||||||
|
int used_ram_percentage = 100 * (total - freeram) / total;
|
||||||
auto format = _config["format"] ? _config["format"].asString() : "{}%";
|
auto format = _config["format"] ? _config["format"].asString() : "{}%";
|
||||||
_label.set_text(fmt::format(format, used_ram_percentage));
|
_label.set_text(fmt::format(format, used_ram_percentage));
|
||||||
auto used_ram_gigabytes = (info.totalram - info.freeram) / std::pow(1024, 3);
|
auto used_ram_gigabytes = (total - freeram) / std::pow(1024, 3);
|
||||||
_label.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1));
|
_label.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
#include "modules/pulseaudio.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
waybar::modules::Pulseaudio::Pulseaudio(Json::Value config)
|
||||||
|
: _config(config), _mainloop(nullptr), _mainloop_api(nullptr),
|
||||||
|
_context(nullptr), _volume(0), _muted(false)
|
||||||
|
{
|
||||||
|
_label.get_style_context()->add_class("pulseaudio");
|
||||||
|
_mainloop = pa_mainloop_new();
|
||||||
|
if (!_mainloop)
|
||||||
|
throw std::runtime_error("pa_mainloop_new() failed.");
|
||||||
|
_mainloop_api = pa_mainloop_get_api(_mainloop);
|
||||||
|
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);
|
||||||
|
_thread = std::thread([this]() {
|
||||||
|
if (pa_mainloop_run(_mainloop, nullptr) < 0)
|
||||||
|
throw std::runtime_error("pa_mainloop_run() failed.");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
pa->_volume = volume * 100.0f;
|
||||||
|
pa->_muted = i->mute;
|
||||||
|
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() : "{}%";
|
||||||
|
if (_muted)
|
||||||
|
format =
|
||||||
|
_config["format-muted"] ? _config["format-muted"].asString() : format;
|
||||||
|
_label.set_text(fmt::format(format, _volume));
|
||||||
|
}
|
||||||
|
|
||||||
|
waybar::modules::Pulseaudio::operator Gtk::Widget &() {
|
||||||
|
return _label;
|
||||||
|
}
|
Loading…
Reference in New Issue