From 2feea7837a62b16b79ca9ca05015f4c8bc9aa6c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Mon, 22 Aug 2016 19:20:57 +0200 Subject: [PATCH] Warp the xcb pointer whenever pointer leaves an X11 surface Summary: For Xwayland windows we observed that passing pointer focus to another window does not trigger proper leave events on X. Which results in e.g. tooltip windows to show after the pointer moved to a completely different position on a completely different surface. This is a bug in Xwayland which will be fixed in 1.19 (already fixed in master). Given that there is a runtime version check. Although it's fixed in Xwayland master it's worth to carry a workaround. To circumvent this problem KWin warps the xcb pointer to 0/0 whever an X window loses pointer focus. That way the X window gets a proper leave through the X protocol. This created a problem though: when giving focus back to the X window it started to warp the pointer for maximized windows as KWin got pointer motion events through the X11 event filter for positions on the window decoration. These are passed into the screen edge filter which pushes the pointer back and warps our Wayland pointer. To solve this problem KWin no longer performs any actions for pointer motion in the X11 event filter if not on X11. The event filter needs to be reworked and most of it should be moved into the Platform API, if possible. Test Plan: Reproduced situations where one could see that pointer updates don't trigger leave. E.g. going from a highlighted window to the decoration. Reviewers: #kwin, #plasma_on_wayland, bshah Subscribers: plasma-devel, kwin Tags: #plasma_on_wayland, #kwin Differential Revision: https://phabricator.kde.org/D2531 --- autotests/integration/xwayland_input_test.cpp | 1 - events.cpp | 4 +++ pointer_input.cpp | 30 +++++++++++++++++++ pointer_input.h | 9 ++++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/autotests/integration/xwayland_input_test.cpp b/autotests/integration/xwayland_input_test.cpp index 4ffce73c55..7de3cbd878 100644 --- a/autotests/integration/xwayland_input_test.cpp +++ b/autotests/integration/xwayland_input_test.cpp @@ -195,7 +195,6 @@ void XWaylandInputTest::testPointerEnterLeave() // move out of window Cursor::setPos(client->geometry().bottomRight() + QPoint(10, 10)); - QEXPECT_FAIL("", "Xwayland doesn't send leave events when the surface gets a leave", Continue); QVERIFY(leftSpy.wait()); // destroy window again diff --git a/events.cpp b/events.cpp index ae48a93ccc..4976369eca 100644 --- a/events.cpp +++ b/events.cpp @@ -286,6 +286,10 @@ bool Workspace::workspaceEvent(xcb_generic_event_t *e) break; } case XCB_MOTION_NOTIFY: { + if (kwinApp()->operationMode() != Application::OperationModeX11) { + // ignore X11 pointer events generated on X windows if we are not on X + return true; + } auto *mouseEvent = reinterpret_cast(e); const QPoint rootPos(mouseEvent->root_x, mouseEvent->root_y); #ifdef KWIN_BUILD_TABBOX diff --git a/pointer_input.cpp b/pointer_input.cpp index 36f99a18a7..fdbd12c85c 100644 --- a/pointer_input.cpp +++ b/pointer_input.cpp @@ -405,6 +405,7 @@ void PointerInputRedirection::update() if (t && t->surface()) { m_window = QPointer(t); // TODO: add convenient API to update global pos together with updating focused surface + warpXcbOnSurfaceLeft(t->surface()); seat->setFocusedPointerSurface(nullptr); seat->setPointerPos(m_pos.toPoint()); seat->setFocusedPointerSurface(t->surface(), t->inputTransformation()); @@ -427,11 +428,40 @@ void PointerInputRedirection::update() ); } else { m_window.clear(); + warpXcbOnSurfaceLeft(nullptr); seat->setFocusedPointerSurface(nullptr); t = nullptr; } } +void PointerInputRedirection::warpXcbOnSurfaceLeft(KWayland::Server::SurfaceInterface *newSurface) +{ + auto xc = waylandServer()->xWaylandConnection(); + if (!xc) { + // No XWayland, no point in warping the x cursor + return; + } + if (!kwinApp()->x11Connection()) { + return; + } + static bool s_hasXWayland119 = xcb_get_setup(kwinApp()->x11Connection())->release_number >= 11900000; + if (s_hasXWayland119) { + return; + } + if (newSurface && newSurface->client() == xc) { + // new window is an X window + return; + } + auto s = waylandServer()->seat()->focusedPointerSurface(); + if (!s || s->client() != xc) { + // pointer was not on an X window + return; + } + // warp pointer to 0/0 to trigger leave events on previously focused X window + xcb_warp_pointer(connection(), XCB_WINDOW_NONE, rootWindow(), 0, 0, 0, 0, 0, 0), + xcb_flush(connection()); +} + void PointerInputRedirection::updatePosition(const QPointF &pos) { // verify that at least one screen contains the pointer position diff --git a/pointer_input.h b/pointer_input.h index e018d56522..cd730e9acc 100644 --- a/pointer_input.h +++ b/pointer_input.h @@ -29,6 +29,14 @@ along with this program. If not, see . class QWindow; +namespace KWayland +{ +namespace Server +{ +class SurfaceInterface; +} +} + namespace KWin { @@ -122,6 +130,7 @@ public: private: void updatePosition(const QPointF &pos); void updateButton(uint32_t button, InputRedirection::PointerButtonState state); + void warpXcbOnSurfaceLeft(KWayland::Server::SurfaceInterface *surface); CursorImage *m_cursor; bool m_inited = false; bool m_supportsWarping;