diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index 7c0693bf4a3..62ccb7572d1 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -1801,8 +1801,11 @@ void CWindow::deactivateGroupMembers() { } bool CWindow::isNotResponding() { - if (!m_pXDGSurface) + if (!m_pXDGSurface && !m_pXWaylandSurface) return false; + if (m_bIsX11) + return g_pANRManager->isXWaylandNotResponding(m_pXWaylandSurface.lock()); + return g_pANRManager->isNotResponding(m_pXDGSurface->owner.lock()); } diff --git a/src/managers/ANRManager.cpp b/src/managers/ANRManager.cpp index af1cb93a188..c0bc0116ee1 100644 --- a/src/managers/ANRManager.cpp +++ b/src/managers/ANRManager.cpp @@ -7,6 +7,7 @@ #include "../protocols/XDGShell.hpp" #include "./eventLoop/EventLoopManager.hpp" #include "../config/ConfigValue.hpp" +#include "../xwayland/XWayland.hpp" using namespace Hyprutils::OS; @@ -26,7 +27,23 @@ CANRManager::CANRManager() { static auto P = g_pHookSystem->hookDynamic("openWindow", [this](void* self, SCallbackInfo& info, std::any data) { auto window = std::any_cast(data); - if (window->m_bIsX11) + if (window->m_bIsX11) { + if (!window->m_pXWaylandSurface) + return; + + for (const auto& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsX11 || w == window || !w->m_pXWaylandSurface) + continue; + + if (w->m_pXWaylandSurface->pid == window->m_pXWaylandSurface->pid) + return; + } + + m_xwaylandData[window->m_pXWaylandSurface] = makeShared(); + return; + } + + if (!window->m_pXDGSurface) return; for (const auto& w : g_pCompositor->m_vWindows) { @@ -43,8 +60,79 @@ CANRManager::CANRManager() { m_timer->updateTimeout(TIMER_TIMEOUT); } +std::pair CANRManager::findFirstWindowAndCount(const WP& wmBase) { + PHLWINDOW firstWindow = nullptr; + int count = 0; + + for (const auto& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped || w->m_bIsX11 || !w->m_pXDGSurface) + continue; + + if (w->m_pXDGSurface->owner != wmBase) + continue; + + count++; + if (!firstWindow) + firstWindow = w; + } + + return {firstWindow, count}; +} + +std::pair CANRManager::findFirstXWaylandWindowAndCount(const WP& surf) { + PHLWINDOW firstWindow = nullptr; + int count = 0; + + for (const auto& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped || !w->m_bIsX11 || !w->m_pXWaylandSurface) + continue; + + if (w->m_pXWaylandSurface != surf) + continue; + + count++; + if (!firstWindow) + firstWindow = w; + } + + return {firstWindow, count}; +} + +template +void CANRManager::setWindowTint(const T& owner, float tint) { + for (const auto& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped) + continue; + + if constexpr (std::is_same_v>) { + if (w->m_bIsX11 || !w->m_pXDGSurface || w->m_pXDGSurface->owner != owner) + continue; + } else { + if (!w->m_bIsX11 || !w->m_pXWaylandSurface || w->m_pXWaylandSurface != owner) + continue; + } + + *w->m_notRespondingTint = tint; + } +} + +void CANRManager::handleDialog(SP& data, PHLWINDOW firstWindow, pid_t pid, const WP& wmBase) { + if (!data->isThreadRunning() && !data->dialogThreadSaidWait) { + data->runDialog("Application Not Responding", firstWindow->m_szTitle, firstWindow->m_szClass, pid); + setWindowTint(wmBase, 0.2F); + } +} + +void CANRManager::handleXWaylandDialog(SP& data, PHLWINDOW firstWindow, const WP& surf) { + if (!data->isThreadRunning() && !data->dialogThreadSaidWait) { + data->runDialog("Application Not Responding", firstWindow->m_szTitle, firstWindow->m_szClass, surf->pid); + setWindowTint(surf, 0.2F); + } +} + void CANRManager::onTick() { std::erase_if(m_data, [](const auto& e) { return e.first.expired(); }); + std::erase_if(m_xwaylandData, [](const auto& e) { return e.first.expired(); }); static auto PENABLEANR = CConfigValue("misc:enable_anr_dialog"); @@ -54,39 +142,31 @@ void CANRManager::onTick() { } for (auto& [wmBase, data] : m_data) { - PHLWINDOW firstWindow; - int count = 0; - for (const auto& w : g_pCompositor->m_vWindows) { - if (!w->m_bIsMapped || w->m_bIsX11 || !w->m_pXDGSurface) - continue; - - if (w->m_pXDGSurface->owner != wmBase) - continue; - - count++; - if (!firstWindow) - firstWindow = w; - } - + auto [firstWindow, count] = findFirstWindowAndCount(wmBase); if (count == 0) continue; if (data->missedResponses > 0) { - if (!data->isThreadRunning() && !data->dialogThreadSaidWait) { - pid_t pid = 0; - wl_client_get_credentials(wmBase->client(), &pid, nullptr, nullptr); - data->runDialog("Application Not Responding", firstWindow->m_szTitle, firstWindow->m_szClass, pid); + pid_t pid = 0; + wl_client_get_credentials(wmBase->client(), &pid, nullptr, nullptr); + handleDialog(data, firstWindow, pid, wmBase); + } else if (data->isThreadRunning()) + data->killDialog(); - for (const auto& w : g_pCompositor->m_vWindows) { - if (!w->m_bIsMapped || w->m_bIsX11 || !w->m_pXDGSurface) - continue; + if (data->missedResponses == 0) + data->dialogThreadSaidWait = false; + + data->missedResponses++; + wmBase->ping(); + } - if (w->m_pXDGSurface->owner != wmBase) - continue; + for (auto& [surf, data] : m_xwaylandData) { + auto [firstWindow, count] = findFirstXWaylandWindowAndCount(surf); + if (count == 0) + continue; - *w->m_notRespondingTint = 0.2F; - } - } + if (data->missedResponses > 0) { + handleXWaylandDialog(data, firstWindow, surf); } else if (data->isThreadRunning()) data->killDialog(); @@ -95,7 +175,14 @@ void CANRManager::onTick() { data->missedResponses++; - wmBase->ping(); + xcb_client_message_event_t event = {.response_type = XCB_CLIENT_MESSAGE, + .format = 32, + .window = surf->xID, + .type = HYPRATOMS["_NET_WM_PING"], + .data = {.data32 = {XCB_CURRENT_TIME, HYPRATOMS["_NET_WM_PING"], surf->xID}}}; + + xcb_send_event(g_pXWayland->pWM->getConnection(), 0, surf->xID, XCB_EVENT_MASK_NO_EVENT, (const char*)&event); + xcb_flush(g_pXWayland->pWM->getConnection()); } m_timer->updateTimeout(TIMER_TIMEOUT); @@ -111,6 +198,16 @@ void CANRManager::onResponse(SP wmBase) { data->killDialog(); } +void CANRManager::onXWaylandResponse(SP surf) { + if (!m_xwaylandData.contains(surf)) + return; + + auto& data = m_xwaylandData.at(surf); + data->missedResponses = 0; + if (data->isThreadRunning()) + data->killDialog(); +} + void CANRManager::SANRData::runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID) { if (!dialogThreadExited) killDialog(); @@ -171,3 +268,9 @@ bool CANRManager::isNotResponding(SP wmBase) { return false; return m_data[wmBase]->missedResponses > 1; } + +bool CANRManager::isXWaylandNotResponding(SP surf) { + if (!m_xwaylandData.contains(surf)) + return false; + return m_xwaylandData[surf]->missedResponses > 1; +} diff --git a/src/managers/ANRManager.hpp b/src/managers/ANRManager.hpp index 7d67e8726b4..10e96eaa4c7 100644 --- a/src/managers/ANRManager.hpp +++ b/src/managers/ANRManager.hpp @@ -12,15 +12,18 @@ #include class CXDGWMBase; +class CXWaylandSurface; class CANRManager { public: CANRManager(); - void onResponse(SP wmBase); - bool isNotResponding(SP wmBase); + void onResponse(SP wmBase); + bool isNotResponding(SP wmBase); + + void onXWaylandResponse(SP surf); + bool isXWaylandNotResponding(SP surf); - private: bool m_active = false; SP m_timer; @@ -29,18 +32,28 @@ class CANRManager { struct SANRData { ~SANRData(); - int missedResponses = 0; + int missedResponses = 0; + bool dialogThreadExited = true; + bool dialogThreadSaidWait = false; std::thread dialogThread; SP dialogProc; - std::atomic dialogThreadExited = false; - std::atomic dialogThreadSaidWait = false; void runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID); bool isThreadRunning(); void killDialog() const; }; - std::map, SP> m_data; + std::map, SP> m_data; + std::map, SP> m_xwaylandData; + + private: + std::pair findFirstWindowAndCount(const WP& wmBase); + std::pair findFirstXWaylandWindowAndCount(const WP& surf); + void handleDialog(SP& data, PHLWINDOW firstWindow, pid_t pid, const WP& wmBase); + void handleXWaylandDialog(SP& data, PHLWINDOW firstWindow, const WP& surf); + + template + void setWindowTint(const T& owner, float tint); }; inline UP g_pANRManager; \ No newline at end of file diff --git a/src/xwayland/XWM.cpp b/src/xwayland/XWM.cpp index a4e3289b7c7..aa4171631c5 100644 --- a/src/xwayland/XWM.cpp +++ b/src/xwayland/XWM.cpp @@ -17,6 +17,7 @@ #include "../managers/SeatManager.hpp" #include "../protocols/XWaylandShell.hpp" #include "../protocols/core/Compositor.hpp" +#include "../managers/ANRManager.hpp" using namespace Hyprutils::OS; #define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f @@ -315,13 +316,13 @@ void CXWM::handleClientMessage(xcb_client_message_event_t* e) { if (!XSURF) return; - std::string propName = "?"; - for (auto const& ha : HYPRATOMS) { - if (ha.second != e->type) - continue; + std::string propName = getAtomName(e->type); - propName = ha.first; - break; + if (e->type == HYPRATOMS["_NET_WM_PING"]) { + if (e->data.data32[0] == XCB_CURRENT_TIME && e->data.data32[1] == HYPRATOMS["_NET_WM_PING"]) { + g_pANRManager->onXWaylandResponse(XSURF); + return; + } } if (e->type == HYPRATOMS["WL_SURFACE_ID"]) { @@ -1465,4 +1466,8 @@ bool SXTransfer::getIncomingSelectionProp(bool erase) { return true; } +CXCBConnection& CXWM::getConnection() { + return connection; +} + #endif diff --git a/src/xwayland/XWM.hpp b/src/xwayland/XWM.hpp index 4326c77b1e1..dedbc6fe124 100644 --- a/src/xwayland/XWM.hpp +++ b/src/xwayland/XWM.hpp @@ -112,6 +112,7 @@ class CXWM { int onEvent(int fd, uint32_t mask); SP getDataDevice(); SP createX11DataOffer(SP surf, SP source); + CXCBConnection& getConnection(); private: void setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot); diff --git a/src/xwayland/XWayland.hpp b/src/xwayland/XWayland.hpp index af8d957c0a7..9202f19eeaa 100644 --- a/src/xwayland/XWayland.hpp +++ b/src/xwayland/XWayland.hpp @@ -83,6 +83,7 @@ inline std::unordered_map HYPRATOMS = { HYPRATOM("_NET_WORKAREA"), HYPRATOM("_NET_WM_ICON"), HYPRATOM("_NET_WM_CM_S0"), + HYPRATOM("_NET_WM_PING"), HYPRATOM("WM_PROTOCOLS"), HYPRATOM("WM_HINTS"), HYPRATOM("WM_DELETE_WINDOW"),