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.
This commit is contained in:
@@ -19,6 +19,9 @@ namespace image {
|
|||||||
class IStrategy {
|
class IStrategy {
|
||||||
public:
|
public:
|
||||||
virtual ~IStrategy() = default;
|
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;
|
virtual void update() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,6 +49,7 @@ class MultipleImageStrategy : public IStrategy {
|
|||||||
public:
|
public:
|
||||||
MultipleImageStrategy(const std::string&, const Json::Value&, const std::string&, Gtk::EventBox&);
|
MultipleImageStrategy(const std::string&, const Json::Value&, const std::string&, Gtk::EventBox&);
|
||||||
~MultipleImageStrategy() override = default;
|
~MultipleImageStrategy() override = default;
|
||||||
|
void fetch() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -67,6 +71,8 @@ class MultipleImageStrategy : public IStrategy {
|
|||||||
int size_;
|
int size_;
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
std::vector<ImageData> images_data_;
|
std::vector<ImageData> images_data_;
|
||||||
|
// stdout captured by fetch() on the worker thread and consumed by update()
|
||||||
|
std::string exec_output_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace image
|
} // namespace image
|
||||||
|
|||||||
+17
-6
@@ -45,6 +45,9 @@ auto waybar::modules::Image::getStrategy(const std::string& id, const Json::Valu
|
|||||||
|
|
||||||
void waybar::modules::Image::delayWorker() {
|
void waybar::modules::Image::delayWorker() {
|
||||||
thread_ = [this] {
|
thread_ = [this] {
|
||||||
|
// Do the blocking work (e.g. running a user script) here on the worker
|
||||||
|
// thread; update() then only parses the result and draws on the main thread.
|
||||||
|
strategy_->fetch();
|
||||||
dp.emit();
|
dp.emit();
|
||||||
thread_.sleep_for(interval_);
|
thread_.sleep_for(interval_);
|
||||||
};
|
};
|
||||||
@@ -82,6 +85,15 @@ MultipleImageStrategy::MultipleImageStrategy(const std::string& id, const Json::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultipleImageStrategy::fetch() {
|
||||||
|
// Run the (blocking) user script off the GTK main thread so the bar doesn't
|
||||||
|
// freeze for the script's duration on every interval. update() consumes the
|
||||||
|
// captured output. The static "entries" path takes priority and needs no exec.
|
||||||
|
if (config_["entries"].empty() && !config_["exec"].empty()) {
|
||||||
|
exec_output_ = util::command::exec(config_["exec"].asString(), "").out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MultipleImageStrategy::update() {
|
void MultipleImageStrategy::update() {
|
||||||
// spdlog::info("update function run");
|
// spdlog::info("update function run");
|
||||||
|
|
||||||
@@ -94,12 +106,12 @@ void MultipleImageStrategy::update() {
|
|||||||
if (!config_["entries"].empty()) {
|
if (!config_["entries"].empty()) {
|
||||||
setImagesData(config_["entries"]);
|
setImagesData(config_["entries"]);
|
||||||
} else if (!config_["exec"].empty()) {
|
} else if (!config_["exec"].empty()) {
|
||||||
auto exec = util::command::exec(config_["exec"].asString(), "");
|
// exec output was captured by fetch() on the worker thread
|
||||||
Json::Value as_json;
|
Json::Value as_json;
|
||||||
Json::Reader reader;
|
Json::Reader reader;
|
||||||
|
|
||||||
if (!reader.parse(exec.out, as_json)) {
|
if (!reader.parse(exec_output_, as_json)) {
|
||||||
spdlog::error("invalid json from exec {}", exec.out);
|
spdlog::error("invalid json from exec {}", exec_output_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,9 +223,8 @@ void MultipleImageStrategy::resetBoxAndMemory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MultipleImageStrategy::handleClick(const Glib::ustring& data) {
|
void MultipleImageStrategy::handleClick(const Glib::ustring& data) {
|
||||||
auto msg = std::string(data);
|
// Fire-and-forget: don't block the main loop waiting on the command's output.
|
||||||
|
util::command::forkExec(data);
|
||||||
auto exec = util::command::exec(data, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SingleImageStrategy::SingleImageStrategy(const std::string& id, const Json::Value& config,
|
SingleImageStrategy::SingleImageStrategy(const std::string& id, const Json::Value& config,
|
||||||
|
|||||||
Reference in New Issue
Block a user