From e8e9d1c193c5050720cf62a2389876c1786762f9 Mon Sep 17 00:00:00 2001 From: TSUMUGI-XE <291260587+TSUMUGI-XE@users.noreply.github.com> Date: Fri, 17 Jul 2026 02:50:32 +0900 Subject: [PATCH] fix(wayland): only add input-capture barriers on the outer desktop edge On a multi-monitor server the portal reports one input-capture zone per output. handleZonesChanged added a pointer barrier on every active side of every zone, including edges that sit on an internal seam between two adjacent monitors. The portal rejects such a barrier ("adjacent to multiple monitor edges"), and because a single rejected barrier fails the whole set_pointer_barriers request, input capture never engaged on multi-monitor Wayland servers. Compute the union (bounding box) of all zones first, then add a barrier for a given side only when that zone edge lies on the outer boundary of the union, skipping internal seams. --- src/lib/platform/PortalInputCapture.cpp | 45 ++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src/lib/platform/PortalInputCapture.cpp b/src/lib/platform/PortalInputCapture.cpp index 957ebc757..554655600 100644 --- a/src/lib/platform/PortalInputCapture.cpp +++ b/src/lib/platform/PortalInputCapture.cpp @@ -777,8 +777,43 @@ void PortalInputCapture::handleZonesChanged(XdpInputCaptureSession *session, con const auto activeSides = m_screen->activeSides(); using enum DirectionMask; - // May not correctly handle different sized screens auto zones = xdp_input_capture_session_get_zones(session); + + // First pass: compute the bounding box (union) of all input-capture zones. + // A pointer barrier must lie on the outer boundary of the combined desktop and + // be adjacent to a single monitor edge. A barrier placed on an internal edge + // between two adjacent monitors is rejected by the portal ("adjacent to + // multiple monitor edges"), and a single rejected barrier fails the whole + // barrier set - so on a multi-monitor server input capture never engages. + gint unionLeft = 0; + gint unionTop = 0; + gint unionRight = 0; + gint unionBottom = 0; + bool boundsInit = false; + for (auto z = zones; z != nullptr; z = z->next) { + guint w; + guint h; + gint x; + gint y; + g_object_get(z->data, "width", &w, "height", &h, "x", &x, "y", &y, nullptr); + const gint right = x + static_cast(w); + const gint bottom = y + static_cast(h); + if (!boundsInit) { + unionLeft = x; + unionTop = y; + unionRight = right; + unionBottom = bottom; + boundsInit = true; + } else { + unionLeft = std::min(unionLeft, x); + unionTop = std::min(unionTop, y); + unionRight = std::max(unionRight, right); + unionBottom = std::max(unionBottom, bottom); + } + } + + // Second pass: only add a barrier for a zone edge that is part of the outer + // boundary of the union (i.e. not an internal seam between two monitors). guint id = 0; while (zones != nullptr) { guint w; @@ -789,19 +824,19 @@ void PortalInputCapture::handleZonesChanged(XdpInputCaptureSession *session, con LOG_DEBUG("input capture zone, %dx%d@%d,%d", w, h, x, y); - if (activeSides & static_cast(LeftMask)) { + if ((activeSides & static_cast(LeftMask)) && x == unionLeft) { addBarrier(++id, BarrierSide::Left, x, y, w, h); } - if (activeSides & static_cast(RightMask)) { + if ((activeSides & static_cast(RightMask)) && (x + static_cast(w)) == unionRight) { addBarrier(++id, BarrierSide::Right, x, y, w, h); } - if (activeSides & static_cast(TopMask)) { + if ((activeSides & static_cast(TopMask)) && y == unionTop) { addBarrier(++id, BarrierSide::Top, x, y, w, h); } - if (activeSides & static_cast(BottomMask)) { + if ((activeSides & static_cast(BottomMask)) && (y + static_cast(h)) == unionBottom) { addBarrier(++id, BarrierSide::Bottom, x, y, w, h); } zones = zones->next;