From 2a05edaf6aea1509555d5b3f3bc57549287ae98e Mon Sep 17 00:00:00 2001 From: yyyumeniku Date: Tue, 12 May 2026 20:09:13 -0400 Subject: [PATCH] hyprland/workspaces: add debounce timer to prevent workspace button flicker When switching between workspaces rapidly (especially empty ones), Hyprland sends createworkspace and destroyworkspace events slightly out of order. This causes workspace buttons to render in wrong positions for a split second before snapping to their correct spots. Add a 7ms debounce timer that batches workspace events before calling dp.emit(), preventing the visual glitch. Fixes #4376 --- include/modules/hyprland/workspaces.hpp | 3 + src/modules/hyprland/workspaces.cpp | 90 +++++++++++++++---------- 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 03548ccb..be5bc66d 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -211,6 +211,9 @@ class Workspaces : public AModule, public EventHandler { Gtk::Box m_box; sigc::connection m_scrollEventConnection_; IPC& m_ipc; + + sigc::connection m_debounceTimer; + bool m_updatePending = false; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 2496117f..762060e2 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -338,46 +338,62 @@ void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value& c } void Workspaces::onEvent(const std::string& ev) { - std::lock_guard lock(m_mutex); - const auto separator = ev.find(">>"); - if (separator == std::string::npos) { - spdlog::warn("Malformed Hyprland workspace event: {}", ev); - return; - } - std::string eventName = ev.substr(0, separator); - std::string payload = ev.substr(separator + 2); + { + std::lock_guard lock(m_mutex); + const auto separator = ev.find(">>"); + if (separator == std::string::npos) { + spdlog::warn("Malformed Hyprland workspace event: {}", ev); + return; + } + std::string eventName = ev.substr(0, separator); + std::string payload = ev.substr(separator + 2); - if (eventName == "workspacev2") { - onWorkspaceActivated(payload); - } else if (eventName == "activespecial") { - onSpecialWorkspaceActivated(payload); - } else if (eventName == "destroyworkspacev2") { - onWorkspaceDestroyed(payload); - } else if (eventName == "createworkspacev2") { - onWorkspaceCreated(payload); - } else if (eventName == "focusedmonv2") { - onMonitorFocused(payload); - } else if (eventName == "moveworkspacev2") { - onWorkspaceMoved(payload); - } else if (eventName == "openwindow") { - onWindowOpened(payload); - } else if (eventName == "closewindow") { - onWindowClosed(payload); - } else if (eventName == "movewindowv2") { - onWindowMoved(payload); - } else if (eventName == "urgent") { - setUrgentWorkspace(payload); - } else if (eventName == "renameworkspace") { - onWorkspaceRenamed(payload); - } else if (eventName == "windowtitlev2") { - onWindowTitleEvent(payload); - } else if (eventName == "activewindowv2") { - onActiveWindowChanged(payload); - } else if (eventName == "configreloaded") { - onConfigReloaded(); + if (eventName == "workspacev2") { + onWorkspaceActivated(payload); + } else if (eventName == "activespecial") { + onSpecialWorkspaceActivated(payload); + } else if (eventName == "destroyworkspacev2") { + onWorkspaceDestroyed(payload); + } else if (eventName == "createworkspacev2") { + onWorkspaceCreated(payload); + } else if (eventName == "focusedmonv2") { + onMonitorFocused(payload); + } else if (eventName == "moveworkspacev2") { + onWorkspaceMoved(payload); + } else if (eventName == "openwindow") { + onWindowOpened(payload); + } else if (eventName == "closewindow") { + onWindowClosed(payload); + } else if (eventName == "movewindowv2") { + onWindowMoved(payload); + } else if (eventName == "urgent") { + setUrgentWorkspace(payload); + } else if (eventName == "renameworkspace") { + onWorkspaceRenamed(payload); + } else if (eventName == "windowtitlev2") { + onWindowTitleEvent(payload); + } else if (eventName == "activewindowv2") { + onActiveWindowChanged(payload); + } else if (eventName == "configreloaded") { + onConfigReloaded(); + } } - dp.emit(); + if (m_debounceTimer.connected()) { + m_debounceTimer.disconnect(); + m_updatePending = false; + } + + m_updatePending = true; + m_debounceTimer = Glib::signal_timeout().connect([this]() { + if (!m_updatePending) return false; + std::lock_guard lock(m_mutex); + if (m_updatePending) { + dp.emit(); + m_updatePending = false; + } + return false; + }, 7); } void Workspaces::onWorkspaceActivated(std::string const& payload) {