helpers: Add an async dialog box impl (#9919)

Adds an async dialog box, way safer than our previous local solution for ANR
This commit is contained in:
Vaxry 2025-04-06 17:31:58 +02:00 committed by GitHub
parent e96b8ce4cc
commit 3c128679ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 215 additions and 76 deletions

View file

@ -39,8 +39,6 @@ CANRManager::CANRManager() {
}
void CANRManager::onTick() {
std::erase_if(m_data, [](const auto& e) { return e->isDefunct(); });
static auto PENABLEANR = CConfigValue<Hyprlang::INT>("misc:enable_anr_dialog");
static auto PANRTHRESHOLD = CConfigValue<Hyprlang::INT>("misc:anr_missed_pings");
@ -68,7 +66,7 @@ void CANRManager::onTick() {
continue;
if (data->missedResponses >= *PANRTHRESHOLD) {
if (!data->isThreadRunning() && !data->dialogThreadSaidWait) {
if (!data->isRunning() && !data->dialogSaidWait) {
data->runDialog("Application Not Responding", firstWindow->m_szTitle, firstWindow->m_szClass, data->getPid());
for (const auto& w : g_pCompositor->m_vWindows) {
@ -81,11 +79,11 @@ void CANRManager::onTick() {
*w->m_notRespondingTint = 0.2F;
}
}
} else if (data->isThreadRunning())
} else if (data->isRunning())
data->killDialog();
if (data->missedResponses == 0)
data->dialogThreadSaidWait = false;
data->dialogSaidWait = false;
data->missedResponses++;
@ -115,7 +113,7 @@ void CANRManager::onResponse(SP<CXWaylandSurface> pXwaylandSurface) {
void CANRManager::onResponse(SP<CANRManager::SANRData> data) {
data->missedResponses = 0;
if (data->isThreadRunning())
if (data->isRunning())
data->killDialog();
}
@ -158,64 +156,39 @@ CANRManager::SANRData::SANRData(PHLWINDOW pWindow) :
}
CANRManager::SANRData::~SANRData() {
if (dialogThread.joinable()) {
if (dialogBox && dialogBox->isRunning())
killDialog();
// dangerous: might lock if the above failed!!
dialogThread.join();
}
}
void CANRManager::SANRData::runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID) {
if (!dialogThreadExited)
if (dialogBox && dialogBox->isRunning())
killDialog();
// dangerous: might lock if the above failed!!
if (dialogThread.joinable())
dialogThread.join();
dialogBox = CAsyncDialogBox::create(title,
std::format("Application {} with class of {} is not responding.\nWhat do you want to do with it?", appName.empty() ? "unknown" : appName,
appClass.empty() ? "unknown" : appClass),
std::vector<std::string>{"Terminate", "Wait"});
dialogThreadExited = false;
dialogThreadSaidWait = false;
dialogThread = std::thread([title, appName, appClass, dialogWmPID, this]() {
SP<CProcess> proc = makeShared<CProcess>("hyprland-dialog",
std::vector<std::string>{"--title", title, "--text",
std::format("Application {} with class of {} is not responding.\nWhat do you want to do with it?",
appName.empty() ? "unknown" : appName, appClass.empty() ? "unknown" : appClass),
"--buttons", "Terminate;Wait"});
dialogProc = proc;
proc->runSync();
dialogThreadExited = true;
if (proc->stdOut().empty())
return;
if (proc->stdOut().starts_with("Terminate"))
kill(dialogWmPID, SIGKILL);
if (proc->stdOut().starts_with("Wait"))
dialogThreadSaidWait = true;
dialogBox->open([dialogWmPID, this](std::string result) {
if (result.starts_with("Terminate"))
::kill(dialogWmPID, SIGKILL);
else if (result.starts_with("Wait"))
dialogSaidWait = true;
else
Debug::log(ERR, "CANRManager::SANRData::runDialog: lambda: unrecognized result: {}", result);
});
}
bool CANRManager::SANRData::isThreadRunning() {
if (dialogThread.native_handle() == 0)
return false;
if (dialogThreadExited)
return false;
return pthread_kill(dialogThread.native_handle(), 0) != ESRCH;
bool CANRManager::SANRData::isRunning() {
return dialogBox && dialogBox->isRunning();
}
void CANRManager::SANRData::killDialog() {
if (!dialogProc)
if (!dialogBox)
return;
if (!dialogProc->pid()) {
Debug::log(ERR, "ANR: cannot kill dialogProc, as it doesn't have a pid.");
dialogProc = nullptr;
return;
}
kill(dialogProc->pid(), SIGKILL);
dialogBox->kill();
dialogBox = nullptr;
}
bool CANRManager::SANRData::fitsWindow(PHLWINDOW pWindow) const {