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
This commit is contained in:
Martin Gräßlin 2016-08-22 19:20:57 +02:00
parent 455c5c07a0
commit 2feea7837a
4 changed files with 43 additions and 1 deletions

View file

@ -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

View file

@ -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<xcb_motion_notify_event_t*>(e);
const QPoint rootPos(mouseEvent->root_x, mouseEvent->root_y);
#ifdef KWIN_BUILD_TABBOX

View file

@ -405,6 +405,7 @@ void PointerInputRedirection::update()
if (t && t->surface()) {
m_window = QPointer<Toplevel>(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

View file

@ -29,6 +29,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
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;