From cbfbd9712a7218b6d39b4e9d93d0941eb0572783 Mon Sep 17 00:00:00 2001 From: EvilLary Date: Tue, 6 Jan 2026 16:29:17 +0300 Subject: [PATCH] anr: open anr dialog on parent's workspace (#12509) --- hyprtester/src/tests/main/misc.cpp | 115 +++++++++++++++++++++++++++++ src/helpers/AsyncDialogBox.cpp | 11 +++ src/helpers/AsyncDialogBox.hpp | 4 +- src/managers/ANRManager.cpp | 32 ++++---- 4 files changed, 147 insertions(+), 15 deletions(-) diff --git a/hyprtester/src/tests/main/misc.cpp b/hyprtester/src/tests/main/misc.cpp index 3187694ba..471eef7a5 100644 --- a/hyprtester/src/tests/main/misc.cpp +++ b/hyprtester/src/tests/main/misc.cpp @@ -18,6 +18,121 @@ using namespace Hyprutils::Memory; #define UP CUniquePointer #define SP CSharedPointer +// Uncomment once test vm can run hyprland-dialog +// static void testAnrDialogs() { +// NLog::log("{}Testing ANR dialogs", Colors::YELLOW); +// +// OK(getFromSocket("/keyword misc:enable_anr_dialog true")); +// OK(getFromSocket("/keyword misc:anr_missed_pings 1")); +// +// NLog::log("{}ANR dialog: regular workspaces", Colors::YELLOW); +// { +// OK(getFromSocket("/dispatch workspace 2")); +// +// auto kitty = Tests::spawnKitty("bad_kitty"); +// +// if (!kitty) { +// ret = 1; +// return; +// } +// +// { +// auto str = getFromSocket("/activewindow"); +// EXPECT_CONTAINS(str, "workspace: 2"); +// } +// +// OK(getFromSocket("/dispatch workspace 1")); +// +// ::kill(kitty->pid(), SIGSTOP); +// Tests::waitUntilWindowsN(2); +// +// { +// auto str = getFromSocket("/activeworkspace"); +// EXPECT_CONTAINS(str, "windows: 0"); +// } +// +// { +// OK(getFromSocket("/dispatch focuswindow class:hyprland-dialog")) +// auto str = getFromSocket("/activewindow"); +// EXPECT_CONTAINS(str, "workspace: 2"); +// } +// } +// +// Tests::killAllWindows(); +// +// NLog::log("{}ANR dialog: named workspaces", Colors::YELLOW); +// { +// OK(getFromSocket("/dispatch workspace name:yummy")); +// +// auto kitty = Tests::spawnKitty("bad_kitty"); +// +// if (!kitty) { +// ret = 1; +// return; +// } +// +// { +// auto str = getFromSocket("/activewindow"); +// EXPECT_CONTAINS(str, "yummy"); +// } +// +// OK(getFromSocket("/dispatch workspace 1")); +// +// ::kill(kitty->pid(), SIGSTOP); +// Tests::waitUntilWindowsN(2); +// +// { +// auto str = getFromSocket("/activeworkspace"); +// EXPECT_CONTAINS(str, "windows: 0"); +// } +// +// { +// OK(getFromSocket("/dispatch focuswindow class:hyprland-dialog")) +// auto str = getFromSocket("/activewindow"); +// EXPECT_CONTAINS(str, "yummy"); +// } +// } +// +// Tests::killAllWindows(); +// +// NLog::log("{}ANR dialog: special workspaces", Colors::YELLOW); +// { +// OK(getFromSocket("/dispatch workspace special:apple")); +// +// auto kitty = Tests::spawnKitty("bad_kitty"); +// +// if (!kitty) { +// ret = 1; +// return; +// } +// +// { +// auto str = getFromSocket("/activewindow"); +// EXPECT_CONTAINS(str, "special:apple"); +// } +// +// OK(getFromSocket("/dispatch togglespecialworkspace apple")); +// OK(getFromSocket("/dispatch workspace 1")); +// +// ::kill(kitty->pid(), SIGSTOP); +// Tests::waitUntilWindowsN(2); +// +// { +// auto str = getFromSocket("/activeworkspace"); +// EXPECT_CONTAINS(str, "windows: 0"); +// } +// +// { +// OK(getFromSocket("/dispatch focuswindow class:hyprland-dialog")) +// auto str = getFromSocket("/activewindow"); +// EXPECT_CONTAINS(str, "special:apple"); +// } +// } +// +// OK(getFromSocket("/reload")); +// Tests::killAllWindows(); +// } + static bool test() { NLog::log("{}Testing config: misc:", Colors::GREEN); diff --git a/src/helpers/AsyncDialogBox.cpp b/src/helpers/AsyncDialogBox.cpp index 4cef42523..8c2c7cd70 100644 --- a/src/helpers/AsyncDialogBox.cpp +++ b/src/helpers/AsyncDialogBox.cpp @@ -4,6 +4,8 @@ #include #include #include "../managers/eventLoop/EventLoopManager.hpp" +#include "../desktop/rule/windowRule/WindowRule.hpp" +#include "../desktop/rule/Engine.hpp" using namespace Hyprutils::OS; @@ -119,6 +121,9 @@ SP> CAsyncDialogBox::open() { m_selfReference = m_selfWeakReference.lock(); + if (!m_execRuleToken.empty()) + proc.addEnv(Desktop::Rule::EXEC_RULE_ENV_NAME, m_execRuleToken); + if (!proc.runAsync()) { Log::logger->log(Log::ERR, "CAsyncDialogBox::open: failed to run async"); wl_event_source_remove(m_readEventSource); @@ -154,3 +159,9 @@ pid_t CAsyncDialogBox::getPID() const { SP CAsyncDialogBox::lockSelf() { return m_selfWeakReference.lock(); } + +void CAsyncDialogBox::setExecRule(std::string&& s) { + auto rule = Desktop::Rule::CWindowRule::buildFromExecString(std::move(s)); + m_execRuleToken = rule->execToken(); + Desktop::Rule::ruleEngine()->registerRule(std::move(rule)); +} diff --git a/src/helpers/AsyncDialogBox.hpp b/src/helpers/AsyncDialogBox.hpp index 8db516cea..1bdeba14d 100644 --- a/src/helpers/AsyncDialogBox.hpp +++ b/src/helpers/AsyncDialogBox.hpp @@ -27,6 +27,7 @@ class CAsyncDialogBox { void kill(); bool isRunning() const; pid_t getPID() const; + void setExecRule(std::string&& s); SP lockSelf(); @@ -41,7 +42,8 @@ class CAsyncDialogBox { pid_t m_dialogPid = 0; wl_event_source* m_readEventSource = nullptr; Hyprutils::OS::CFileDescriptor m_pipeReadFd; - std::string m_stdout = ""; + std::string m_stdout = ""; + std::string m_execRuleToken = ""; const std::string m_title; const std::string m_description; diff --git a/src/managers/ANRManager.cpp b/src/managers/ANRManager.cpp index a9cff74f9..c36527945 100644 --- a/src/managers/ANRManager.cpp +++ b/src/managers/ANRManager.cpp @@ -188,21 +188,25 @@ void CANRManager::SANRData::runDialog(const std::string& appName, const std::str const auto OPTION_TERMINATE_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_OPTION_TERMINATE, {}); const auto OPTION_WAIT_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_OPTION_WAIT, {}); + const auto OPTIONS = std::vector{OPTION_TERMINATE_STR, OPTION_WAIT_STR}; + const auto CLASS_STR = appClass.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appClass; + const auto TITLE_STR = appName.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appName; + const auto DESCRIPTION_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_CONTENT, {{"title", TITLE_STR}, {"class", CLASS_STR}}); - dialogBox = - CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_TITLE, {}), - I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_CONTENT, - { - // - {"class", appClass.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appClass}, // - {"title", appName.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appName} // - }), - std::vector{ - // - OPTION_TERMINATE_STR, // - OPTION_WAIT_STR // - } // - ); + dialogBox = CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_TITLE, {}), DESCRIPTION_STR, OPTIONS); + + for (const auto& w : g_pCompositor->m_windows) { + if (!w->m_isMapped) + continue; + + if (!fitsWindow(w)) + continue; + + if (w->m_workspace) + dialogBox->setExecRule(std::format("workspace {} silent", w->m_workspace->getConfigName())); + + break; + } dialogBox->open()->then([dialogWmPID, this, OPTION_TERMINATE_STR, OPTION_WAIT_STR](SP> r) { if (r->hasError()) {