From c9a7cbbdb385521f992d60539daa6a94d403d253 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Mon, 9 Feb 2026 15:26:23 -0600 Subject: [PATCH] fix(image): treat missing interval as once PR #4390 enabled millisecond intervals but changed image interval parsing so a missing interval could resolve to 1ms. That creates a hot update loop and can spike CPU and destabilize rendering in drawer/group setups (issue #4835). Use explicit semantics: missing, null, non-numeric, or <=0 interval is treated as "once" (max sleep), while positive numeric values still support ms precision with a 1ms floor. This keeps the intended feature (sub-second polling when requested) without defaulting to busy looping. Also align waybar-image(5) docs with runtime behavior. Signed-off-by: Austin Horstman --- man/waybar-image.5.scd | 3 ++- src/modules/image.cpp | 27 ++++++++++++++++----------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/man/waybar-image.5.scd b/man/waybar-image.5.scd index 8c991265..8be182a6 100644 --- a/man/waybar-image.5.scd +++ b/man/waybar-image.5.scd @@ -26,7 +26,8 @@ The *image* module displays an image from a path. *interval*: ++ typeof: integer or float ++ The interval (in seconds) to re-render the image. ++ - Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++ + If set to a positive value, the minimum is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++ + Zero or negative values are treated as "once". ++ This is useful if the contents of *path* changes. ++ If no *interval* is defined, the image will only be rendered once. diff --git a/src/modules/image.cpp b/src/modules/image.cpp index 189deee6..98bf3c46 100644 --- a/src/modules/image.cpp +++ b/src/modules/image.cpp @@ -14,22 +14,27 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config) size_ = config["size"].asInt(); - interval_ = config_["interval"] == "once" - ? std::chrono::milliseconds::max() - : std::chrono::milliseconds(std::max( - 1L, // Minimum 1ms due to millisecond precision - static_cast( - (config_["interval"].isNumeric() ? config_["interval"].asDouble() : 0) * - 1000))); + 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(interval_seconds * 1000))); + } + } else { + interval_ = once; + } if (size_ == 0) { size_ = 16; } - if (interval_.count() == 0) { - interval_ = std::chrono::milliseconds::max(); - } - delayWorker(); }