summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvilLary <EvilLary@proton.me>2026-01-06 16:29:17 +0300
committerGitHub <noreply@github.com>2026-01-06 14:29:17 +0100
commitcbfbd9712a7218b6d39b4e9d93d0941eb0572783 (patch)
treeadc1e519d80c8a1d9f1c11d7e6e122c4900d380d
parent9817553c664b0b7f6776671383a6368c74ee8dee (diff)
anr: open anr dialog on parent's workspace (#12509)
-rw-r--r--hyprtester/src/tests/main/misc.cpp115
-rw-r--r--src/helpers/AsyncDialogBox.cpp11
-rw-r--r--src/helpers/AsyncDialogBox.hpp4
-rw-r--r--src/managers/ANRManager.cpp32
4 files changed, 147 insertions, 15 deletions
diff --git a/hyprtester/src/tests/main/misc.cpp b/hyprtester/src/tests/main/misc.cpp
index 3187694b..471eef7a 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 4cef4252..8c2c7cd7 100644
--- a/src/helpers/AsyncDialogBox.cpp
+++ b/src/helpers/AsyncDialogBox.cpp
@@ -4,6 +4,8 @@
#include <algorithm>
#include <unistd.h>
#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<CPromise<std::string>> 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> 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 8db516ce..1bdeba14 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<CAsyncDialogBox> 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 a9cff74f..c3652794 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<std::string>{
- //
- 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<CPromiseResult<std::string>> r) {
if (r->hasError()) {