From d5c400c0cc5f2ba1e8a414abfafe74ed5e1aeb00 Mon Sep 17 00:00:00 2001 From: Kenny Phelps-McKeown Date: Wed, 9 Feb 2022 02:53:52 -0500 Subject: [PATCH 1/6] Initial commit for Waybar JACK monitoring module -DSP load -xruns -connected/disconnected state -only tested with Pipewire so far but should work with JACK2 as well On branch dsp Changes to be committed: modified: include/factory.hpp new file: include/modules/jack.hpp modified: meson.build modified: meson_options.txt modified: src/factory.cpp new file: src/modules/jack.cpp --- include/factory.hpp | 3 + include/modules/jack.hpp | 29 +++++++++ meson.build | 9 +++ meson_options.txt | 2 + src/factory.cpp | 5 ++ src/modules/jack.cpp | 127 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 175 insertions(+) create mode 100644 include/modules/jack.hpp create mode 100644 src/modules/jack.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 3855ce2..8702632 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -62,6 +62,9 @@ # include "modules/bluetooth.hpp" # endif #endif +#ifdef HAVE_LIBJACK +#include "modules/jack.hpp" +#endif namespace waybar { diff --git a/include/modules/jack.hpp b/include/modules/jack.hpp new file mode 100644 index 0000000..df02096 --- /dev/null +++ b/include/modules/jack.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +class JACK : public ALabel { + public: + JACK(const std::string&, const Json::Value&); + ~JACK() = default; + auto update() -> void; + jack_nframes_t bufsize_; + jack_client_t* client_; + unsigned int xruns_; + std::string state_; + + private: + std::string JACKState(); + + jack_nframes_t samplerate_; + util::SleeperThread thread_; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index 2c644cc..3308590 100644 --- a/meson.build +++ b/meson.build @@ -97,6 +97,8 @@ libudev = dependency('libudev', required: get_option('libudev')) libevdev = dependency('libevdev', required: get_option('libevdev')) libmpdclient = dependency('libmpdclient', required: get_option('mpd')) xkbregistry = dependency('xkbregistry') +libjack = dependency('jack', required: get_option('jack')) +libprocps = dependency('libprocps', required: get_option('jack')) libsndio = compiler.find_library('sndio', required: get_option('sndio')) if libsndio.found() @@ -207,6 +209,11 @@ if libpulse.found() src_files += 'src/modules/pulseaudio.cpp' endif +if libjack.found() and libprocps.found() + add_project_arguments('-DHAVE_LIBJACK', language: 'cpp') + src_files += 'src/modules/jack.cpp' +endif + if dbusmenu_gtk.found() add_project_arguments('-DHAVE_DBUSMENU', language: 'cpp') src_files += files( @@ -288,6 +295,8 @@ executable( libnl, libnlgen, libpulse, + libjack, + libprocps, libudev, libepoll, libmpdclient, diff --git a/meson_options.txt b/meson_options.txt index 230a53d..a41f70a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -13,3 +13,5 @@ option('sndio', type: 'feature', value: 'auto', description: 'Enable support for option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind') option('tests', type: 'feature', value: 'auto', description: 'Enable tests') option('experimental', type : 'boolean', value : false, description: 'Enable experimental features') +option('jack', type: 'feature', value: 'auto', description: 'Enable support for JACK DSP load monitoring') + diff --git a/src/factory.cpp b/src/factory.cpp index 900653b..30325fe 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -85,6 +85,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { return new waybar::modules::Pulseaudio(id, config_[name]); } #endif +#ifdef HAVE_LIBJACK + if (ref == "jack") { + return new waybar::modules::JACK(id, config_[name]); + } +#endif #ifdef HAVE_LIBMPDCLIENT if (ref == "mpd") { return new waybar::modules::MPD(id, config_[name]); diff --git a/src/modules/jack.cpp b/src/modules/jack.cpp new file mode 100644 index 0000000..08d1414 --- /dev/null +++ b/src/modules/jack.cpp @@ -0,0 +1,127 @@ +#include "modules/jack.hpp" + +extern "C" { + + int bufSizeCallback(unsigned int size, void *obj) { + waybar::modules::JACK* x = (waybar::modules::JACK*)obj; + x->bufsize_ = size; + return size; + } + + int xrunCallback(void *obj) { + waybar::modules::JACK* x = (waybar::modules::JACK*)obj; + x->xruns_ += 1; + x->state_ = "xrun"; + return 0; + } + + void shutdownCallback(void *obj) { + waybar::modules::JACK* x = (waybar::modules::JACK*)obj; + x->client_ = NULL; + x->state_ = "disconnected"; + x->xruns_ = 0; + } + +} + +waybar::modules::JACK::JACK(const std::string& id, const Json::Value& config) + : ALabel(config, "jack", id, "{load}%", 1) { + xruns_ = 0; + state_ = "disconnected"; + client_ = NULL; + + state_ = JACKState(); + thread_ = [this] { + dp.emit(); + thread_.sleep_for(interval_); + }; +} + +std::string waybar::modules::JACK::JACKState() { + if(state_.compare("xrun") == 0) + return "xrun"; + if(state_.compare("connected") == 0) + return "connected"; + + std::string procname; + bool foundJACK = false; + proc_t** proctab = readproctab(PROC_FILLSTAT); + for(int i=0; proctab[i]; i++) { + procname = proctab[i]->cmd; + if(!procname.compare("jackd") || !procname.compare("pipewire")) + foundJACK = true; + freeproc(proctab[i]); + } + free(proctab); + if(!foundJACK) + return "disconnected"; + + client_ = jack_client_open("waybar", JackNoStartServer, NULL); + + if (client_) { + bufsize_ = jack_get_buffer_size(client_); + samplerate_ = jack_get_sample_rate(client_); + jack_set_buffer_size_callback(client_, bufSizeCallback, this); + jack_set_xrun_callback(client_, xrunCallback, this); + jack_on_shutdown(client_, shutdownCallback, this); + + if (!jack_activate(client_)) + return "connected"; + } + return "disconnected"; +} + +auto waybar::modules::JACK::update() -> void { + std::string format; + float latency = 1000 * (float)bufsize_ / (float)samplerate_; + auto state = JACKState(); + float load; + + if(label_.get_style_context()->has_class("xrun")) { + label_.get_style_context()->remove_class("xrun"); + state = "connected"; + } + + if(state.compare("disconnected") != 0) + load = jack_cpu_load(client_); + else { + load = 0; + bufsize_ = 0; + samplerate_ = 0; + latency = 0; + } + + if(label_.get_style_context()->has_class(state_)) + label_.get_style_context()->remove_class(state_); + + if (config_["format-" + state].isString()) { + format = config_["format-" + state].asString(); + } else if (config_["format"].isString()) { + format = config_["format"].asString(); + } else format = "DSP {load}%"; + + if(!label_.get_style_context()->has_class(state)) + label_.get_style_context()->add_class(state); + state_ = state; + + label_.set_markup(fmt::format(format, fmt::arg("load", std::round(load)), + fmt::arg("bufsize", bufsize_), + fmt::arg("samplerate", samplerate_), + fmt::arg("latency", fmt::format("{:.2f}", latency)), + fmt::arg("xruns", xruns_))); + + if (tooltipEnabled()) { + std::string tooltip_format = "{bufsize}/{samplerate} {latency}ms"; + if (config_["tooltip-format"].isString()) + tooltip_format = config_["tooltip-format"].asString(); + label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("load", std::round(load)), + fmt::arg("bufsize", bufsize_), + fmt::arg("samplerate", samplerate_), + fmt::arg("latency", fmt::format("{:.2f}", latency)), + fmt::arg("xruns", xruns_))); + + } + + // Call parent update + ALabel::update(); +} From c1cda1553ae07409dd1e14092e83ae704e5154cb Mon Sep 17 00:00:00 2001 From: kennypm Date: Sat, 12 Feb 2022 01:51:11 -0500 Subject: [PATCH 2/6] fix callbacks --- include/modules/jack.hpp | 6 ++++++ src/modules/jack.cpp | 10 +++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/modules/jack.hpp b/include/modules/jack.hpp index df02096..2433a43 100644 --- a/include/modules/jack.hpp +++ b/include/modules/jack.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" @@ -18,6 +19,7 @@ class JACK : public ALabel { jack_client_t* client_; unsigned int xruns_; std::string state_; + pthread_t jack_thread_; private: std::string JACKState(); @@ -27,3 +29,7 @@ class JACK : public ALabel { }; } // namespace waybar::modules + +int bufSizeCallback(unsigned int size, void *obj); +int xrunCallback(void *obj); +void shutdownCallback(void *obj); diff --git a/src/modules/jack.cpp b/src/modules/jack.cpp index 08d1414..ab43c33 100644 --- a/src/modules/jack.cpp +++ b/src/modules/jack.cpp @@ -1,6 +1,6 @@ #include "modules/jack.hpp" -extern "C" { +//extern "C" { int bufSizeCallback(unsigned int size, void *obj) { waybar::modules::JACK* x = (waybar::modules::JACK*)obj; @@ -17,12 +17,13 @@ extern "C" { void shutdownCallback(void *obj) { waybar::modules::JACK* x = (waybar::modules::JACK*)obj; + pthread_cancel(x->jack_thread_); x->client_ = NULL; x->state_ = "disconnected"; x->xruns_ = 0; } -} +//} waybar::modules::JACK::JACK(const std::string& id, const Json::Value& config) : ALabel(config, "jack", id, "{load}%", 1) { @@ -57,8 +58,11 @@ std::string waybar::modules::JACK::JACKState() { return "disconnected"; client_ = jack_client_open("waybar", JackNoStartServer, NULL); - if (client_) { + jack_thread_ = jack_client_thread_id(client_); + if(config_["realtime"].isBool() && !config_["realtime"].asBool()) + jack_drop_real_time_scheduling(jack_thread_); + bufsize_ = jack_get_buffer_size(client_); samplerate_ = jack_get_sample_rate(client_); jack_set_buffer_size_callback(client_, bufSizeCallback, this); From 3bf815f6def5e4e3a75187b9e3dc391772006ef1 Mon Sep 17 00:00:00 2001 From: kennypm Date: Sat, 12 Feb 2022 01:52:51 -0500 Subject: [PATCH 3/6] fix callbacks --- src/modules/jack.cpp | 46 ++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/src/modules/jack.cpp b/src/modules/jack.cpp index ab43c33..fd183c7 100644 --- a/src/modules/jack.cpp +++ b/src/modules/jack.cpp @@ -1,30 +1,5 @@ #include "modules/jack.hpp" -//extern "C" { - - int bufSizeCallback(unsigned int size, void *obj) { - waybar::modules::JACK* x = (waybar::modules::JACK*)obj; - x->bufsize_ = size; - return size; - } - - int xrunCallback(void *obj) { - waybar::modules::JACK* x = (waybar::modules::JACK*)obj; - x->xruns_ += 1; - x->state_ = "xrun"; - return 0; - } - - void shutdownCallback(void *obj) { - waybar::modules::JACK* x = (waybar::modules::JACK*)obj; - pthread_cancel(x->jack_thread_); - x->client_ = NULL; - x->state_ = "disconnected"; - x->xruns_ = 0; - } - -//} - waybar::modules::JACK::JACK(const std::string& id, const Json::Value& config) : ALabel(config, "jack", id, "{load}%", 1) { xruns_ = 0; @@ -129,3 +104,24 @@ auto waybar::modules::JACK::update() -> void { // Call parent update ALabel::update(); } + +int bufSizeCallback(unsigned int size, void *obj) { + waybar::modules::JACK* x = (waybar::modules::JACK*)obj; + x->bufsize_ = size; + return size; +} + +int xrunCallback(void *obj) { + waybar::modules::JACK* x = (waybar::modules::JACK*)obj; + x->xruns_ += 1; + x->state_ = "xrun"; + return 0; +} + +void shutdownCallback(void *obj) { + waybar::modules::JACK* x = (waybar::modules::JACK*)obj; + pthread_cancel(x->jack_thread_); + x->client_ = NULL; + x->state_ = "disconnected"; + x->xruns_ = 0; +} From 823ed887ab6d24920ceaefcce6ae1bd09ac7c505 Mon Sep 17 00:00:00 2001 From: Kenny Phelps-McKeown <64751911+kennypm@users.noreply.github.com> Date: Sat, 12 Feb 2022 05:53:32 -0500 Subject: [PATCH 4/6] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index c5806e3..c853573 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +## note: this fork depends on libjack and libprocps as well as the dependencies listed below + + + + + # Waybar [![Licence](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Paypal Donate](https://img.shields.io/badge/Donate-Paypal-2244dd.svg)](https://paypal.me/ARouillard)
![Waybar](https://raw.githubusercontent.com/alexays/waybar/master/preview-2.png) > Highly customizable Wayland bar for Sway and Wlroots based compositors.
From e6262b870cccd888240ff552d7cd7738d9072670 Mon Sep 17 00:00:00 2001 From: kennypm Date: Fri, 18 Feb 2022 02:13:43 -0500 Subject: [PATCH 5/6] changed callbacks to use static_cast --- include/modules/jack.hpp | 14 +++++++++----- src/modules/jack.cpp | 35 ++++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/include/modules/jack.hpp b/include/modules/jack.hpp index 2433a43..7175398 100644 --- a/include/modules/jack.hpp +++ b/include/modules/jack.hpp @@ -15,16 +15,20 @@ class JACK : public ALabel { JACK(const std::string&, const Json::Value&); ~JACK() = default; auto update() -> void; - jack_nframes_t bufsize_; - jack_client_t* client_; - unsigned int xruns_; - std::string state_; - pthread_t jack_thread_; + + int bufSize(unsigned int size); + int xrun(); + void shutdown(); private: std::string JACKState(); + jack_client_t* client_; + jack_nframes_t bufsize_; jack_nframes_t samplerate_; + unsigned int xruns_; + std::string state_; + pthread_t jack_thread_; util::SleeperThread thread_; }; diff --git a/src/modules/jack.cpp b/src/modules/jack.cpp index fd183c7..45dab23 100644 --- a/src/modules/jack.cpp +++ b/src/modules/jack.cpp @@ -105,23 +105,32 @@ auto waybar::modules::JACK::update() -> void { ALabel::update(); } -int bufSizeCallback(unsigned int size, void *obj) { - waybar::modules::JACK* x = (waybar::modules::JACK*)obj; - x->bufsize_ = size; +int waybar::modules::JACK::bufSize(unsigned int size) { + bufsize_ = size; return size; } -int xrunCallback(void *obj) { - waybar::modules::JACK* x = (waybar::modules::JACK*)obj; - x->xruns_ += 1; - x->state_ = "xrun"; +int waybar::modules::JACK::xrun() { + xruns_ += 1; + state_ = "xrun"; return 0; } -void shutdownCallback(void *obj) { - waybar::modules::JACK* x = (waybar::modules::JACK*)obj; - pthread_cancel(x->jack_thread_); - x->client_ = NULL; - x->state_ = "disconnected"; - x->xruns_ = 0; +void waybar::modules::JACK::shutdown() { + pthread_cancel(jack_thread_); + client_ = NULL; + state_ = "disconnected"; + xruns_ = 0; +} + +int bufSizeCallback(unsigned int size, void *obj) { + return static_cast(obj)->bufSize(size); +} + +int xrunCallback(void *obj) { + return static_cast(obj)->xrun(); +} + +void shutdownCallback(void *obj) { + return static_cast(obj)->shutdown(); } From ccce2b700b823be87ac91819d9611486675a42fe Mon Sep 17 00:00:00 2001 From: kennypm Date: Thu, 24 Feb 2022 02:46:45 -0500 Subject: [PATCH 6/6] fix segfault when stopping JACK2 server --- meson.build | 2 +- src/modules/jack.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 3308590..e0fb452 100644 --- a/meson.build +++ b/meson.build @@ -5,7 +5,7 @@ project( meson_version: '>= 0.49.0', default_options : [ 'cpp_std=c++17', - 'buildtype=release', + 'buildtype=debug', 'default_library=static' ], ) diff --git a/src/modules/jack.cpp b/src/modules/jack.cpp index 45dab23..af4870e 100644 --- a/src/modules/jack.cpp +++ b/src/modules/jack.cpp @@ -24,7 +24,8 @@ std::string waybar::modules::JACK::JACKState() { proc_t** proctab = readproctab(PROC_FILLSTAT); for(int i=0; proctab[i]; i++) { procname = proctab[i]->cmd; - if(!procname.compare("jackd") || !procname.compare("pipewire")) + if(!procname.compare("jackd") || !procname.compare("jackdbus") || + !procname.compare("pipewire")) foundJACK = true; freeproc(proctab[i]); } @@ -116,8 +117,14 @@ int waybar::modules::JACK::xrun() { return 0; } +/* +** problem: pipewire leaves old client threads hanging around after server +** is killed. was handling this sloppily by calling pthread_cancel() on the +** JACK thread but since JACK2 cleans up after itself properly, this call +** led to segfault when using JACK2. probably best course of action is to +** submit a bug report to pipewire. +*/ void waybar::modules::JACK::shutdown() { - pthread_cancel(jack_thread_); client_ = NULL; state_ = "disconnected"; xruns_ = 0;