From f48fce57dc6f40d8b3992041a715776fe6072dd1 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 6 Mar 2026 18:38:21 -0600 Subject: [PATCH 1/2] fix(menu): keep popup menus alive after builder teardown The popup menu was retrieved from GtkBuilder and stored in menu_, but the builder was unref'd immediately after construction. That left the later popup path operating on a builder-owned GtkMenu whose lifetime was no longer guaranteed, which matches the GTK_IS_WIDGET and GTK_IS_MENU assertions from the regression report. Take an owned reference to the built menu and release it in AModule teardown so popup menus stay valid without extending the lifetime of the whole builder. Signed-off-by: Austin Horstman --- src/ALabel.cpp | 2 ++ src/AModule.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/ALabel.cpp b/src/ALabel.cpp index fe87e098..795f87f1 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -100,6 +100,8 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st g_object_unref(builder); throw std::runtime_error("Failed to get 'menu' object from GtkBuilder"); } + // Keep the menu alive after dropping the transient GtkBuilder. + g_object_ref(menu_); submenus_ = std::map(); menuActionsMap_ = std::map(); diff --git a/src/AModule.cpp b/src/AModule.cpp index 5f3a187a..a5ba69d3 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -88,6 +88,10 @@ AModule::~AModule() { killpg(pid, SIGTERM); } } + if (menu_ != nullptr) { + g_object_unref(menu_); + menu_ = nullptr; + } } auto AModule::update() -> void { From 790101f824513a6b48396fd956dd3a9dfae5ad4b Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 6 Mar 2026 18:49:02 -0600 Subject: [PATCH 2/2] chore: format Signed-off-by: Austin Horstman --- include/modules/sway/ipc/client.hpp | 2 +- src/modules/custom.cpp | 1 + src/modules/hyprland/window.cpp | 17 ++++++++++------- src/modules/niri/backend.cpp | 2 +- test/utils/command.cpp | 5 +++-- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/include/modules/sway/ipc/client.hpp b/include/modules/sway/ipc/client.hpp index eb0f32f9..f6eb7c40 100644 --- a/include/modules/sway/ipc/client.hpp +++ b/include/modules/sway/ipc/client.hpp @@ -13,8 +13,8 @@ #include "ipc.hpp" #include "util/SafeSignal.hpp" -#include "util/sleeper_thread.hpp" #include "util/scoped_fd.hpp" +#include "util/sleeper_thread.hpp" namespace waybar::modules::sway { diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index 93956614..28def8c9 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -1,6 +1,7 @@ #include "modules/custom.hpp" #include + #include #include "util/scope_guard.hpp" diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 97d49eaf..a67e81ac 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -189,8 +189,9 @@ void Window::queryActiveWorkspace() { if (workspace_.windows > 0) { const auto clients = m_ipc.getSocket1JsonReply("clients"); if (clients.isArray()) { - auto activeWindow = std::ranges::find_if( - clients, [&](const Json::Value& window) { return window["address"] == workspace_.last_window; }); + auto activeWindow = std::ranges::find_if(clients, [&](const Json::Value& window) { + return window["address"] == workspace_.last_window; + }); if (activeWindow == std::end(clients)) { focused_ = false; @@ -200,17 +201,19 @@ void Window::queryActiveWorkspace() { windowData_ = WindowData::parse(*activeWindow); updateAppIconName(windowData_.class_name, windowData_.initial_class_name); std::vector workspaceWindows; - std::ranges::copy_if(clients, std::back_inserter(workspaceWindows), [&](const Json::Value& window) { - return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); - }); + std::ranges::copy_if( + clients, std::back_inserter(workspaceWindows), [&](const Json::Value& window) { + return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool(); + }); swallowing_ = std::ranges::any_of(workspaceWindows, [&](const Json::Value& window) { return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0"; }); std::vector visibleWindows; std::ranges::copy_if(workspaceWindows, std::back_inserter(visibleWindows), [&](const Json::Value& window) { return !window["hidden"].asBool(); }); - solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(), - [&](const Json::Value& window) { return !window["floating"].asBool(); }); + solo_ = 1 == std::count_if( + visibleWindows.begin(), visibleWindows.end(), + [&](const Json::Value& window) { return !window["floating"].asBool(); }); allFloating_ = std::ranges::all_of( visibleWindows, [&](const Json::Value& window) { return window["floating"].asBool(); }); fullscreen_ = windowData_.fullscreen; diff --git a/src/modules/niri/backend.cpp b/src/modules/niri/backend.cpp index c23aaa47..de3f8a9a 100644 --- a/src/modules/niri/backend.cpp +++ b/src/modules/niri/backend.cpp @@ -13,11 +13,11 @@ #include #include -#include "util/scoped_fd.hpp" #include "giomm/datainputstream.h" #include "giomm/dataoutputstream.h" #include "giomm/unixinputstream.h" #include "giomm/unixoutputstream.h" +#include "util/scoped_fd.hpp" namespace waybar::modules::niri { diff --git a/test/utils/command.cpp b/test/utils/command.cpp index 2ccb3383..053a2b77 100644 --- a/test/utils/command.cpp +++ b/test/utils/command.cpp @@ -4,11 +4,12 @@ #include #endif +#include +#include + #include #include #include -#include -#include std::mutex reap_mtx; std::list reap;