From e2e6d8b08be04a8a42d909947d7d25c8c54d6e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 15 Apr 2016 16:07:39 +0200 Subject: [PATCH] Warp the cursor pos to the specificed global position in a NET::Move request Summary: The cursor position is the reference KWin uses while moving a window. If we don't warp the cursor position the window "jumps" to the cursor position on first movement. For requests triggered by the client (e.g. widget style) this does not matter as the cursor is at the correct position. But for tools such as task bars we should ensure the cursor is at the right pos. Reviewers: #plasma, hein Subscribers: plasma-devel Projects: #plasma Differential Revision: https://phabricator.kde.org/D1421 --- autotests/wayland/CMakeLists.txt | 2 +- autotests/wayland/move_resize_window_test.cpp | 82 +++++++++++++++++++ events.cpp | 8 +- 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/autotests/wayland/CMakeLists.txt b/autotests/wayland/CMakeLists.txt index a77ee7a67c..f2361af707 100644 --- a/autotests/wayland/CMakeLists.txt +++ b/autotests/wayland/CMakeLists.txt @@ -32,7 +32,7 @@ ecm_mark_as_test(testQuickTiling) ######################################################## set( testMoveResize_SRCS move_resize_window_test.cpp kwin_wayland_test.cpp ) add_executable(testMoveResize ${testMoveResize_SRCS}) -target_link_libraries( testMoveResize kwin Qt5::Test) +target_link_libraries( testMoveResize kwin Qt5::Test XCB::ICCCM) add_test(kwin-testMoveResize testMoveResize) ecm_mark_as_test(testMoveResize) diff --git a/autotests/wayland/move_resize_window_test.cpp b/autotests/wayland/move_resize_window_test.cpp index 4a87ffff3f..a22f7d60aa 100644 --- a/autotests/wayland/move_resize_window_test.cpp +++ b/autotests/wayland/move_resize_window_test.cpp @@ -38,6 +38,7 @@ along with this program. If not, see . #include #include +#include Q_DECLARE_METATYPE(KWin::AbstractClient::QuickTileMode) Q_DECLARE_METATYPE(KWin::MaximizeMode) @@ -65,6 +66,7 @@ private Q_SLOTS: void testPointerMoveEnd(); void testPlasmaShellSurfaceMovable_data(); void testPlasmaShellSurfaceMovable(); + void testNetMove(); private: KWayland::Client::ConnectionThread *m_connection = nullptr; @@ -571,6 +573,86 @@ void MoveResizeWindowTest::testPlasmaShellSurfaceMovable() QTEST(c->isResizable(), "resizable"); } +void MoveResizeWindowTest::testNetMove() +{ + // this test verifies that a move request for an X11 window through NET API works + // create an xcb window + struct XcbConnectionDeleter + { + static inline void cleanup(xcb_connection_t *pointer) + { + xcb_disconnect(pointer); + } + }; + QScopedPointer c(xcb_connect(nullptr, nullptr)); + QVERIFY(!xcb_connection_has_error(c.data())); + + xcb_window_t w = xcb_generate_id(c.data()); + xcb_create_window(c.data(), XCB_COPY_FROM_PARENT, w, rootWindow(), + 0, 0, 100, 100, + 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); + xcb_size_hints_t hints; + memset(&hints, 0, sizeof(hints)); + xcb_icccm_size_hints_set_position(&hints, 1, 0, 0); + xcb_icccm_size_hints_set_size(&hints, 1, 100, 100); + xcb_icccm_set_wm_normal_hints(c.data(), w, &hints); + // let's set a no-border + NETWinInfo winInfo(c.data(), w, rootWindow(), NET::WMWindowType, NET::Properties2()); + winInfo.setWindowType(NET::Override); + xcb_map_window(c.data(), w); + xcb_flush(c.data()); + + QSignalSpy windowCreatedSpy(workspace(), &Workspace::clientAdded); + QVERIFY(windowCreatedSpy.isValid()); + QVERIFY(windowCreatedSpy.wait()); + Client *client = windowCreatedSpy.first().first().value(); + QVERIFY(client); + QCOMPARE(client->window(), w); + const QRect origGeo = client->geometry(); + + // let's move the cursor outside the window + Cursor::setPos(screens()->geometry(0).center()); + QVERIFY(!origGeo.contains(Cursor::pos())); + + QSignalSpy moveStartSpy(client, &Client::clientStartUserMovedResized); + QVERIFY(moveStartSpy.isValid()); + QSignalSpy moveEndSpy(client, &Client::clientFinishUserMovedResized); + QVERIFY(moveEndSpy.isValid()); + QSignalSpy moveStepSpy(client, &Client::clientStepUserMovedResized); + QVERIFY(moveStepSpy.isValid()); + + // use NETRootInfo to trigger a move request + NETRootInfo root(c.data(), NET::Properties()); + root.moveResizeRequest(w, origGeo.center().x(), origGeo.center().y(), NET::Move); + xcb_flush(c.data()); + + QVERIFY(moveStartSpy.wait()); + QCOMPARE(workspace()->getMovingClient(), client); + QVERIFY(client->isMove()); + QCOMPARE(client->geometryRestore(), origGeo); + QCOMPARE(Cursor::pos(), origGeo.center()); + + // let's move a step + Cursor::setPos(Cursor::pos() + QPoint(10, 10)); + QCOMPARE(moveStepSpy.count(), 1); + QCOMPARE(moveStepSpy.first().last().toRect(), origGeo.translated(10, 10)); + + // let's cancel the move resize again through the net API + root.moveResizeRequest(w, client->geometry().center().x(), client->geometry().center().y(), NET::MoveResizeCancel); + xcb_flush(c.data()); + QVERIFY(moveEndSpy.wait()); + + // and destroy the window again + xcb_unmap_window(c.data(), w); + xcb_destroy_window(c.data(), w); + xcb_flush(c.data()); + c.reset(); + + QSignalSpy windowClosedSpy(client, &Client::windowClosed); + QVERIFY(windowClosedSpy.isValid()); + QVERIFY(windowClosedSpy.wait()); +} + } WAYLANDTEST_MAIN(KWin::MoveResizeWindowTest) diff --git a/events.cpp b/events.cpp index 61462abbe9..3528fb5f8f 100644 --- a/events.cpp +++ b/events.cpp @@ -1340,9 +1340,13 @@ void Client::focusOutEvent(xcb_focus_out_event_t *e) // performs _NET_WM_MOVERESIZE void Client::NETMoveResize(int x_root, int y_root, NET::Direction direction) { - if (direction == NET::Move) + if (direction == NET::Move) { + // move cursor to the provided position to prevent the window jumping there on first movement + // the expectation is that the cursor is already at the provided position, + // thus it's more a safety measurement + Cursor::setPos(QPoint(x_root, y_root)); performMouseCommand(Options::MouseMove, QPoint(x_root, y_root)); - else if (isMoveResize() && direction == NET::MoveResizeCancel) { + } else if (isMoveResize() && direction == NET::MoveResizeCancel) { finishMoveResize(true); setMoveResizePointerButtonDown(false); updateCursor();