From eb86905e678b9b829523cff3d8a9c0ca28eb5bd3 Mon Sep 17 00:00:00 2001 From: Florian Egger Date: Tue, 8 Sep 2026 20:51:24 +0200 Subject: [PATCH] fix(app): build waybar output with nlohmann/json, not format strings The hand-rolled std::format strings embedded literal newline and Unicode characters via C++ universal character name escapes (\u23f8, \u2014, \u25b6, \n), which the compiler converts to actual control characters in the output. Raw newlines inside JSON strings are invalid, so waybar's parser failed and displayed the raw JSON text instead of the widget. Build the status line with nlohmann::json, which escapes everything correctly. --- src/app/main.cpp | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/app/main.cpp b/src/app/main.cpp index 1cc5680..1088e71 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -7,6 +7,8 @@ #include "screencast/network/discovery.h" #include "screencast/network/signaling.h" +#include + #include #include @@ -205,10 +207,19 @@ void spawn_detached_sender(const std::string& peer, int bitrate_kbps) { void print_waybar_status() { const auto state = sc::read_sender_state(); + + // Built with nlohmann::json so all escaping (newlines in tooltips, + // non-ASCII characters) is handled correctly. Hand-rolled format + // strings produced literal control characters that broke waybar's + // JSON parser. + nlohmann::json json = nlohmann::json::object(); + if (!state.has_value()) { - std::cout << std::format("{{\"text\":\"\u23f8\",\"alt\":\"idle\",\"class\":\"idle\"," - "\"tooltip\":\"screencast idle \u2014 click to stream to the last receiver\n" - "right-click: open the panel\"}}\n"); + json["text"] = "\u23f8"; // pause symbol + json["alt"] = "idle"; + json["class"] = "idle"; + json["tooltip"] = "screencast idle \u2014 click to stream to the last receiver\nright-click: open the panel"; + std::cout << json.dump() << '\n'; return; } @@ -218,13 +229,17 @@ void print_waybar_status() { state->started_epoch_ms; const std::int64_t minutes = elapsed_ms / 60000; const std::int64_t seconds = (elapsed_ms / 1000) % 60; - std::cout << std::format("{{\"text\":\"\u25b6\",\"alt\":\"streaming\",\"class\":\"streaming\"," - "\"tooltip\":\"screencast \u2192 {}\\n{} kbps \u00b7 {}m {:02}s\\nsession {}\"}}\n", - state->receiver, - state->bitrate_kbps, - minutes, - seconds, - state->session_id); + + json["text"] = "\u25b6"; // play symbol + json["alt"] = "streaming"; + json["class"] = "streaming"; + json["tooltip"] = std::format("screencast \u2192 {}\n{} kbps \u00b7 {}m {:02}s\nsession {}", + state->receiver, + state->bitrate_kbps, + minutes, + seconds, + state->session_id); + std::cout << json.dump() << '\n'; } int run_waybar(const sc::WaybarCommand& command) {