From ce9787b3f47ce550027274a1aa25e74b6fb1c74a Mon Sep 17 00:00:00 2001 From: jmanc3 Date: Wed, 29 Oct 2025 06:24:34 -0500 Subject: [PATCH] xwayland: set _NET_WORKAREA property (#12148) --- src/Compositor.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/Compositor.hpp | 1 + src/layout/DwindleLayout.cpp | 6 ++++++ src/layout/MasterLayout.cpp | 6 ++++++ src/xwayland/XWM.cpp | 18 +++++++++++++++++- src/xwayland/XWM.hpp | 1 + 6 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 78b19c71..3c7ad6a2 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -1789,6 +1789,37 @@ bool CCompositor::isPointOnReservedArea(const Vector2D& point, const PHLMONITOR return VECNOTINRECT(point, XY1.x, XY1.y, XY2.x, XY2.y); } +CBox CCompositor::calculateX11WorkArea() { + static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); + CBox workbox = {0, 0, 0, 0}; + bool firstMonitor = true; + + for (const auto& monitor : m_monitors) { + // we ignore monitor->m_position on purpose + auto x = monitor->m_reservedTopLeft.x; + auto y = monitor->m_reservedTopLeft.y; + auto w = monitor->m_size.x - monitor->m_reservedBottomRight.x - x; + auto h = monitor->m_size.y - monitor->m_reservedBottomRight.y - y; + CBox box = {x, y, w, h}; + if ((*PXWLFORCESCALEZERO)) + box.scale(monitor->m_scale); + + if (firstMonitor) { + firstMonitor = false; + workbox = box; + } else { + // if this monitor creates a different workbox than previous monitor, we remove the _NET_WORKAREA property all together + if ((std::abs(box.x - workbox.x) > 3) || (std::abs(box.y - workbox.y) > 3) || (std::abs(box.w - workbox.w) > 3) || (std::abs(box.h - workbox.h) > 3)) { + workbox = {0, 0, 0, 0}; + break; + } + } + } + + // returning 0, 0 will remove the _NET_WORKAREA property + return workbox; +} + PHLMONITOR CCompositor::getMonitorInDirection(const char& dir) { return getMonitorInDirection(m_lastMonitor.lock(), dir); } @@ -2983,6 +3014,11 @@ void CCompositor::arrangeMonitors() { } PROTO::xdgOutput->updateAllOutputs(); + +#ifndef NO_XWAYLAND + CBox box = g_pCompositor->calculateX11WorkArea(); + g_pXWayland->m_wm->updateWorkArea(box.x, box.y, box.w, box.h); +#endif } void CCompositor::enterUnsafeState() { diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 49222d05..bf6401e5 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -125,6 +125,7 @@ class CCompositor { WORKSPACEID getNextAvailableNamedWorkspace(); bool isPointOnAnyMonitor(const Vector2D&); bool isPointOnReservedArea(const Vector2D& point, const PHLMONITOR monitor = nullptr); + CBox calculateX11WorkArea(); PHLMONITOR getMonitorInDirection(const char&); PHLMONITOR getMonitorInDirection(PHLMONITOR, const char&); void updateAllWindowsAnimatedDecorationValues(); diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp index 4856c355..e46a0963 100644 --- a/src/layout/DwindleLayout.cpp +++ b/src/layout/DwindleLayout.cpp @@ -7,6 +7,7 @@ #include "../managers/input/InputManager.hpp" #include "../managers/LayoutManager.hpp" #include "../managers/EventManager.hpp" +#include "xwayland/XWayland.hpp" SWorkspaceGaps CHyprDwindleLayout::getWorkspaceGaps(const PHLWORKSPACE& pWorkspace) { const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(pWorkspace); @@ -597,6 +598,11 @@ void CHyprDwindleLayout::recalculateMonitor(const MONITORID& monid) { calculateWorkspace(PMONITOR->m_activeSpecialWorkspace); calculateWorkspace(PMONITOR->m_activeWorkspace); + +#ifndef NO_XWAYLAND + CBox box = g_pCompositor->calculateX11WorkArea(); + g_pXWayland->m_wm->updateWorkArea(box.x, box.y, box.w, box.h); +#endif } void CHyprDwindleLayout::calculateWorkspace(const PHLWORKSPACE& pWorkspace) { diff --git a/src/layout/MasterLayout.cpp b/src/layout/MasterLayout.cpp index c9dbcdd6..b40c339a 100644 --- a/src/layout/MasterLayout.cpp +++ b/src/layout/MasterLayout.cpp @@ -9,6 +9,7 @@ #include "../managers/input/InputManager.hpp" #include "../managers/LayoutManager.hpp" #include "../managers/EventManager.hpp" +#include "xwayland/XWayland.hpp" SMasterNodeData* CHyprMasterLayout::getNodeFromWindow(PHLWINDOW pWindow) { for (auto& nd : m_masterNodesData) { @@ -293,6 +294,11 @@ void CHyprMasterLayout::recalculateMonitor(const MONITORID& monid) { calculateWorkspace(PMONITOR->m_activeSpecialWorkspace); calculateWorkspace(PMONITOR->m_activeWorkspace); + +#ifndef NO_XWAYLAND + CBox box = g_pCompositor->calculateX11WorkArea(); + g_pXWayland->m_wm->updateWorkArea(box.x, box.y, box.w, box.h); +#endif } void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { diff --git a/src/xwayland/XWM.cpp b/src/xwayland/XWM.cpp index cadf66af..9abd955a 100644 --- a/src/xwayland/XWM.cpp +++ b/src/xwayland/XWM.cpp @@ -987,7 +987,7 @@ CXWM::CXWM() : m_connection(makeUnique(g_pXWayland->m_server->m_ xcb_atom_t supported[] = { HYPRATOMS["_NET_WM_STATE"], HYPRATOMS["_NET_ACTIVE_WINDOW"], HYPRATOMS["_NET_WM_MOVERESIZE"], HYPRATOMS["_NET_WM_STATE_FOCUSED"], HYPRATOMS["_NET_WM_STATE_MODAL"], HYPRATOMS["_NET_WM_STATE_FULLSCREEN"], HYPRATOMS["_NET_WM_STATE_MAXIMIZED_VERT"], HYPRATOMS["_NET_WM_STATE_MAXIMIZED_HORZ"], - HYPRATOMS["_NET_WM_STATE_HIDDEN"], HYPRATOMS["_NET_CLIENT_LIST"], HYPRATOMS["_NET_CLIENT_LIST_STACKING"], + HYPRATOMS["_NET_WM_STATE_HIDDEN"], HYPRATOMS["_NET_CLIENT_LIST"], HYPRATOMS["_NET_CLIENT_LIST_STACKING"], HYPRATOMS["_NET_WORKAREA"], }; xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, m_screen->root, HYPRATOMS["_NET_SUPPORTED"], XCB_ATOM_ATOM, 32, sizeof(supported) / sizeof(*supported), supported); @@ -1193,6 +1193,22 @@ void CXWM::updateClientList() { xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, m_screen->root, HYPRATOMS["_NET_CLIENT_LIST_STACKING"], XCB_ATOM_WINDOW, 32, windows.size(), windows.data()); } +void CXWM::updateWorkArea(int x, int y, int w, int h) { + if (!g_pXWayland || !g_pXWayland->m_wm || !g_pXWayland->m_wm->getConnection() || !m_screen || !m_screen->root) + return; + auto connection = g_pXWayland->m_wm->getConnection(); + + if (w <= 0 || h <= 0) { + xcb_delete_property(connection, m_screen->root, HYPRATOMS["_NET_WORKAREA"]); + xcb_flush(connection); + return; + } + + uint32_t values[4] = {sc(x), sc(y), sc(w), sc(h)}; + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, m_screen->root, HYPRATOMS["_NET_WORKAREA"], XCB_ATOM_CARDINAL, 32, 4, values); + xcb_flush(connection); +} + bool CXWM::isWMWindow(xcb_window_t w) { return w == m_wmWindow || w == m_clipboard.window || w == m_dndSelection.window; } diff --git a/src/xwayland/XWM.hpp b/src/xwayland/XWM.hpp index b03ab4b2..b328a2c9 100644 --- a/src/xwayland/XWM.hpp +++ b/src/xwayland/XWM.hpp @@ -118,6 +118,7 @@ class CXWM { int onEvent(int fd, uint32_t mask); SP getDataDevice(); SP createX11DataOffer(SP surf, SP source); + void updateWorkArea(int x, int y, int w, int h); private: void setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot);