fix(custom): validate JSON output UTF-8 and stop restart-interval:0 busy loop

- parseOutputJson() passed script text/alt/tooltip straight to Pango/GTK; an
  invalid-UTF-8 byte aborted the bar in g_utf8_collate. Validate/make_valid
  like parseOutputRaw already does. Fixes #2829.
- restart-interval:0 was floored to 1ms, respawning the script ~1000x/s and
  starving the main loop; a non-positive restart-interval now stops instead.
  Part of #4842.
This commit is contained in:
Alex
2026-07-04 03:09:20 +02:00
parent b5842d4f53
commit bfe38bd1e2
+23 -10
View File
@@ -105,7 +105,8 @@ void waybar::modules::Custom::continuousWorker() {
dp.emit();
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
}
if (config_["restart-interval"].isNumeric()) {
if (config_["restart-interval"].isNumeric() &&
config_["restart-interval"].asDouble() > 0) {
pid_ = -1;
thread_.sleep_for(std::chrono::milliseconds(
std::max(1L, // Minimum 1ms due to millisecond precision
@@ -115,6 +116,8 @@ void waybar::modules::Custom::continuousWorker() {
throw std::runtime_error("Unable to open " + cmd);
}
} else {
// A non-positive restart-interval must not busy-respawn the script
// (that starves the GTK main loop); treat it as "do not restart".
thread_.stop();
return;
}
@@ -316,22 +319,32 @@ void waybar::modules::Custom::parseOutputJson() {
std::istringstream output(output_.out);
std::string line;
class_.clear();
// A script can emit invalid UTF-8; passing it unchecked to Pango/GTK aborts
// the whole bar in g_utf8_* (see parseOutputRaw, which validates the same way).
auto sanitize = [](const std::string& s) -> Glib::ustring {
Glib::ustring value = s;
if (!value.validate()) {
value = value.make_valid();
}
return value;
};
while (getline(output, line)) {
auto parsed = parser_.parse(line);
if (config_["escape"].isBool() && config_["escape"].asBool()) {
text_ = Glib::Markup::escape_text(parsed["text"].asString());
const bool escape = config_["escape"].isBool() && config_["escape"].asBool();
if (escape) {
text_ = Glib::Markup::escape_text(sanitize(parsed["text"].asString()));
} else {
text_ = parsed["text"].asString();
text_ = sanitize(parsed["text"].asString());
}
if (config_["escape"].isBool() && config_["escape"].asBool()) {
alt_ = Glib::Markup::escape_text(parsed["alt"].asString());
if (escape) {
alt_ = Glib::Markup::escape_text(sanitize(parsed["alt"].asString()));
} else {
alt_ = parsed["alt"].asString();
alt_ = sanitize(parsed["alt"].asString());
}
if (config_["escape"].isBool() && config_["escape"].asBool()) {
tooltip_ = Glib::Markup::escape_text(parsed["tooltip"].asString());
if (escape) {
tooltip_ = Glib::Markup::escape_text(sanitize(parsed["tooltip"].asString()));
} else {
tooltip_ = parsed["tooltip"].asString();
tooltip_ = sanitize(parsed["tooltip"].asString());
}
if (parsed["class"].isString()) {
class_.push_back(parsed["class"].asString());