From 74cf45d53017da6ab9ccf54b6740d488d2bfa977 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 5 Jul 2026 10:11:59 +0200 Subject: [PATCH] fix(hyprland): detect Lua protocol without side-effecting dispatch isLuaProtocol() probed the protocol by sending a real "dispatch workspace __waybar_probe__". On Hyprland < 0.54 "workspace" is a valid dispatcher, so the probe actually switched the user to a junk workspace named __waybar_probe__ on the first workspace click/scroll. Detect the protocol with the read-only "version" IPC query instead: parse the numeric "version" field (falling back to the always-present "tag" field) and treat Hyprland >= 0.54 as Lua. This has no side effects. On any parse/query failure we log and fall back to the legacy protocol, preserving prior behavior for older versions. --- src/modules/hyprland/backend.cpp | 39 ++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index f38f8216..891bed1a 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -297,11 +297,40 @@ bool IPC::isLuaProtocol() { return *s_luaProtocolDetected_; } - // Probe: send a harmless old-style dispatch and check the error. - // In Lua-based Hyprland (>= 0.54) the error contains "hl.dispatch". - // In older versions it returns "ok" or a different error. - auto reply = getSocket1Reply("dispatch workspace __waybar_probe__"); - bool luaProto = reply.find("hl.dispatch") != std::string::npos; + // Detect the Lua-based dispatch protocol (Hyprland >= 0.54) via the read-only + // "version" query. This MUST have no side effects: an earlier probe issued a real + // "dispatch workspace __waybar_probe__", which on Hyprland < 0.54 actually switched + // the user to a junk workspace named __waybar_probe__ on the first click/scroll. + bool luaProto = false; + try { + util::JsonParser parser; + const Json::Value ver = parser.parse(getSocket1Reply("j/version")); + + // Prefer the numeric "version" field ("0.54.0"); fall back to the "tag" field + // ("v0.54.0" or "v0.54.0-16-gdeadbee"), which is present on all releases. + std::string versionStr = ver["version"].asString(); + if (versionStr.empty()) { + versionStr = ver["tag"].asString(); + } + + const size_t firstDigit = versionStr.find_first_of("0123456789"); + if (firstDigit != std::string::npos) { + // std::stoi parses the leading integer and stops at the first non-digit, so it + // tolerates the trailing ".patch-commits-ghash" suffix on the tag. + const int major = std::stoi(versionStr.substr(firstDigit)); + int minor = 0; + const size_t dot = versionStr.find('.', firstDigit); + if (dot != std::string::npos && dot + 1 < versionStr.size()) { + minor = std::stoi(versionStr.substr(dot + 1)); + } + luaProto = major > 0 || (major == 0 && minor >= 54); + } else { + spdlog::warn("Hyprland IPC: could not parse version '{}', assuming legacy protocol", + versionStr); + } + } catch (const std::exception& e) { + spdlog::warn("Hyprland IPC: version detection failed ({}), assuming legacy protocol", e.what()); + } if (luaProto) { spdlog::info("Hyprland IPC: detected Lua-based dispatch protocol (Hyprland >= 0.54)");