289 lines
8.4 KiB
C++
289 lines
8.4 KiB
C++
#include "modules/image.hpp"
|
|
|
|
#include <json/value.h>
|
|
|
|
#include <config.hpp>
|
|
|
|
waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
|
|
: AModule(config, "image", id) {
|
|
strategy_ = getStrategy(id, config, MODULE_CLASS, event_box_, tooltipEnabled());
|
|
|
|
const auto once = std::chrono::milliseconds::max();
|
|
if (!config_.isMember("interval") || config_["interval"].isNull() ||
|
|
config_["interval"] == "once") {
|
|
interval_ = once;
|
|
} else if (config_["interval"].isNumeric()) {
|
|
const auto interval_seconds = config_["interval"].asDouble();
|
|
if (interval_seconds <= 0) {
|
|
interval_ = once;
|
|
} else {
|
|
interval_ =
|
|
std::chrono::milliseconds(std::max(1L, // Minimum 1ms due to millisecond precision
|
|
static_cast<long>(interval_seconds * 1000)));
|
|
}
|
|
} else {
|
|
interval_ = once;
|
|
}
|
|
|
|
delayWorker();
|
|
}
|
|
|
|
auto waybar::modules::Image::getStrategy(const std::string& id, const Json::Value& cfg,
|
|
const std::string& module, Gtk::EventBox& evbox,
|
|
bool hasTooltip)
|
|
-> std::unique_ptr<waybar::modules::image::IStrategy> {
|
|
std::unique_ptr<waybar::modules::image::IStrategy> strat;
|
|
if (!cfg["multiple"].empty() && cfg["multiple"].asBool()) {
|
|
strat = std::make_unique<waybar::modules::image::MultipleImageStrategy>(id, cfg, module, evbox);
|
|
} else {
|
|
strat = std::make_unique<waybar::modules::image::SingleImageStrategy>(id, cfg, module, evbox,
|
|
hasTooltip);
|
|
}
|
|
|
|
return strat;
|
|
}
|
|
|
|
void waybar::modules::Image::delayWorker() {
|
|
thread_ = [this] {
|
|
dp.emit();
|
|
thread_.sleep_for(interval_);
|
|
};
|
|
}
|
|
|
|
void waybar::modules::Image::refresh(int sig) {
|
|
#ifdef SIGRTMIN
|
|
if (config_["signal"].isInt() && sig == SIGRTMIN + config_["signal"].asInt()) {
|
|
thread_.wake_up();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
auto waybar::modules::Image::update() -> void {
|
|
strategy_->update();
|
|
|
|
AModule::update();
|
|
}
|
|
|
|
namespace waybar::modules::image {
|
|
|
|
MultipleImageStrategy::MultipleImageStrategy(const std::string& id, const Json::Value& config,
|
|
const std::string& module, Gtk::EventBox& evbox)
|
|
: IStrategy(), box_(Gtk::ORIENTATION_HORIZONTAL, 0) {
|
|
config_ = config;
|
|
|
|
box_.set_name("image");
|
|
box_.get_style_context()->add_class(id);
|
|
box_.get_style_context()->add_class(module);
|
|
evbox.add(box_);
|
|
|
|
size_ = config["size"].asInt();
|
|
if (size_ == 0) {
|
|
size_ = 16;
|
|
}
|
|
}
|
|
|
|
void MultipleImageStrategy::update() {
|
|
// spdlog::info("update function run");
|
|
|
|
// clear box_, previous css classes and memory
|
|
if (box_.get_children().size() > 0) {
|
|
resetBoxAndMemory();
|
|
}
|
|
|
|
// set new images from config script
|
|
if (!config_["entries"].empty()) {
|
|
setImagesData(config_["entries"]);
|
|
} else if (!config_["exec"].empty()) {
|
|
auto exec = util::command::exec(config_["exec"].asString(), "");
|
|
Json::Value as_json;
|
|
Json::Reader reader;
|
|
|
|
if (!reader.parse(exec.out, as_json)) {
|
|
spdlog::error("invalid json from exec {}", exec.out);
|
|
return;
|
|
}
|
|
|
|
setImagesData(as_json);
|
|
} else {
|
|
spdlog::error("no image files provded in config");
|
|
return;
|
|
}
|
|
|
|
setupAndDraw();
|
|
}
|
|
|
|
void MultipleImageStrategy::setupAndDraw() {
|
|
for (unsigned int i = 0; i < images_data_.size(); i++) {
|
|
images_data_[i].img = std::make_shared<Gtk::Image>();
|
|
images_data_[i].btn = std::make_shared<Gtk::Button>();
|
|
|
|
auto img = images_data_[i].img;
|
|
auto data = images_data_[i];
|
|
|
|
auto path = data.path;
|
|
auto marker = data.marker;
|
|
auto tooltip = data.tooltip;
|
|
bool has_onclick = !data.on_click.empty();
|
|
|
|
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
|
pixbuf = Gdk::Pixbuf::create_from_file(path, size_, size_);
|
|
|
|
if (has_onclick) {
|
|
auto btn = images_data_[i].btn;
|
|
btn->set_name("button_" + path);
|
|
btn->get_style_context()->add_class(marker);
|
|
btn->set_tooltip_text(tooltip);
|
|
btn->set_image(*img);
|
|
box_.pack_start(*btn);
|
|
|
|
btn->add_events(Gdk::BUTTON_PRESS_MASK);
|
|
btn->signal_clicked().connect(
|
|
sigc::bind(sigc::mem_fun(*this, &MultipleImageStrategy::handleClick), data.on_click));
|
|
|
|
if (pixbuf) {
|
|
btn->show_all();
|
|
img->set(pixbuf);
|
|
box_.get_style_context()->remove_class("empty");
|
|
} else {
|
|
btn->hide();
|
|
img->clear();
|
|
img->hide();
|
|
box_.get_style_context()->add_class("empty");
|
|
}
|
|
} else {
|
|
img->set_name(path);
|
|
img->get_style_context()->add_class(marker);
|
|
img->set_tooltip_text(tooltip);
|
|
box_.pack_start(*img);
|
|
// spdlog::info("added image -> {}:{}", marker, path);
|
|
|
|
if (pixbuf) {
|
|
img->set(pixbuf);
|
|
img->show();
|
|
box_.get_style_context()->remove_class("empty");
|
|
} else {
|
|
img->clear();
|
|
img->hide();
|
|
box_.get_style_context()->add_class("empty");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MultipleImageStrategy::setImagesData(const Json::Value& entries) {
|
|
for (unsigned int i = 0; i < entries.size(); i++) {
|
|
auto path = entries[i]["path"];
|
|
auto marker = entries[i]["marker"];
|
|
auto tooltip = entries[i]["tooltip"];
|
|
auto onclick = entries[i]["on-click"];
|
|
|
|
bool has_tooltip_err = !tooltip.empty() && !tooltip.isString();
|
|
bool has_onclick_err = !onclick.empty() && !onclick.isString();
|
|
|
|
if (!path.isString() || !marker.isString() || has_tooltip_err || has_onclick_err ||
|
|
!Glib::file_test(path.asString(), Glib::FILE_TEST_EXISTS)) {
|
|
spdlog::error("invalid input in images config -> {}", entries[i]);
|
|
return;
|
|
}
|
|
ImageData data;
|
|
data.path = path.asString();
|
|
data.marker = marker.asString();
|
|
data.tooltip = !tooltip.empty() ? tooltip.asString() : "";
|
|
data.on_click = onclick.asString();
|
|
|
|
images_data_.push_back(data);
|
|
}
|
|
}
|
|
|
|
void MultipleImageStrategy::resetBoxAndMemory() {
|
|
auto children = box_.get_children();
|
|
for (auto child : children) {
|
|
box_.remove(*child);
|
|
// spdlog::info("child removed with name -> {}", std::string(child->get_name()));
|
|
}
|
|
|
|
images_data_.clear();
|
|
}
|
|
|
|
void MultipleImageStrategy::handleClick(const Glib::ustring& data) {
|
|
auto msg = std::string(data);
|
|
|
|
auto exec = util::command::exec(data, "");
|
|
}
|
|
|
|
SingleImageStrategy::SingleImageStrategy(const std::string& id, const Json::Value& config,
|
|
const std::string& module, Gtk::EventBox& evbox,
|
|
bool tooltipEnabled)
|
|
: IStrategy(), box_(Gtk::ORIENTATION_HORIZONTAL, 0) {
|
|
config_ = config;
|
|
hasTooltip_ = tooltipEnabled;
|
|
|
|
box_.pack_start(image_);
|
|
box_.set_name("image");
|
|
if (!id.empty()) {
|
|
box_.get_style_context()->add_class(id);
|
|
}
|
|
box_.get_style_context()->add_class(module);
|
|
evbox.add(box_);
|
|
|
|
size_ = config["size"].asInt();
|
|
if (size_ == 0) {
|
|
size_ = 16;
|
|
}
|
|
}
|
|
|
|
void SingleImageStrategy::update() {
|
|
if (config_["path"].isString()) {
|
|
auto result = Config::tryExpandPath(config_["path"].asString(), "");
|
|
path_ = result.empty() ? "" : result.front();
|
|
} else if (config_["exec"].isString()) {
|
|
output_ = util::command::exec(config_["exec"].asString(), "");
|
|
parseOutputRaw();
|
|
// expand path if "~" or "$HOME" is present in original path
|
|
auto result = Config::tryExpandPath(path_, "");
|
|
path_ = result.empty() ? "" : result.front();
|
|
}
|
|
|
|
if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS)) {
|
|
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
|
|
|
int scaled_icon_size = size_ * image_.get_scale_factor();
|
|
pixbuf = Gdk::Pixbuf::create_from_file(path_, scaled_icon_size, scaled_icon_size);
|
|
|
|
auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(),
|
|
image_.get_window());
|
|
image_.set(surface);
|
|
image_.show();
|
|
|
|
if (hasTooltip_ && !tooltip_.empty()) {
|
|
if (box_.get_tooltip_markup() != tooltip_) {
|
|
box_.set_tooltip_markup(tooltip_);
|
|
}
|
|
}
|
|
|
|
box_.get_style_context()->remove_class("empty");
|
|
} else {
|
|
image_.clear();
|
|
image_.hide();
|
|
box_.get_style_context()->add_class("empty");
|
|
}
|
|
}
|
|
|
|
void SingleImageStrategy::parseOutputRaw() {
|
|
std::istringstream output(output_.out);
|
|
std::string line;
|
|
int i = 0;
|
|
while (getline(output, line)) {
|
|
if (i == 0) {
|
|
path_ = line;
|
|
} else if (i == 1) {
|
|
tooltip_ = line;
|
|
} else {
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
} // namespace waybar::modules::image
|