Skip to content

Commit 28c332e

Browse files
committed
anr: open anr dialog on parent's workspace
1 parent f8d5aad commit 28c332e

File tree

4 files changed

+164
-16
lines changed

4 files changed

+164
-16
lines changed

hyprtester/src/tests/main/misc.cpp

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,122 @@ using namespace Hyprutils::Memory;
1818
#define UP CUniquePointer
1919
#define SP CSharedPointer
2020

21+
static void testAnrDialogs() {
22+
NLog::log("{}Testing anrdialogs", Colors::YELLOW);
23+
24+
OK(getFromSocket("/keyword misc:enable_anr_dialog true"));
25+
OK(getFromSocket("/keyword misc:anr_missed_pings 1"));
26+
27+
NLog::log("{}anrdialogs Should pop up on parent's workspace", Colors::YELLOW);
28+
29+
NLog::log("{}anrdialog: regular workspaces", Colors::YELLOW);
30+
{
31+
OK(getFromSocket("/dispatch workspace 2"));
32+
33+
Tests::spawnKitty("bad_kitty");
34+
35+
int pid = 0;
36+
{
37+
auto str = getFromSocket("/activewindow");
38+
EXPECT_CONTAINS(str, "workspace: 2");
39+
auto pos = str.find("pid") + 4;
40+
auto end = str.find('\n', pos);
41+
pid = std::stoi(str.substr(pos, end));
42+
}
43+
44+
OK(getFromSocket("/dispatch workspace 1"));
45+
46+
::kill(pid, SIGSTOP);
47+
48+
Tests::waitUntilWindowsN(2);
49+
50+
{
51+
auto str = getFromSocket("/activeworkspace");
52+
EXPECT_CONTAINS(str, "windows: 0");
53+
}
54+
55+
{
56+
OK(getFromSocket("/dispatch focuswindow class:hyprland-dialog"))
57+
auto str = getFromSocket("/activewindow");
58+
EXPECT_CONTAINS(str, "workspace: 2");
59+
}
60+
61+
Tests::killAllWindows();
62+
}
63+
64+
NLog::log("{}anrdialog: named workspaces", Colors::YELLOW);
65+
{
66+
67+
OK(getFromSocket("/dispatch workspace name:yummy"));
68+
69+
Tests::spawnKitty("bad_kitty");
70+
71+
int pid = 0;
72+
{
73+
auto str = getFromSocket("/activewindow");
74+
EXPECT_CONTAINS(str, "yummy"); // can't predetermined workspace id
75+
auto pos = str.find("pid") + 4;
76+
auto end = str.find('\n', pos);
77+
pid = std::stoi(str.substr(pos, end));
78+
}
79+
80+
OK(getFromSocket("/dispatch workspace 1"));
81+
82+
::kill(pid, SIGSTOP);
83+
84+
Tests::waitUntilWindowsN(2);
85+
86+
{
87+
auto str = getFromSocket("/activeworkspace");
88+
EXPECT_CONTAINS(str, "windows: 0");
89+
}
90+
91+
{
92+
OK(getFromSocket("/dispatch focuswindow class:hyprland-dialog"))
93+
auto str = getFromSocket("/activewindow");
94+
EXPECT_CONTAINS(str, "yummy");
95+
}
96+
97+
Tests::killAllWindows();
98+
}
99+
100+
NLog::log("{}anrdialog: special workspaces", Colors::YELLOW);
101+
{
102+
103+
OK(getFromSocket("/dispatch workspace special:apple"));
104+
105+
Tests::spawnKitty("bad_kitty");
106+
107+
int pid = 0;
108+
{
109+
auto str = getFromSocket("/activewindow");
110+
EXPECT_CONTAINS(str, "special:apple");
111+
112+
auto pos = str.find("pid") + 4;
113+
auto end = str.find('\n', pos);
114+
pid = std::stoi(str.substr(pos, end));
115+
}
116+
117+
OK(getFromSocket("/dispatch togglespecialworkspace apple"));
118+
OK(getFromSocket("/dispatch workspace 1"));
119+
120+
::kill(pid, SIGSTOP);
121+
122+
Tests::waitUntilWindowsN(2);
123+
124+
{
125+
OK(getFromSocket("/dispatch focuswindow class:hyprland-dialog"))
126+
auto str = getFromSocket("/activewindow");
127+
EXPECT_CONTAINS(str, "special:apple");
128+
}
129+
130+
Tests::killAllWindows();
131+
}
132+
133+
OK(getFromSocket("/reload"));
134+
Tests::killAllWindows();
135+
}
136+
21137
static bool test() {
22138
NLog::log("{}Testing config: misc:", Colors::GREEN);
23139

@@ -204,6 +320,11 @@ static bool test() {
204320
EXPECT_CONTAINS(str, "fullscreen: 2");
205321
}
206322

323+
OK(getFromSocket("/reload"));
324+
Tests::killAllWindows();
325+
326+
testAnrDialogs();
327+
207328
// Ensure that the process autostarted in the config does not
208329
// become a zombie even if it terminates very quickly.
209330
EXPECT(Tests::execAndGet("pgrep -f 'sleep 0'").empty(), true);

src/helpers/AsyncDialogBox.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include <algorithm>
55
#include <unistd.h>
66
#include "../managers/eventLoop/EventLoopManager.hpp"
7+
#include "../desktop/rule/windowRule/WindowRule.hpp"
8+
#include "../desktop/rule/Engine.hpp"
79

810
using namespace Hyprutils::OS;
911

@@ -119,6 +121,9 @@ SP<CPromise<std::string>> CAsyncDialogBox::open() {
119121

120122
m_selfReference = m_selfWeakReference.lock();
121123

124+
if (!m_execRuleToken.empty())
125+
proc.addEnv(Desktop::Rule::EXEC_RULE_ENV_NAME, m_execRuleToken);
126+
122127
if (!proc.runAsync()) {
123128
Debug::log(ERR, "CAsyncDialogBox::open: failed to run async");
124129
wl_event_source_remove(m_readEventSource);
@@ -150,3 +155,9 @@ bool CAsyncDialogBox::isRunning() const {
150155
SP<CAsyncDialogBox> CAsyncDialogBox::lockSelf() {
151156
return m_selfWeakReference.lock();
152157
}
158+
159+
void CAsyncDialogBox::setExecRule(std::string&& s) {
160+
auto rule = Desktop::Rule::CWindowRule::buildFromExecString(std::move(s));
161+
m_execRuleToken = rule->execToken();
162+
Desktop::Rule::ruleEngine()->registerRule(std::move(rule));
163+
}

src/helpers/AsyncDialogBox.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class CAsyncDialogBox {
2626
SP<CPromise<std::string>> open();
2727
void kill();
2828
bool isRunning() const;
29+
void setExecRule(std::string&& s);
2930

3031
SP<CAsyncDialogBox> lockSelf();
3132

@@ -40,7 +41,8 @@ class CAsyncDialogBox {
4041
pid_t m_dialogPid = 0;
4142
wl_event_source* m_readEventSource = nullptr;
4243
Hyprutils::OS::CFileDescriptor m_pipeReadFd;
43-
std::string m_stdout = "";
44+
std::string m_stdout = "";
45+
std::string m_execRuleToken = "";
4446

4547
const std::string m_title;
4648
const std::string m_description;
@@ -51,4 +53,4 @@ class CAsyncDialogBox {
5153
// WARNING: cyclic reference. This will be removed once the event source is removed to avoid dangling pointers
5254
SP<CAsyncDialogBox> m_selfReference;
5355
WP<CAsyncDialogBox> m_selfWeakReference;
54-
};
56+
};

src/managers/ANRManager.cpp

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -183,21 +183,35 @@ void CANRManager::SANRData::runDialog(const std::string& appName, const std::str
183183

184184
const auto OPTION_TERMINATE_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_OPTION_TERMINATE, {});
185185
const auto OPTION_WAIT_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_OPTION_WAIT, {});
186+
const auto OPTIONS = std::vector{OPTION_TERMINATE_STR, OPTION_WAIT_STR};
187+
const auto CLASS_STR = appClass.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appClass;
188+
const auto TITLE_STR = appName.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appName;
189+
const auto DESCRIPTION_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_CONTENT, {{"title", TITLE_STR}, {"class", CLASS_STR}});
190+
191+
PHLWINDOW parentWindow = nullptr;
192+
for (const auto& w : g_pCompositor->m_windows) {
193+
if (!w->m_isMapped)
194+
continue;
195+
196+
if (!fitsWindow(w))
197+
continue;
198+
199+
parentWindow = w;
200+
break;
201+
}
202+
203+
dialogBox = CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_TITLE, {}), DESCRIPTION_STR, OPTIONS);
186204

187-
dialogBox =
188-
CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_TITLE, {}),
189-
I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_CONTENT,
190-
{
191-
//
192-
{"class", appClass.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appClass}, //
193-
{"title", appName.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appName} //
194-
}),
195-
std::vector<std::string>{
196-
//
197-
OPTION_TERMINATE_STR, //
198-
OPTION_WAIT_STR //
199-
} //
200-
);
205+
if (parentWindow) {
206+
const auto WORKSPACEID = parentWindow->workspaceID();
207+
208+
if (parentWindow->onSpecialWorkspace()) {
209+
PHLWORKSPACE parentWorkspace = g_pCompositor->getWorkspaceByID(WORKSPACEID);
210+
if (parentWorkspace)
211+
dialogBox->setExecRule(std::format("workspace {} silent", parentWorkspace->m_name));
212+
} else
213+
dialogBox->setExecRule(std::format("workspace e~{} silent", WORKSPACEID));
214+
}
201215

202216
dialogBox->open()->then([dialogWmPID, this, OPTION_TERMINATE_STR, OPTION_WAIT_STR](SP<CPromiseResult<std::string>> r) {
203217
if (r->hasError()) {

0 commit comments

Comments
 (0)