Implement shared execs for custom modules
For more complicated configurations with several actions activated through clicks on various connected modules, it is currently necessary to have each of those connected modules exec their own subprocess to produce the wanted output. This is problematic because it greatly increases CPU usage. Implement a new configuration option for bars, "custom-execs", which contains an array of subprocess specifications ("exec", "exec_if", "interval", "restart-interval"). The bar runs each of these executables and reads their output. The output is in the form of a JSON object with module names as keys and either strings (for "raw" type output) or objects (for "json" type output) as values. These values are forwarded to the custom modules named by the keys.pull/1276/head
parent
ccdc9b8e0b
commit
eb1775d5f8
|
@ -9,6 +9,9 @@
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
#include "AModule.hpp"
|
#include "AModule.hpp"
|
||||||
|
#include "modules/custom.hpp"
|
||||||
|
#include "util/json.hpp"
|
||||||
|
#include "util/worker_thread.hpp"
|
||||||
#include "xdg-output-unstable-v1-client-protocol.h"
|
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
@ -76,6 +79,7 @@ class Bar {
|
||||||
void getModules(const Factory &, const std::string &);
|
void getModules(const Factory &, const std::string &);
|
||||||
void setupAltFormatKeyForModule(const std::string &module_name);
|
void setupAltFormatKeyForModule(const std::string &module_name);
|
||||||
void setupAltFormatKeyForModuleList(const char *module_list_name);
|
void setupAltFormatKeyForModuleList(const char *module_list_name);
|
||||||
|
void customExecOutputCallback(std::string output);
|
||||||
|
|
||||||
std::unique_ptr<BarSurface> surface_impl_;
|
std::unique_ptr<BarSurface> surface_impl_;
|
||||||
bar_layer layer_;
|
bar_layer layer_;
|
||||||
|
@ -86,6 +90,10 @@ class Bar {
|
||||||
std::vector<std::unique_ptr<waybar::AModule>> modules_left_;
|
std::vector<std::unique_ptr<waybar::AModule>> modules_left_;
|
||||||
std::vector<std::unique_ptr<waybar::AModule>> modules_center_;
|
std::vector<std::unique_ptr<waybar::AModule>> modules_center_;
|
||||||
std::vector<std::unique_ptr<waybar::AModule>> modules_right_;
|
std::vector<std::unique_ptr<waybar::AModule>> modules_right_;
|
||||||
|
waybar::util::JsonParser parser_;
|
||||||
|
// Contains pointers to modules in modules_left_, etc.
|
||||||
|
std::map<std::string, waybar::modules::Custom *> custom_modules_;
|
||||||
|
std::vector<std::unique_ptr<waybar::util::WorkerThread>> custom_threads_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar
|
} // namespace waybar
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
#include "util/command.hpp"
|
#include "util/command.hpp"
|
||||||
|
@ -15,6 +17,7 @@ namespace waybar::modules {
|
||||||
class Custom : public ALabel {
|
class Custom : public ALabel {
|
||||||
public:
|
public:
|
||||||
Custom(const std::string&, const std::string&, const Json::Value&);
|
Custom(const std::string&, const std::string&, const Json::Value&);
|
||||||
|
void injectOutput(Json::Value);
|
||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
void refresh(int /*signal*/);
|
void refresh(int /*signal*/);
|
||||||
|
|
||||||
|
@ -23,6 +26,7 @@ class Custom : public ALabel {
|
||||||
void workerOutputCallback(std::string);
|
void workerOutputCallback(std::string);
|
||||||
void parseOutputRaw(const std::string&);
|
void parseOutputRaw(const std::string&);
|
||||||
void parseOutputJson(const std::string&);
|
void parseOutputJson(const std::string&);
|
||||||
|
void handleOutputJson(const Json::Value&);
|
||||||
void handleEvent();
|
void handleEvent();
|
||||||
bool handleScroll(GdkEventScroll* e);
|
bool handleScroll(GdkEventScroll* e);
|
||||||
bool handleToggle(GdkEventButton* const& e);
|
bool handleToggle(GdkEventButton* const& e);
|
||||||
|
@ -33,9 +37,12 @@ class Custom : public ALabel {
|
||||||
std::string tooltip_;
|
std::string tooltip_;
|
||||||
std::vector<std::string> class_;
|
std::vector<std::string> class_;
|
||||||
int percentage_;
|
int percentage_;
|
||||||
util::command::res output_;
|
// Worker exit code, worker raw output, or injected JSON.
|
||||||
|
// Injected JSON is string for raw output, object for JSON output.
|
||||||
|
std::variant<std::monostate, int, std::string, Json::Value> output_;
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
|
// Protects output_ since it is accessed from many threads.
|
||||||
|
std::mutex output_mutex_;
|
||||||
waybar::util::WorkerThread thread_;
|
waybar::util::WorkerThread thread_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
31
src/bar.cpp
31
src/bar.cpp
|
@ -508,6 +508,20 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||||
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
|
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
|
||||||
|
|
||||||
setupWidgets();
|
setupWidgets();
|
||||||
|
|
||||||
|
if (config["custom-execs"].isArray()) {
|
||||||
|
Json::ArrayIndex size = config["custom-execs"].size();
|
||||||
|
for (Json::ArrayIndex i = 0; i < size; i++) {
|
||||||
|
custom_threads_.push_back(std::make_unique<waybar::util::WorkerThread>(
|
||||||
|
config["custom-execs"][i],
|
||||||
|
[this](std::string output) { customExecOutputCallback(output); },
|
||||||
|
[this](int exit_code) {
|
||||||
|
// Do nothing. We don't know which modules this was meant for.
|
||||||
|
spdlog::debug("Custom exec exited with code {}", exit_code);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.show_all();
|
window.show_all();
|
||||||
|
|
||||||
if (spdlog::should_log(spdlog::level::debug)) {
|
if (spdlog::should_log(spdlog::level::debug)) {
|
||||||
|
@ -521,6 +535,19 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::Bar::customExecOutputCallback(std::string output) {
|
||||||
|
// parsed is an object with module names as keys and output (string or object) as values.
|
||||||
|
auto parsed = parser_.parse(output);
|
||||||
|
auto end = parsed.end();
|
||||||
|
for (Json::Value::const_iterator module_it = parsed.begin(); module_it != end; ++module_it) {
|
||||||
|
auto it = custom_modules_.find(module_it.name());
|
||||||
|
if (it != custom_modules_.end()) {
|
||||||
|
it->second->injectOutput(*module_it);
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void waybar::Bar::onMap(GdkEventAny*) {
|
void waybar::Bar::onMap(GdkEventAny*) {
|
||||||
/*
|
/*
|
||||||
* Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor).
|
* Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor).
|
||||||
|
@ -612,6 +639,10 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos) {
|
||||||
for (const auto& name : config[pos]) {
|
for (const auto& name : config[pos]) {
|
||||||
try {
|
try {
|
||||||
auto module = factory.makeModule(name.asString());
|
auto module = factory.makeModule(name.asString());
|
||||||
|
auto custom = dynamic_cast<waybar::modules::Custom*>(module);
|
||||||
|
if (custom != nullptr) {
|
||||||
|
custom_modules_[name.asString()] = custom;
|
||||||
|
}
|
||||||
if (pos == "modules-left") {
|
if (pos == "modules-left") {
|
||||||
modules_left_.emplace_back(module);
|
modules_left_.emplace_back(module);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,26 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Custom::workerExitCallback(int exit_code) {
|
void waybar::modules::Custom::workerExitCallback(int exit_code) {
|
||||||
output_ = {exit_code, ""};
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(output_mutex_);
|
||||||
|
output_ = exit_code;
|
||||||
|
}
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Custom::workerOutputCallback(std::string output) {
|
void waybar::modules::Custom::workerOutputCallback(std::string output) {
|
||||||
output_ = {0, std::move(output)};
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(output_mutex_);
|
||||||
|
output_ = std::move(output);
|
||||||
|
}
|
||||||
|
dp.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void waybar::modules::Custom::injectOutput(Json::Value output) {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(output_mutex_);
|
||||||
|
output_ = std::move(output);
|
||||||
|
}
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,23 +61,52 @@ bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Custom::update() -> void {
|
auto waybar::modules::Custom::update() -> void {
|
||||||
// Hide label if output is empty
|
std::variant<std::monostate, int, std::string, Json::Value> output;
|
||||||
if ((config_["exec"].isString() || config_["exec-if"].isString()) &&
|
{
|
||||||
(output_.out.empty() || output_.exit_code != 0)) {
|
std::lock_guard<std::mutex> guard(output_mutex_);
|
||||||
event_box_.hide();
|
output = std::move(output_);
|
||||||
} else {
|
output_ = std::monostate();
|
||||||
if (config_["return-type"].asString() == "json") {
|
|
||||||
parseOutputJson(output_.out);
|
|
||||||
} else {
|
|
||||||
parseOutputRaw(output_.out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide label if output is empty
|
||||||
|
bool hide = config_["exec"].isString() || config_["exec-if"].isString();
|
||||||
|
|
||||||
|
if (std::holds_alternative<std::monostate>(output)) {
|
||||||
|
if (hide) {
|
||||||
|
// No changes since the last update, do nothing.
|
||||||
|
ALabel::update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (std::holds_alternative<int>(output)) {
|
||||||
|
// The exit code is non-zero if we get here, so do nothing to hide the label.
|
||||||
|
} else if (std::holds_alternative<std::string>(output)) {
|
||||||
|
const std::string& s = std::get<std::string>(output);
|
||||||
|
if (!s.empty()) {
|
||||||
|
hide = false;
|
||||||
|
if (config_["return-type"].asString() == "json") {
|
||||||
|
parseOutputJson(s);
|
||||||
|
} else {
|
||||||
|
parseOutputRaw(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hide = false;
|
||||||
|
const Json::Value& value = std::get<Json::Value>(output);
|
||||||
|
if (value.isString()) {
|
||||||
|
parseOutputRaw(value.asString());
|
||||||
|
} else {
|
||||||
|
handleOutputJson(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hide) {
|
||||||
auto str = fmt::format(format_,
|
auto str = fmt::format(format_,
|
||||||
text_,
|
text_,
|
||||||
fmt::arg("alt", alt_),
|
fmt::arg("alt", alt_),
|
||||||
fmt::arg("icon", getIcon(percentage_, alt_)),
|
fmt::arg("icon", getIcon(percentage_, alt_)),
|
||||||
fmt::arg("percentage", percentage_));
|
fmt::arg("percentage", percentage_));
|
||||||
if (str.empty()) {
|
if (str.empty()) {
|
||||||
event_box_.hide();
|
hide = true;
|
||||||
} else {
|
} else {
|
||||||
label_.set_markup(str);
|
label_.set_markup(str);
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
|
@ -87,6 +130,11 @@ auto waybar::modules::Custom::update() -> void {
|
||||||
event_box_.show();
|
event_box_.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hide) {
|
||||||
|
event_box_.hide();
|
||||||
|
}
|
||||||
|
|
||||||
// Call parent update
|
// Call parent update
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
@ -121,29 +169,33 @@ void waybar::modules::Custom::parseOutputJson(const std::string& output_str) {
|
||||||
class_.clear();
|
class_.clear();
|
||||||
while (getline(output, line)) {
|
while (getline(output, line)) {
|
||||||
auto parsed = parser_.parse(line);
|
auto parsed = parser_.parse(line);
|
||||||
if (config_["escape"].isBool() && config_["escape"].asBool()) {
|
handleOutputJson(parsed);
|
||||||
text_ = Glib::Markup::escape_text(parsed["text"].asString());
|
|
||||||
} else {
|
|
||||||
text_ = parsed["text"].asString();
|
|
||||||
}
|
|
||||||
if (config_["escape"].isBool() && config_["escape"].asBool()) {
|
|
||||||
alt_ = Glib::Markup::escape_text(parsed["alt"].asString());
|
|
||||||
} else {
|
|
||||||
alt_ = parsed["alt"].asString();
|
|
||||||
}
|
|
||||||
tooltip_ = parsed["tooltip"].asString();
|
|
||||||
if (parsed["class"].isString()) {
|
|
||||||
class_.push_back(parsed["class"].asString());
|
|
||||||
} else if (parsed["class"].isArray()) {
|
|
||||||
for (auto const& c : parsed["class"]) {
|
|
||||||
class_.push_back(c.asString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!parsed["percentage"].asString().empty() && parsed["percentage"].isUInt()) {
|
|
||||||
percentage_ = parsed["percentage"].asUInt();
|
|
||||||
} else {
|
|
||||||
percentage_ = 0;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::modules::Custom::handleOutputJson(const Json::Value& value) {
|
||||||
|
if (config_["escape"].isBool() && config_["escape"].asBool()) {
|
||||||
|
text_ = Glib::Markup::escape_text(value["text"].asString());
|
||||||
|
} else {
|
||||||
|
text_ = value["text"].asString();
|
||||||
|
}
|
||||||
|
if (config_["escape"].isBool() && config_["escape"].asBool()) {
|
||||||
|
alt_ = Glib::Markup::escape_text(value["alt"].asString());
|
||||||
|
} else {
|
||||||
|
alt_ = value["alt"].asString();
|
||||||
|
}
|
||||||
|
tooltip_ = value["tooltip"].asString();
|
||||||
|
if (value["class"].isString()) {
|
||||||
|
class_.push_back(value["class"].asString());
|
||||||
|
} else if (value["class"].isArray()) {
|
||||||
|
for (auto const& c : value["class"]) {
|
||||||
|
class_.push_back(c.asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!value["percentage"].asString().empty() && value["percentage"].isUInt()) {
|
||||||
|
percentage_ = value["percentage"].asUInt();
|
||||||
|
} else {
|
||||||
|
percentage_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue