Files
Waybar/include/modules/image.hpp
T
Alex 3f77c07875 fix(image): don't block the main loop with exec in the multiple-image path
MultipleImageStrategy::update() and handleClick() ran util::command::exec
(a blocking fork+exec+read) on the GTK main thread, so the whole bar froze
for the script's duration on every interval and on every click.

Move the exec into a new IStrategy::fetch() hook that the SleeperThread
worker runs before dp.emit(); update() now only parses the cached output
and draws on the main thread (mirroring how custom.cpp separates exec from
formatting). handleClick() uses forkExec() so clicks fire-and-forget instead
of blocking on the command's output. The entries and single-image paths are
unchanged.
2026-07-05 10:20:41 +02:00

99 lines
2.4 KiB
C++

#pragma once
#include <fmt/format.h>
#include <gtkmm/image.h>
#include <csignal>
#include <string>
#include "ALabel.hpp"
#include "gtkmm/box.h"
#include "util/command.hpp"
#include "util/json.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules {
namespace image {
class IStrategy {
public:
virtual ~IStrategy() = default;
// Runs on the worker thread before update(). Use it for blocking work (e.g.
// spawning a user script) so the GTK main loop isn't stalled. Default no-op.
virtual void fetch() {}
virtual void update() = 0;
};
class SingleImageStrategy : public IStrategy {
public:
SingleImageStrategy(const std::string&, const Json::Value&, const std::string&, Gtk::EventBox&,
bool);
~SingleImageStrategy() override = default;
void update() override;
private:
void parseOutputRaw();
util::command::res output_;
Json::Value config_;
Gtk::Image image_;
std::string path_;
std::string tooltip_;
int size_;
Gtk::Box box_;
bool hasTooltip_;
};
class MultipleImageStrategy : public IStrategy {
public:
MultipleImageStrategy(const std::string&, const Json::Value&, const std::string&, Gtk::EventBox&);
~MultipleImageStrategy() override = default;
void fetch() override;
void update() override;
private:
struct ImageData {
std::string path;
std::string marker;
std::string tooltip;
std::string on_click;
std::shared_ptr<Gtk::Image> img;
std::shared_ptr<Gtk::Button> btn;
};
void setImagesData(const Json::Value&);
void setupAndDraw();
void resetBoxAndMemory();
void handleClick(const Glib::ustring& data);
Json::Value config_;
int size_;
Gtk::Box box_;
std::vector<ImageData> images_data_;
// stdout captured by fetch() on the worker thread and consumed by update()
std::string exec_output_;
};
} // namespace image
class Image : public AModule {
public:
Image(const std::string&, const Json::Value&);
virtual ~Image() = default;
auto update() -> void override;
void refresh(int /*signal*/) override;
private:
void delayWorker();
void handleEvent();
static std::unique_ptr<image::IStrategy> getStrategy(const std::string&, const Json::Value&,
const std::string&, Gtk::EventBox&, bool);
std::chrono::milliseconds interval_;
std::unique_ptr<image::IStrategy> strategy_;
util::SleeperThread thread_;
};
} // namespace waybar::modules