Skip to content

Commit

Permalink
feat(anr): add xwayland support
Browse files Browse the repository at this point in the history
  • Loading branch information
nnyyxxxx committed Feb 18, 2025
1 parent 0137a5f commit 64fa4a7
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 42 deletions.
5 changes: 4 additions & 1 deletion src/desktop/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
159 changes: 131 additions & 28 deletions src/managers/ANRManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "../protocols/XDGShell.hpp"
#include "./eventLoop/EventLoopManager.hpp"
#include "../config/ConfigValue.hpp"
#include "../xwayland/XWayland.hpp"

using namespace Hyprutils::OS;

Expand All @@ -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<PHLWINDOW>(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<SANRData>();
return;
}

if (!window->m_pXDGSurface)
return;

for (const auto& w : g_pCompositor->m_vWindows) {
Expand All @@ -43,8 +60,79 @@ CANRManager::CANRManager() {
m_timer->updateTimeout(TIMER_TIMEOUT);
}

std::pair<PHLWINDOW, int> CANRManager::findFirstWindowAndCount(const WP<CXDGWMBase>& 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<PHLWINDOW, int> CANRManager::findFirstXWaylandWindowAndCount(const WP<CXWaylandSurface>& 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 <typename T>
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<T, WP<CXDGWMBase>>) {
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<SANRData>& data, PHLWINDOW firstWindow, pid_t pid, const WP<CXDGWMBase>& 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<SANRData>& data, PHLWINDOW firstWindow, const WP<CXWaylandSurface>& 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<Hyprlang::INT>("misc:enable_anr_dialog");

Expand All @@ -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();

Expand All @@ -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);
Expand All @@ -111,6 +198,16 @@ void CANRManager::onResponse(SP<CXDGWMBase> wmBase) {
data->killDialog();
}

void CANRManager::onXWaylandResponse(SP<CXWaylandSurface> 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();
Expand Down Expand Up @@ -171,3 +268,9 @@ bool CANRManager::isNotResponding(SP<CXDGWMBase> wmBase) {
return false;
return m_data[wmBase]->missedResponses > 1;
}

bool CANRManager::isXWaylandNotResponding(SP<CXWaylandSurface> surf) {
if (!m_xwaylandData.contains(surf))
return false;
return m_xwaylandData[surf]->missedResponses > 1;
}
27 changes: 20 additions & 7 deletions src/managers/ANRManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@
#include <thread>

class CXDGWMBase;
class CXWaylandSurface;

class CANRManager {
public:
CANRManager();

void onResponse(SP<CXDGWMBase> wmBase);
bool isNotResponding(SP<CXDGWMBase> wmBase);
void onResponse(SP<CXDGWMBase> wmBase);
bool isNotResponding(SP<CXDGWMBase> wmBase);

void onXWaylandResponse(SP<CXWaylandSurface> surf);
bool isXWaylandNotResponding(SP<CXWaylandSurface> surf);

private:
bool m_active = false;
SP<CEventLoopTimer> m_timer;

Expand All @@ -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<Hyprutils::OS::CProcess> dialogProc;
std::atomic<bool> dialogThreadExited = false;
std::atomic<bool> 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<WP<CXDGWMBase>, SP<SANRData>> m_data;
std::map<WP<CXDGWMBase>, SP<SANRData>> m_data;
std::map<WP<CXWaylandSurface>, SP<SANRData>> m_xwaylandData;

private:
std::pair<PHLWINDOW, int> findFirstWindowAndCount(const WP<CXDGWMBase>& wmBase);
std::pair<PHLWINDOW, int> findFirstXWaylandWindowAndCount(const WP<CXWaylandSurface>& surf);
void handleDialog(SP<SANRData>& data, PHLWINDOW firstWindow, pid_t pid, const WP<CXDGWMBase>& wmBase);
void handleXWaylandDialog(SP<SANRData>& data, PHLWINDOW firstWindow, const WP<CXWaylandSurface>& surf);

template <typename T>
void setWindowTint(const T& owner, float tint);
};

inline UP<CANRManager> g_pANRManager;
17 changes: 11 additions & 6 deletions src/xwayland/XWM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"]) {
Expand Down Expand Up @@ -1465,4 +1466,8 @@ bool SXTransfer::getIncomingSelectionProp(bool erase) {
return true;
}

CXCBConnection& CXWM::getConnection() {
return connection;
}

#endif
1 change: 1 addition & 0 deletions src/xwayland/XWM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ class CXWM {
int onEvent(int fd, uint32_t mask);
SP<CX11DataDevice> getDataDevice();
SP<IDataOffer> createX11DataOffer(SP<CWLSurfaceResource> surf, SP<IDataSource> source);
CXCBConnection& getConnection();

private:
void setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot);
Expand Down
1 change: 1 addition & 0 deletions src/xwayland/XWayland.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ inline std::unordered_map<std::string, uint32_t> 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"),
Expand Down

0 comments on commit 64fa4a7

Please sign in to comment.