Move CustomList into Custom
parent
8cc877368c
commit
185755df64
|
@ -5,21 +5,50 @@
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "AButton.hpp"
|
#include "AModule.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
#include "util/command.hpp"
|
#include "util/command.hpp"
|
||||||
#include "util/json.hpp"
|
#include "util/json.hpp"
|
||||||
#include "util/sleeper_thread.hpp"
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
|
#include <gtkmm/button.h>
|
||||||
|
#include <gtkmm/label.h>
|
||||||
|
#include <gtkmm/box.h>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
|
|
||||||
class Custom : public AButton {
|
class Custom : public AModule {
|
||||||
public:
|
public:
|
||||||
Custom(const std::string&, const std::string&, const Json::Value&);
|
Custom(const std::string&, const waybar::Bar&, const std::string&, const Json::Value&);
|
||||||
~Custom();
|
~Custom();
|
||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
void refresh(int /*signal*/);
|
void refresh(int /*signal*/);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
std::string name_;
|
||||||
|
std::string text_;
|
||||||
|
std::string alt_;
|
||||||
|
std::string tooltip_;
|
||||||
|
std::string format_;
|
||||||
|
std::string onclick_;
|
||||||
|
bool hide_;
|
||||||
|
std::vector<std::string> class_;
|
||||||
|
int percentage_;
|
||||||
|
Node() : name_("unnamed"),
|
||||||
|
text_(""),
|
||||||
|
alt_(""),
|
||||||
|
tooltip_(""),
|
||||||
|
format_(""),
|
||||||
|
onclick_(""),
|
||||||
|
hide_(false),
|
||||||
|
class_(std::vector<std::string>()),
|
||||||
|
percentage_(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
void delayWorker();
|
void delayWorker();
|
||||||
void continuousWorker();
|
void continuousWorker();
|
||||||
void parseOutputRaw();
|
void parseOutputRaw();
|
||||||
|
@ -27,17 +56,22 @@ class Custom : public AButton {
|
||||||
void handleEvent();
|
void handleEvent();
|
||||||
bool handleScroll(GdkEventScroll* e);
|
bool handleScroll(GdkEventScroll* e);
|
||||||
bool handleToggle(GdkEventButton* const& e);
|
bool handleToggle(GdkEventButton* const& e);
|
||||||
|
void handleClick(std::string name);
|
||||||
|
Node parseItem(Json::Value &parsed);
|
||||||
|
Gtk::Button &addButton(const Node &node);
|
||||||
|
|
||||||
|
const std::chrono::seconds interval_;
|
||||||
|
util::command::res output_;
|
||||||
|
|
||||||
|
std::vector<Node> results_;
|
||||||
|
std::vector<Node> prev_;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, Gtk::Button> buttons_;
|
||||||
|
Gtk::Box box_;
|
||||||
|
|
||||||
const std::string name_;
|
|
||||||
std::string text_;
|
|
||||||
std::string id_;
|
|
||||||
std::string alt_;
|
|
||||||
std::string tooltip_;
|
|
||||||
std::vector<std::string> class_;
|
|
||||||
int percentage_;
|
|
||||||
FILE* fp_;
|
FILE* fp_;
|
||||||
int pid_;
|
int pid_;
|
||||||
util::command::res output_;
|
|
||||||
util::JsonParser parser_;
|
util::JsonParser parser_;
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
util::SleeperThread thread_;
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
#include <csignal>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <gtkmm/button.h>
|
|
||||||
#include <gtkmm/label.h>
|
|
||||||
#include <gtkmm/box.h>
|
|
||||||
#include "bar.hpp"
|
|
||||||
#include "AModule.hpp"
|
|
||||||
#include "util/command.hpp"
|
|
||||||
#include "util/json.hpp"
|
|
||||||
#include "util/sleeper_thread.hpp"
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace waybar::modules {
|
|
||||||
|
|
||||||
class CustomList : public AModule {
|
|
||||||
public:
|
|
||||||
CustomList(const std::string&, const waybar::Bar&, const std::string&,const Json::Value&);
|
|
||||||
~CustomList();
|
|
||||||
auto update() -> void;
|
|
||||||
void refresh(int /*signal*/);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void delayWorker();
|
|
||||||
void continuousWorker();
|
|
||||||
void parseOutputRaw();
|
|
||||||
void parseOutputJson();
|
|
||||||
void handleEvent();
|
|
||||||
bool handleScroll(GdkEventScroll* e);
|
|
||||||
bool handleToggle(GdkEventButton* const& e);
|
|
||||||
|
|
||||||
Gtk::Button &addButton(const Json::Value &node);
|
|
||||||
void handleClick(std::string);
|
|
||||||
|
|
||||||
std::unordered_map<std::string, Gtk::Button> buttons_;
|
|
||||||
std::vector<Json::Value> results_;
|
|
||||||
std::vector<Json::Value> prev_;
|
|
||||||
const std::string name_;
|
|
||||||
const Bar& bar_;
|
|
||||||
Gtk::Box box_;
|
|
||||||
std::chrono::seconds interval_;
|
|
||||||
|
|
||||||
std::string id_;
|
|
||||||
std::vector<std::string> class_;
|
|
||||||
FILE* fp_;
|
|
||||||
int pid_;
|
|
||||||
util::command::res output_;
|
|
||||||
util::JsonParser parser_;
|
|
||||||
|
|
||||||
util::SleeperThread thread_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace waybar::modules
|
|
|
@ -147,10 +147,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||||
return new waybar::modules::Temperature(id, config_[name]);
|
return new waybar::modules::Temperature(id, config_[name]);
|
||||||
}
|
}
|
||||||
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
|
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
|
||||||
return new waybar::modules::Custom(ref.substr(7), id, config_[name]);
|
return new waybar::modules::Custom(ref.substr(7), bar_, id, config_[name]);
|
||||||
}
|
|
||||||
if(ref.compare(0, 11, "customlist/") == 0 && ref.size() > 11) {
|
|
||||||
return new waybar::modules::CustomList(ref.substr(12), bar_, id, config_[name]);
|
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());
|
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());
|
||||||
|
|
|
@ -2,14 +2,20 @@
|
||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
|
waybar::modules::Custom::Custom(const std::string& name,
|
||||||
|
const Bar &bar,
|
||||||
|
const std::string& id,
|
||||||
const Json::Value& config)
|
const Json::Value& config)
|
||||||
: AButton(config, "custom-" + name, id, "{}"),
|
: AModule(config, "custom-" + name, id, false, !config["disable-scroll"].asBool()),
|
||||||
name_(name),
|
interval_(config["interval"].isUInt() ? config_["interval"].asUInt() : 0),
|
||||||
id_(id),
|
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||||
percentage_(0),
|
|
||||||
fp_(nullptr),
|
fp_(nullptr),
|
||||||
pid_(-1) {
|
pid_(-1) {
|
||||||
|
box_.set_name("custom-" + name);
|
||||||
|
event_box_.add(box_);
|
||||||
|
if(!id.empty()) {
|
||||||
|
box_.get_style_context()->add_class(id);
|
||||||
|
}
|
||||||
dp.emit();
|
dp.emit();
|
||||||
if (interval_.count() > 0) {
|
if (interval_.count() > 0) {
|
||||||
delayWorker();
|
delayWorker();
|
||||||
|
@ -80,7 +86,6 @@ void waybar::modules::Custom::continuousWorker() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::string output = buff;
|
std::string output = buff;
|
||||||
|
|
||||||
// Remove last newline
|
// Remove last newline
|
||||||
if (!output.empty() && output[output.length() - 1] == '\n') {
|
if (!output.empty() && output[output.length() - 1] == '\n') {
|
||||||
output.erase(output.length() - 1);
|
output.erase(output.length() - 1);
|
||||||
|
@ -104,13 +109,13 @@ void waybar::modules::Custom::handleEvent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) {
|
bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) {
|
||||||
auto ret = AButton::handleScroll(e);
|
auto ret = AModule::handleScroll(e);
|
||||||
handleEvent();
|
handleEvent();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) {
|
bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) {
|
||||||
auto ret = AButton::handleToggle(e);
|
auto ret = AModule::handleToggle(e);
|
||||||
handleEvent();
|
handleEvent();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -121,99 +126,188 @@ auto waybar::modules::Custom::update() -> void {
|
||||||
(output_.out.empty() || output_.exit_code != 0)) {
|
(output_.out.empty() || output_.exit_code != 0)) {
|
||||||
event_box_.hide();
|
event_box_.hide();
|
||||||
} else {
|
} else {
|
||||||
|
event_box_.show();
|
||||||
if (config_["return-type"].asString() == "json") {
|
if (config_["return-type"].asString() == "json") {
|
||||||
parseOutputJson();
|
parseOutputJson();
|
||||||
} else {
|
} else {
|
||||||
parseOutputRaw();
|
parseOutputRaw();
|
||||||
}
|
}
|
||||||
auto str = fmt::format(format_, text_, fmt::arg("alt", alt_),
|
bool needReorder = false;
|
||||||
fmt::arg("icon", getIcon(percentage_, alt_)),
|
for(auto res = results_.begin(); res != results_.end(); res++) {
|
||||||
fmt::arg("percentage", percentage_));
|
std::string name = res->name_;
|
||||||
if (str.empty()) {
|
auto bit = buttons_.find(name);
|
||||||
event_box_.hide();
|
if(bit == buttons_.end()) {
|
||||||
|
needReorder = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &button = bit == buttons_.end() ? addButton(*res) : bit->second;
|
||||||
|
|
||||||
|
auto str = res->text_;
|
||||||
|
//auto str = fmt::format(res->format_, res->text_, fmt::arg("alt", res->alt_),
|
||||||
|
//fmt::arg("icon", getIcon(res->percentage_, res->alt_)), TODO
|
||||||
|
// fmt::arg("percentage", res->percentage_));
|
||||||
|
|
||||||
|
if(str.empty() || res->hide_) {
|
||||||
|
button.hide();
|
||||||
} else {
|
} else {
|
||||||
|
Gtk::Label *label_ = static_cast<Gtk::Label *>(button.get_children()[0]);
|
||||||
label_->set_markup(str);
|
label_->set_markup(str);
|
||||||
if (tooltipEnabled()) {
|
button.show();
|
||||||
if (text_ == tooltip_) {
|
auto prev = std::find_if(prev_.begin(), prev_.end(), [name](auto p) {
|
||||||
if (button_.get_tooltip_markup() != str) {
|
return p.name_ == name;
|
||||||
button_.set_tooltip_markup(str);
|
});
|
||||||
|
if(prev != prev_.end()) {
|
||||||
|
for(auto it : prev->class_) {
|
||||||
|
if(std::find(res->class_.begin(), res->class_.end(), it) == res->class_.end()) {
|
||||||
|
button.get_style_context()->remove_class(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(auto it : res->class_) {
|
||||||
|
if(prev != prev_.end()) {
|
||||||
|
if(std::find(prev->class_.begin(), prev->class_.end(), it) == prev->class_.end()) {
|
||||||
|
button.get_style_context()->add_class(it);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (button_.get_tooltip_markup() != tooltip_) {
|
button.get_style_context()->add_class(it);
|
||||||
button_.set_tooltip_markup(tooltip_);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(needReorder) {
|
||||||
|
box_.reorder_child(button, res - results_.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(button.get_tooltip_markup() != res->tooltip_) {
|
||||||
|
button.set_tooltip_markup(res->tooltip_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto classes = button_.get_style_context()->list_classes();
|
|
||||||
for (auto const& c : classes) {
|
for(auto prev : prev_) {
|
||||||
if (c == id_) continue;
|
auto res_it = std::find_if(results_.begin(), results_.end(), [prev](auto it) {
|
||||||
button_.get_style_context()->remove_class(c);
|
return it.name_ == prev.name_;
|
||||||
}
|
});
|
||||||
for (auto const& c : class_) {
|
if(res_it == results_.end()) {
|
||||||
button_.get_style_context()->add_class(c);
|
auto bit = buttons_.find(prev.name_);
|
||||||
}
|
if(bit != buttons_.end()) {
|
||||||
button_.get_style_context()->add_class("flat");
|
auto &button = bit->second;
|
||||||
button_.get_style_context()->add_class("text-button");
|
box_.remove(button);
|
||||||
event_box_.show();
|
buttons_.erase(bit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_ = results_;
|
||||||
// Call parent update
|
// Call parent update
|
||||||
AButton::update();
|
AModule::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Custom::parseOutputRaw() {
|
void waybar::modules::Custom::parseOutputRaw() {
|
||||||
|
Node n = Node();
|
||||||
|
// Retain name if there is only one node
|
||||||
|
n.name_ = name_;
|
||||||
std::istringstream output(output_.out);
|
std::istringstream output(output_.out);
|
||||||
std::string line;
|
std::string line;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (getline(output, line)) {
|
while (getline(output, line)) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
if (config_["escape"].isBool() && config_["escape"].asBool()) {
|
if (config_["escape"].isBool() && config_["escape"].asBool()) {
|
||||||
text_ = Glib::Markup::escape_text(line);
|
n.text_ = Glib::Markup::escape_text(line);
|
||||||
} else {
|
} else {
|
||||||
text_ = line;
|
n.text_ = line;
|
||||||
}
|
}
|
||||||
tooltip_ = line;
|
n.tooltip_ = line;
|
||||||
class_.clear();
|
n.class_.clear();
|
||||||
} else if (i == 1) {
|
} else if (i == 1) {
|
||||||
tooltip_ = line;
|
n.tooltip_ = line;
|
||||||
} else if (i == 2) {
|
} else if (i == 2) {
|
||||||
class_.push_back(line);
|
n.class_.push_back(line);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
results_.clear();
|
||||||
|
results_.push_back(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
waybar::modules::Custom::Node waybar::modules::Custom::parseItem(Json::Value &parsed) {
|
||||||
|
Node n;
|
||||||
|
if (config_["escape"].isBool() && config_["escape"].asBool()) {
|
||||||
|
n.text_ = Glib::Markup::escape_text(parsed["text"].asString());
|
||||||
|
} else {
|
||||||
|
n.text_ = parsed["text"].asString();
|
||||||
|
}
|
||||||
|
if (config_["escape"].isBool() && config_["escape"].asBool()) {
|
||||||
|
n.alt_ = Glib::Markup::escape_text(parsed["alt"].asString());
|
||||||
|
} else {
|
||||||
|
n.alt_ = parsed["alt"].asString();
|
||||||
|
}
|
||||||
|
n.tooltip_ = parsed["tooltip"].asString();
|
||||||
|
if (parsed["class"].isString()) {
|
||||||
|
n.class_.push_back(parsed["class"].asString());
|
||||||
|
} else if (parsed["class"].isArray()) {
|
||||||
|
for (auto const& c : parsed["class"]) {
|
||||||
|
n.class_.push_back(c.asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!parsed["name"].asString().empty()) {
|
||||||
|
n.name_ = name_ + parsed["name"].asString();
|
||||||
|
}
|
||||||
|
if (!parsed["percentage"].asString().empty() && parsed["percentage"].isUInt()) {
|
||||||
|
n.percentage_ = parsed["percentage"].asUInt();
|
||||||
|
} else {
|
||||||
|
n.percentage_ = 0;
|
||||||
|
}
|
||||||
|
if (!parsed["onclick"].asString().empty() && parsed["onclick"].isString()) {
|
||||||
|
n.onclick_ = parsed["onclick"].asString();
|
||||||
|
}
|
||||||
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Custom::parseOutputJson() {
|
void waybar::modules::Custom::parseOutputJson() {
|
||||||
std::istringstream output(output_.out);
|
std::istringstream output(output_.out);
|
||||||
std::string line;
|
std::string line;
|
||||||
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()) {
|
results_.clear();
|
||||||
text_ = Glib::Markup::escape_text(parsed["text"].asString());
|
if(parsed["data"].isArray()) {
|
||||||
|
for(auto it : parsed["data"]) {
|
||||||
|
results_.push_back(parseItem(it));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
text_ = parsed["text"].asString();
|
Node n = parseItem(parsed);
|
||||||
}
|
n.name_ = name_ + "-node";
|
||||||
if (config_["escape"].isBool() && config_["escape"].asBool()) {
|
results_.push_back(n);
|
||||||
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;
|
|
||||||
|
Gtk::Button &waybar::modules::Custom::addButton(const waybar::modules::Custom::Node &node) {
|
||||||
|
auto pair = buttons_.emplace(node.name_, node.name_);
|
||||||
|
auto &&button = pair.first->second;
|
||||||
|
box_.pack_start(button, false, false, 0);
|
||||||
|
button.set_name(name_ + node.name_);
|
||||||
|
button.set_relief(Gtk::RELIEF_NONE);
|
||||||
|
if(!config_["disable-click"].asBool()) {
|
||||||
|
button.signal_pressed().connect([this, node] {
|
||||||
|
handleClick(node.name_);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void waybar::modules::Custom::handleClick(std::string name) {
|
||||||
|
auto node = std::find_if(results_.begin(), results_.end(), [name](auto it) {
|
||||||
|
return it.name_ == name;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(node == results_.end())
|
||||||
|
return;
|
||||||
|
auto cmd = node->onclick_;
|
||||||
|
|
||||||
|
if(!cmd.empty()) {
|
||||||
|
util::command::execNoRead(cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,261 +0,0 @@
|
||||||
#include "modules/custom_list.hpp"
|
|
||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
|
|
||||||
waybar::modules::CustomList::CustomList(const std::string& name,
|
|
||||||
const Bar &bar,
|
|
||||||
const std::string& id,
|
|
||||||
const Json::Value& config)
|
|
||||||
: AModule(config, "custom-list-" + name, id, false, !config["disable-scroll"].asBool()),
|
|
||||||
name_(name),
|
|
||||||
bar_(bar),
|
|
||||||
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
|
|
||||||
interval_(config["interval"].isUInt() ? config_["interval"].asUInt() : 0),
|
|
||||||
fp_(nullptr),
|
|
||||||
pid_(-1) {
|
|
||||||
box_.set_name("custom-list-" + name);
|
|
||||||
event_box_.add(box_);
|
|
||||||
if(!id.empty())
|
|
||||||
box_.get_style_context()->add_class(id);
|
|
||||||
if (interval_.count() > 0) {
|
|
||||||
delayWorker();
|
|
||||||
} else if (config_["exec"].isString()) {
|
|
||||||
continuousWorker();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
waybar::modules::CustomList::~CustomList() {
|
|
||||||
if (pid_ != -1) {
|
|
||||||
killpg(pid_, SIGTERM);
|
|
||||||
pid_ = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::modules::CustomList::delayWorker() {
|
|
||||||
thread_ = [this] {
|
|
||||||
bool can_update = true;
|
|
||||||
if (config_["exec-if"].isString()) {
|
|
||||||
output_ = util::command::execNoRead(config_["exec-if"].asString());
|
|
||||||
if (output_.exit_code != 0) {
|
|
||||||
can_update = false;
|
|
||||||
dp.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (can_update) {
|
|
||||||
if (config_["exec"].isString()) {
|
|
||||||
output_ = util::command::exec(config_["exec"].asString());
|
|
||||||
}
|
|
||||||
dp.emit();
|
|
||||||
}
|
|
||||||
thread_.sleep_for(interval_);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::modules::CustomList::continuousWorker() {
|
|
||||||
auto cmd = config_["exec"].asString();
|
|
||||||
pid_ = -1;
|
|
||||||
fp_ = util::command::open(cmd, pid_);
|
|
||||||
if (!fp_) {
|
|
||||||
throw std::runtime_error("Unable to open " + cmd);
|
|
||||||
}
|
|
||||||
thread_ = [this, cmd] {
|
|
||||||
char* buff = nullptr;
|
|
||||||
size_t len = 0;
|
|
||||||
if (getline(&buff, &len, fp_) == -1) {
|
|
||||||
int exit_code = 1;
|
|
||||||
if (fp_) {
|
|
||||||
exit_code = WEXITSTATUS(util::command::close(fp_, pid_));
|
|
||||||
fp_ = nullptr;
|
|
||||||
}
|
|
||||||
if (exit_code != 0) {
|
|
||||||
output_ = {exit_code, ""};
|
|
||||||
dp.emit();
|
|
||||||
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
|
|
||||||
}
|
|
||||||
if (config_["restart-interval"].isUInt()) {
|
|
||||||
pid_ = -1;
|
|
||||||
thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt()));
|
|
||||||
fp_ = util::command::open(cmd, pid_);
|
|
||||||
if (!fp_) {
|
|
||||||
throw std::runtime_error("Unable to open " + cmd);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
thread_.stop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::string output = buff;
|
|
||||||
|
|
||||||
// Remove last newline
|
|
||||||
if (!output.empty() && output[output.length() - 1] == '\n') {
|
|
||||||
output.erase(output.length() - 1);
|
|
||||||
}
|
|
||||||
output_ = {0, output};
|
|
||||||
dp.emit();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::modules::CustomList::refresh(int sig) {
|
|
||||||
if (sig == SIGRTMIN + config_["signal"].asInt()) {
|
|
||||||
thread_.wake_up();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::modules::CustomList::handleEvent() {
|
|
||||||
if (!config_["exec-on-event"].isBool() || config_["exec-on-event"].asBool()) {
|
|
||||||
thread_.wake_up();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waybar::modules::CustomList::handleScroll(GdkEventScroll* e) {
|
|
||||||
//auto ret = ALabel::handleScroll(e);
|
|
||||||
///* TODO */
|
|
||||||
handleEvent();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waybar::modules::CustomList::handleToggle(GdkEventButton* const& e) {
|
|
||||||
/* TODO */
|
|
||||||
//auto ret = ALabel::handleToggle(e);
|
|
||||||
handleEvent();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto waybar::modules::CustomList::update() -> void {
|
|
||||||
// Hide label if output is empty
|
|
||||||
if ((config_["exec"].isString() || config_["exec-if"].isString()) &&
|
|
||||||
(output_.out.empty() || output_.exit_code != 0)) {
|
|
||||||
event_box_.hide();
|
|
||||||
} else {
|
|
||||||
parseOutputJson();
|
|
||||||
}
|
|
||||||
bool needReorder = false;
|
|
||||||
for(auto res = results_.begin(); res != results_.end(); res++) {
|
|
||||||
std::string name = (*res)["name"].asString();
|
|
||||||
auto bit = buttons_.find(name);
|
|
||||||
if(bit == buttons_.end()) {
|
|
||||||
needReorder = true;
|
|
||||||
}
|
|
||||||
auto &button = bit == buttons_.end() ? addButton(*res) : bit->second;
|
|
||||||
|
|
||||||
auto prev = std::find_if(prev_.begin(), prev_.end(), [name](auto p) {
|
|
||||||
return p["name"] == name;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Classes
|
|
||||||
std::vector<std::string> prev_classes;
|
|
||||||
if(prev != prev_.end()) {
|
|
||||||
if ((*prev)["class"].isString()) {
|
|
||||||
prev_classes.push_back((*prev)["class"].asString());
|
|
||||||
} else if ((*prev)["class"].isArray()) {
|
|
||||||
for (auto const& c : (*prev)["class"]) {
|
|
||||||
prev_classes.push_back(c.asString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::vector<std::string> new_classes;
|
|
||||||
if ((*res)["class"].isString()) {
|
|
||||||
new_classes.push_back((*res)["class"].asString());
|
|
||||||
} else if ((*res)["class"].isArray()) {
|
|
||||||
for (auto const& c : (*res)["class"]) {
|
|
||||||
new_classes.push_back(c.asString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto it : prev_classes) {
|
|
||||||
if(std::find(new_classes.begin(), new_classes.end(), it) == new_classes.end())
|
|
||||||
button.get_style_context()->remove_class(it);
|
|
||||||
}
|
|
||||||
for(auto it : new_classes) {
|
|
||||||
if(std::find(prev_classes.begin(), prev_classes.end(), it) == prev_classes.end())
|
|
||||||
button.get_style_context()->add_class(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reorder to match results order
|
|
||||||
if(needReorder) {
|
|
||||||
box_.reorder_child(button, res - results_.begin());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!(*res)["disable-markup"].asBool()) {
|
|
||||||
static_cast<Gtk::Label *>(button.get_children()[0])->set_markup((*res)["text"].asString());
|
|
||||||
} else {
|
|
||||||
button.set_label((*res)["text"].asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if((*res)["hide"].asBool()) {
|
|
||||||
button.hide();
|
|
||||||
} else {
|
|
||||||
button.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(button.get_tooltip_markup() != (*res)["tooltip"].asString()) {
|
|
||||||
button.set_tooltip_markup((*res)["tooltip"].asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
for(auto prev : prev_) {
|
|
||||||
auto res_it = std::find_if(results_.begin(), results_.end(), [prev](auto it) {
|
|
||||||
return it["name"] == prev["name"];
|
|
||||||
});
|
|
||||||
if(res_it == results_.end()) {
|
|
||||||
// Node has been removed
|
|
||||||
auto bit = buttons_.find(prev["name"].asString());
|
|
||||||
if(bit != buttons_.end()) {
|
|
||||||
auto &button = bit->second;
|
|
||||||
box_.remove(button);
|
|
||||||
buttons_.erase(bit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
prev_ = results_;
|
|
||||||
|
|
||||||
// Call parent update
|
|
||||||
AModule::update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::modules::CustomList::handleClick(std::string name) {
|
|
||||||
auto node = std::find_if(results_.begin(), results_.end(), [name](auto it) {
|
|
||||||
return it["name"] == name;
|
|
||||||
});
|
|
||||||
|
|
||||||
if(node == results_.end())
|
|
||||||
return;
|
|
||||||
auto cmd = (*node)["onclick"];
|
|
||||||
|
|
||||||
if(cmd.isString()) {
|
|
||||||
util::command::execNoRead(cmd.asString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk::Button &waybar::modules::CustomList::addButton(const Json::Value &node) {
|
|
||||||
auto pair = buttons_.emplace(node["name"].asString(), node["name"].asString());
|
|
||||||
auto &&button = pair.first->second;
|
|
||||||
box_.pack_start(button, false, false, 0);
|
|
||||||
button.set_name(name_ + node["name"].asString());
|
|
||||||
button.set_relief(Gtk::RELIEF_NONE);
|
|
||||||
if(!config_["disable-click"].asBool()) {
|
|
||||||
button.signal_pressed().connect([this, node] {
|
|
||||||
handleClick(node["name"].asString());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waybar::modules::CustomList::parseOutputJson() {
|
|
||||||
std::istringstream output(output_.out);
|
|
||||||
std::string line;
|
|
||||||
class_.clear();
|
|
||||||
while (getline(output, line)) {
|
|
||||||
auto parsed = parser_.parse(line);
|
|
||||||
if(!parsed["data"].isArray()) {
|
|
||||||
throw std::runtime_error("Output should be a list");
|
|
||||||
}
|
|
||||||
results_.clear();
|
|
||||||
for(auto it : parsed["data"]) {
|
|
||||||
results_.push_back(it);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue