fix(AModule): don't crash on click/scroll commands with literal braces

handleUserEvent ran the configured command through fmt::format(fmt::runtime(...))
to substitute {x}/{y}. Commands containing literal braces that aren't {x}/{y}
(e.g. `echo ${HOME}`, `awk '{print $1}'`, brace expansions) made libfmt throw
fmt::format_error. Uncaught inside a GTK signal handler this aborts the whole bar.

Only format when {x}/{y} is present and fall back to the raw command on failure.

Fixes bar abort/std::terminate on on-click/on-scroll commands containing braces.
This commit is contained in:
Alex
2026-07-05 10:13:24 +02:00
parent fc297a7df3
commit afa6ab1fd8
+17 -3
View File
@@ -238,9 +238,23 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
if (!format.empty()) {
const int width = gdk_window_get_width(e->window);
const int height = gdk_window_get_height(e->window);
const std::string cmd =
fmt::format(fmt::runtime(format), fmt::arg("x", (int)round(100. * e->x / width)),
fmt::arg("y", (int)round(100. * e->y / height)));
// Substitute {x}/{y} with the click position. The configured command is
// arbitrary user input that may contain literal braces which are not {x}/{y}
// (e.g. `echo ${HOME}`, `awk '{print $1}'`, brace expansions). Those make
// libfmt throw fmt::format_error; since we run inside a GTK signal handler an
// uncaught exception aborts the whole bar. Only format when a placeholder is
// actually present, and fall back to the raw command if formatting throws.
std::string cmd = format;
if (format.find("{x}") != std::string::npos || format.find("{y}") != std::string::npos) {
try {
cmd = fmt::format(fmt::runtime(format), fmt::arg("x", (int)round(100. * e->x / width)),
fmt::arg("y", (int)round(100. * e->y / height)));
} catch (const fmt::format_error& err) {
spdlog::warn("Failed to format command '{}': {}. Running it unformatted.", format,
err.what());
cmd = format;
}
}
pid_children_.push_back(util::command::forkExec(cmd));
}
dp.emit();