diff --git a/autotests/client/test_wayland_registry.cpp b/autotests/client/test_wayland_registry.cpp --- a/autotests/client/test_wayland_registry.cpp +++ b/autotests/client/test_wayland_registry.cpp @@ -26,6 +26,7 @@ #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/output.h" +#include "../../src/client/pointergestures.h" #include "../../src/client/seat.h" #include "../../src/client/relativepointer.h" #include "../../src/client/server_decoration.h" @@ -46,6 +47,7 @@ #include "../../src/server/subcompositor_interface.h" #include "../../src/server/outputmanagement_interface.h" #include "../../src/server/outputdevice_interface.h" +#include "../../src/server/pointergestures_interface.h" #include "../../src/server/textinput_interface.h" #include "../../src/server/xdgshell_interface.h" #include "../../src/server/relativepointer_interface.h" @@ -57,6 +59,7 @@ #include #include #include +#include class TestWaylandRegistry : public QObject { @@ -84,6 +87,7 @@ void testBindTextInputManagerUnstableV2(); void testBindXdgShellUnstableV5(); void testBindRelativePointerManagerUnstableV1(); + void testBindPointerGesturesUnstableV1(); void testGlobalSync(); void testGlobalSyncThreaded(); void testRemoval(); @@ -106,6 +110,7 @@ KWayland::Server::TextInputManagerInterface *m_textInputManagerV2; KWayland::Server::XdgShellInterface *m_xdgShellUnstableV5; KWayland::Server::RelativePointerManagerInterface *m_relativePointerV1; + KWayland::Server::PointerGesturesInterface *m_pointerGesturesV1; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-registry-0"); @@ -126,6 +131,7 @@ , m_textInputManagerV2(nullptr) , m_xdgShellUnstableV5(nullptr) , m_relativePointerV1(nullptr) + , m_pointerGesturesV1(nullptr) { } @@ -170,6 +176,9 @@ m_relativePointerV1 = m_display->createRelativePointerManager(KWayland::Server::RelativePointerInterfaceVersion::UnstableV1); m_relativePointerV1->create(); QCOMPARE(m_relativePointerV1->interfaceVersion(), KWayland::Server::RelativePointerInterfaceVersion::UnstableV1); + m_pointerGesturesV1 = m_display->createPointerGestures(KWayland::Server::PointerGesturesInterfaceVersion::UnstableV1); + m_pointerGesturesV1->create(); + QCOMPARE(m_pointerGesturesV1->interfaceVersion(), KWayland::Server::PointerGesturesInterfaceVersion::UnstableV1); } void TestWaylandRegistry::cleanup() @@ -317,6 +326,11 @@ TEST_BIND(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1, SIGNAL(relativePointerManagerUnstableV1Announced(quint32,quint32)), bindRelativePointerManagerUnstableV1, zwp_relative_pointer_manager_v1_destroy) } +void TestWaylandRegistry::testBindPointerGesturesUnstableV1() +{ + TEST_BIND(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1, SIGNAL(pointerGesturesUnstableV1Announced(quint32,quint32)), bindPointerGesturesUnstableV1, zwp_pointer_gestures_v1_destroy) +} + #undef TEST_BIND void TestWaylandRegistry::testRemoval() diff --git a/autotests/client/test_wayland_seat.cpp b/autotests/client/test_wayland_seat.cpp --- a/autotests/client/test_wayland_seat.cpp +++ b/autotests/client/test_wayland_seat.cpp @@ -28,6 +28,7 @@ #include "../../src/client/event_queue.h" #include "../../src/client/keyboard.h" #include "../../src/client/pointer.h" +#include "../../src/client/pointergestures.h" #include "../../src/client/surface.h" #include "../../src/client/registry.h" #include "../../src/client/relativepointer.h" @@ -42,6 +43,7 @@ #include "../../src/server/display.h" #include "../../src/server/keyboard_interface.h" #include "../../src/server/pointer_interface.h" +#include "../../src/server/pointergestures_interface.h" #include "../../src/server/relativepointer_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/subcompositor_interface.h" @@ -72,6 +74,10 @@ void testPointerButton_data(); void testPointerButton(); void testPointerSubSurfaceTree(); + void testPointerSwipeGesture_data(); + void testPointerSwipeGesture(); + void testPointerPinchGesture_data(); + void testPointerPinchGesture(); void testKeyboardSubSurfaceTreeFromPointer(); void testCursor(); void testCursorDamage(); @@ -92,12 +98,14 @@ KWayland::Server::SeatInterface *m_seatInterface; KWayland::Server::SubCompositorInterface *m_subCompositorInterface; KWayland::Server::RelativePointerManagerInterface *m_relativePointerManagerInterface; + KWayland::Server::PointerGesturesInterface *m_pointerGesturesInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::Seat *m_seat; KWayland::Client::ShmPool *m_shm; KWayland::Client::SubCompositor * m_subCompositor; KWayland::Client::RelativePointerManager *m_relativePointerManager; + KWayland::Client::PointerGestures *m_pointerGestures; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; @@ -111,12 +119,14 @@ , m_seatInterface(nullptr) , m_subCompositorInterface(nullptr) , m_relativePointerManagerInterface(nullptr) + , m_pointerGesturesInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_seat(nullptr) , m_shm(nullptr) , m_subCompositor(nullptr) , m_relativePointerManager(nullptr) + , m_pointerGestures(nullptr) , m_queue(nullptr) , m_thread(nullptr) { @@ -147,6 +157,11 @@ m_relativePointerManagerInterface->create(); QVERIFY(m_relativePointerManagerInterface->isValid()); + m_pointerGesturesInterface = m_display->createPointerGestures(PointerGesturesInterfaceVersion::UnstableV1, m_display); + QVERIFY(m_pointerGesturesInterface); + m_pointerGesturesInterface->create(); + QVERIFY(m_pointerGesturesInterface->isValid()); + // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); @@ -200,10 +215,19 @@ registry.interface(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1).version, this); QVERIFY(m_relativePointerManager->isValid()); + + m_pointerGestures = registry.createPointerGestures(registry.interface(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1).name, + registry.interface(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1).version, + this); + QVERIFY(m_pointerGestures->isValid()); } void TestWaylandSeat::cleanup() { + if (m_pointerGestures) { + delete m_pointerGestures; + m_pointerGestures = nullptr; + } if (m_relativePointerManager) { delete m_relativePointerManager; m_relativePointerManager = nullptr; @@ -251,6 +275,9 @@ delete m_relativePointerManagerInterface; m_relativePointerManagerInterface = nullptr; + delete m_pointerGesturesInterface; + m_pointerGesturesInterface = nullptr; + delete m_display; m_display = nullptr; } @@ -871,6 +898,214 @@ QCOMPARE(pointer->enteredSurface(), parentSurface.data()); } +void TestWaylandSeat::testPointerSwipeGesture_data() +{ + QTest::addColumn("cancel"); + QTest::addColumn("expectedEndCount"); + QTest::addColumn("expectedCancelCount"); + + QTest::newRow("end") << false << 1 << 0; + QTest::newRow("cancel") << true << 0 << 1; +} + +void TestWaylandSeat::testPointerSwipeGesture() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + + // first create the pointer and pointer swipe gesture + QSignalSpy hasPointerChangedSpy(m_seat, &Seat::hasPointerChanged); + QVERIFY(hasPointerChangedSpy.isValid()); + m_seatInterface->setHasPointer(true); + QVERIFY(hasPointerChangedSpy.wait()); + QScopedPointer pointer(m_seat->createPointer()); + QScopedPointer gesture(m_pointerGestures->createSwipeGesture(pointer.data())); + QVERIFY(gesture); + QVERIFY(gesture->isValid()); + QVERIFY(gesture->surface().isNull()); + QCOMPARE(gesture->fingerCount(), 0u); + + QSignalSpy startSpy(gesture.data(), &PointerSwipeGesture::started); + QVERIFY(startSpy.isValid()); + QSignalSpy updateSpy(gesture.data(), &PointerSwipeGesture::updated); + QVERIFY(updateSpy.isValid()); + QSignalSpy endSpy(gesture.data(), &PointerSwipeGesture::ended); + QVERIFY(endSpy.isValid()); + QSignalSpy cancelledSpy(gesture.data(), &PointerSwipeGesture::cancelled); + QVERIFY(cancelledSpy.isValid()); + + // now create a surface + QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); + QVERIFY(surfaceCreatedSpy.isValid()); + QScopedPointer surface(m_compositor->createSurface()); + QVERIFY(surfaceCreatedSpy.wait()); + auto serverSurface = surfaceCreatedSpy.first().first().value(); + QVERIFY(serverSurface); + m_seatInterface->setFocusedPointerSurface(serverSurface); + QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); + QVERIFY(m_seatInterface->focusedPointer()); + + // send in the start + quint32 timestamp = 1; + m_seatInterface->setTimestamp(timestamp++); + m_seatInterface->startPointerSwipeGesture(2); + QVERIFY(startSpy.wait()); + QCOMPARE(startSpy.count(), 1); + QCOMPARE(startSpy.first().at(0).value(), m_display->serial()); + QCOMPARE(startSpy.first().at(1).value(), 1u); + QCOMPARE(gesture->fingerCount(), 2u); + QCOMPARE(gesture->surface().data(), surface.data()); + + // another start should not be possible + m_seatInterface->startPointerSwipeGesture(2); + QVERIFY(!startSpy.wait()); + + // send in some updates + m_seatInterface->setTimestamp(timestamp++); + m_seatInterface->updatePointerSwipeGesture(QSizeF(2, 3)); + QVERIFY(updateSpy.wait()); + m_seatInterface->setTimestamp(timestamp++); + m_seatInterface->updatePointerSwipeGesture(QSizeF(4, 5)); + QVERIFY(updateSpy.wait()); + QCOMPARE(updateSpy.count(), 2); + QCOMPARE(updateSpy.at(0).at(0).toSizeF(), QSizeF(2, 3)); + QCOMPARE(updateSpy.at(0).at(1).value(), 2u); + QCOMPARE(updateSpy.at(1).at(0).toSizeF(), QSizeF(4, 5)); + QCOMPARE(updateSpy.at(1).at(1).value(), 3u); + + // now end or cancel + QFETCH(bool, cancel); + QSignalSpy *spy; + m_seatInterface->setTimestamp(timestamp++); + if (cancel) { + m_seatInterface->cancelPointerSwipeGesture(); + spy = &cancelledSpy; + } else { + m_seatInterface->endPointerSwipeGesture(); + spy = &endSpy; + } + QVERIFY(spy->wait()); + QTEST(endSpy.count(), "expectedEndCount"); + QTEST(cancelledSpy.count(), "expectedCancelCount"); + QCOMPARE(spy->count(), 1); + QCOMPARE(spy->first().at(0).value(), m_display->serial()); + QCOMPARE(spy->first().at(1).value(), 4u); + + QCOMPARE(gesture->fingerCount(), 0u); + QVERIFY(gesture->surface().isNull()); + + // now a start should be possible again + m_seatInterface->setTimestamp(timestamp++); + m_seatInterface->startPointerSwipeGesture(2); + QVERIFY(startSpy.wait()); +} + +void TestWaylandSeat::testPointerPinchGesture_data() +{ + QTest::addColumn("cancel"); + QTest::addColumn("expectedEndCount"); + QTest::addColumn("expectedCancelCount"); + + QTest::newRow("end") << false << 1 << 0; + QTest::newRow("cancel") << true << 0 << 1; +} + +void TestWaylandSeat::testPointerPinchGesture() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + + // first create the pointer and pointer swipe gesture + QSignalSpy hasPointerChangedSpy(m_seat, &Seat::hasPointerChanged); + QVERIFY(hasPointerChangedSpy.isValid()); + m_seatInterface->setHasPointer(true); + QVERIFY(hasPointerChangedSpy.wait()); + QScopedPointer pointer(m_seat->createPointer()); + QScopedPointer gesture(m_pointerGestures->createPinchGesture(pointer.data())); + QVERIFY(gesture); + QVERIFY(gesture->isValid()); + QVERIFY(gesture->surface().isNull()); + QCOMPARE(gesture->fingerCount(), 0u); + + QSignalSpy startSpy(gesture.data(), &PointerPinchGesture::started); + QVERIFY(startSpy.isValid()); + QSignalSpy updateSpy(gesture.data(), &PointerPinchGesture::updated); + QVERIFY(updateSpy.isValid()); + QSignalSpy endSpy(gesture.data(), &PointerPinchGesture::ended); + QVERIFY(endSpy.isValid()); + QSignalSpy cancelledSpy(gesture.data(), &PointerPinchGesture::cancelled); + QVERIFY(cancelledSpy.isValid()); + + // now create a surface + QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); + QVERIFY(surfaceCreatedSpy.isValid()); + QScopedPointer surface(m_compositor->createSurface()); + QVERIFY(surfaceCreatedSpy.wait()); + auto serverSurface = surfaceCreatedSpy.first().first().value(); + QVERIFY(serverSurface); + m_seatInterface->setFocusedPointerSurface(serverSurface); + QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); + QVERIFY(m_seatInterface->focusedPointer()); + + // send in the start + quint32 timestamp = 1; + m_seatInterface->setTimestamp(timestamp++); + m_seatInterface->startPointerPinchGesture(3); + QVERIFY(startSpy.wait()); + QCOMPARE(startSpy.count(), 1); + QCOMPARE(startSpy.first().at(0).value(), m_display->serial()); + QCOMPARE(startSpy.first().at(1).value(), 1u); + QCOMPARE(gesture->fingerCount(), 3u); + QCOMPARE(gesture->surface().data(), surface.data()); + + // another start should not be possible + m_seatInterface->startPointerPinchGesture(3); + QVERIFY(!startSpy.wait()); + + // send in some updates + m_seatInterface->setTimestamp(timestamp++); + m_seatInterface->updatePointerPinchGesture(QSizeF(2, 3), 2, 45); + QVERIFY(updateSpy.wait()); + m_seatInterface->setTimestamp(timestamp++); + m_seatInterface->updatePointerPinchGesture(QSizeF(4, 5), 1, 90); + QVERIFY(updateSpy.wait()); + QCOMPARE(updateSpy.count(), 2); + QCOMPARE(updateSpy.at(0).at(0).toSizeF(), QSizeF(2, 3)); + QCOMPARE(updateSpy.at(0).at(1).value(), 2u); + QCOMPARE(updateSpy.at(0).at(2).value(), 45u); + QCOMPARE(updateSpy.at(0).at(3).value(), 2u); + QCOMPARE(updateSpy.at(1).at(0).toSizeF(), QSizeF(4, 5)); + QCOMPARE(updateSpy.at(1).at(1).value(), 1u); + QCOMPARE(updateSpy.at(1).at(2).value(), 90u); + QCOMPARE(updateSpy.at(1).at(3).value(), 3u); + + // now end or cancel + QFETCH(bool, cancel); + QSignalSpy *spy; + m_seatInterface->setTimestamp(timestamp++); + if (cancel) { + m_seatInterface->cancelPointerPinchGesture(); + spy = &cancelledSpy; + } else { + m_seatInterface->endPointerPinchGesture(); + spy = &endSpy; + } + QVERIFY(spy->wait()); + QTEST(endSpy.count(), "expectedEndCount"); + QTEST(cancelledSpy.count(), "expectedCancelCount"); + QCOMPARE(spy->count(), 1); + QCOMPARE(spy->first().at(0).value(), m_display->serial()); + QCOMPARE(spy->first().at(1).value(), 4u); + + QCOMPARE(gesture->fingerCount(), 0u); + QVERIFY(gesture->surface().isNull()); + + // now a start should be possible again + m_seatInterface->setTimestamp(timestamp++); + m_seatInterface->startPointerPinchGesture(3); + QVERIFY(startSpy.wait()); +} + void TestWaylandSeat::testKeyboardSubSurfaceTreeFromPointer() { // this test verifies that when clicking on a sub-surface the keyboard focus passes to it @@ -1419,6 +1654,7 @@ connect(m_connection, &ConnectionThread::connectionDied, m_shm, &ShmPool::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_subCompositor, &SubCompositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_relativePointerManager, &RelativePointerManager::destroy); + connect(m_connection, &ConnectionThread::connectionDied, m_pointerGestures, &PointerGestures::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); QVERIFY(m_seat->isValid()); @@ -1430,6 +1666,7 @@ m_seatInterface = nullptr; m_subCompositorInterface = nullptr; m_relativePointerManagerInterface = nullptr; + m_pointerGesturesInterface = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the seat should be destroyed; @@ -1977,6 +2214,7 @@ pointer->destroy(); touch->destroy(); m_relativePointerManager->destroy(); + m_pointerGestures->destroy(); m_compositor->destroy(); m_seat->destroy(); m_shm->destroy(); diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -28,6 +28,7 @@ logging.cpp output.cpp pointer.cpp + pointergestures.cpp plasmashell.cpp plasmawindowmanagement.cpp plasmawindowmodel.cpp @@ -128,6 +129,10 @@ PROTOCOL ${KWAYLAND_SOURCE_DIR}/src/client/protocols/relative-pointer-unstable-v1.xml BASENAME relativepointer-unstable-v1 ) +ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS + PROTOCOL ${KWAYLAND_SOURCE_DIR}/src/client/protocols/pointer-gestures-unstable-v1.xml + BASENAME pointer-gestures-unstable-v1 +) add_library(KF5WaylandClient ${CLIENT_LIB_SRCS}) generate_export_header(KF5WaylandClient @@ -184,6 +189,7 @@ plasmashell.h plasmawindowmanagement.h plasmawindowmodel.h + pointergestures.h region.h registry.h relativepointer.h diff --git a/src/client/pointergestures.h b/src/client/pointergestures.h new file mode 100644 --- /dev/null +++ b/src/client/pointergestures.h @@ -0,0 +1,416 @@ +/**************************************************************************** +Copyright 2016 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_CLIENT_POINTERGESTURES_H +#define KWAYLAND_CLIENT_POINTERGESTURES_H + +#include +#include + +#include + +struct zwp_pointer_gestures_v1; +struct zwp_pointer_gesture_swipe_v1; +struct zwp_pointer_gesture_pinch_v1; + +class QSizeF; + +namespace KWayland +{ +namespace Client +{ + +class EventQueue; +class PointerPinchGesture; +class Pointer; +class Surface; +class PointerSwipeGesture; + +/** + * @short Wrapper for the zwp_pointer_gestures_v1 interface. + * + * This class provides a convenient wrapper for the zwp_pointer_gestures_v1 interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the PointerGestures interface: + * @code + * PointerGestures *c = registry->createPointerGestures(name, version); + * @endcode + * + * This creates the PointerGestures and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * PointerGestures *c = new PointerGestures; + * c->setup(registry->bindPointerGestures(name, version)); + * @endcode + * + * The PointerGestures can be used as a drop-in replacement for any zwp_pointer_gestures_v1 + * pointer as it provides matching cast operators. + * + * @see Registry + * @see PointerSwipeGesture + * @see PointerPinchGesture + * @since 5.28 + **/ +class KWAYLANDCLIENT_EXPORT PointerGestures : public QObject +{ + Q_OBJECT +public: + /** + * Creates a new PointerGestures. + * Note: after constructing the PointerGestures it is not yet valid and one needs + * to call setup. In order to get a ready to use PointerGestures prefer using + * Registry::createPointerGestures. + **/ + explicit PointerGestures(QObject *parent = nullptr); + virtual ~PointerGestures(); + + /** + * Setup this PointerGestures to manage the @p pointergestures. + * When using Registry::createPointerGestures there is no need to call this + * method. + **/ + void setup(zwp_pointer_gestures_v1 *pointergestures); + /** + * @returns @c true if managing a zwp_pointer_gestures_v1. + **/ + bool isValid() const; + /** + * Releases the zwp_pointer_gestures_v1 interface. + * After the interface has been released the PointerGestures instance is no + * longer valid and can be setup with another zwp_pointer_gestures_v1 interface. + **/ + void release(); + /** + * Destroys the data held by this PointerGestures. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new zwp_pointer_gestures_v1 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, pointergestures, &PointerGestures::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the @p queue to use for creating objects with this PointerGestures. + **/ + void setEventQueue(EventQueue *queue); + /** + * @returns The event queue to use for creating objects with this PointerGestures. + **/ + EventQueue *eventQueue(); + + /** + * Creates a PointerSwipeGesture for the given @p pointer with the @p parent. + **/ + PointerSwipeGesture *createSwipeGesture(Pointer *pointer, QObject *parent = nullptr); + + /** + * Creates a PointerPinchGesture for the given @p pointer with the @p parent. + **/ + PointerPinchGesture *createPinchGesture(Pointer *pointer, QObject *parent = nullptr); + + operator zwp_pointer_gestures_v1*(); + operator zwp_pointer_gestures_v1*() const; + +Q_SIGNALS: + /** + * The corresponding global for this interface on the Registry got removed. + * + * This signal gets only emitted if the PointerGestures got created by + * Registry::createPointerGestures + **/ + void removed(); + +private: + class Private; + QScopedPointer d; +}; + +/** + * This class is a wrapper for the zwp_pointer_gesture_swipe_v1 protocol. + * + * A PointerSwipeGesture object notifies a client about a multi-finger swipe + * gesture detected on an indirect input device such as a touchpad. + * The gesture is usually initiated by multiple fingers moving in the + * same direction but once initiated the direction may change. + * The precise conditions of when such a gesture is detected are + * implementation-dependent. + * + * A gesture consists of three stages: begin, update (optional) and end. + * There cannot be multiple simultaneous pinch or swipe gestures on a + * same pointer/seat, how compositors prevent these situations is + * implementation-dependent. + * + * A gesture may be cancelled by the compositor or the hardware. + * Clients should not consider performing permanent or irreversible + * actions until the end of a gesture has been received. + * + * @see PointerGestures + * @see PointerPinchGesture + * @since 5.28 + **/ +class KWAYLANDCLIENT_EXPORT PointerSwipeGesture : public QObject +{ + Q_OBJECT +public: + virtual ~PointerSwipeGesture(); + + /** + * Setup this PointerSwipeGesture to manage the @p pointerswipegesture. + * When using PointerGestures::createPointerSwipeGesture there is no need to call this + * method. + **/ + void setup(zwp_pointer_gesture_swipe_v1 *pointerswipegesture); + /** + * @returns @c true if managing a zwp_pointer_gesture_swipe_v1. + **/ + bool isValid() const; + /** + * Releases the zwp_pointer_gesture_swipe_v1 interface. + * After the interface has been released the PointerSwipeGesture instance is no + * longer valid and can be setup with another zwp_pointer_gesture_swipe_v1 interface. + **/ + void release(); + /** + * Destroys the data held by this PointerSwipeGesture. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new zwp_pointer_gesture_swipe_v1 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, pointerswipegesture, &PointerSwipeGesture::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * The number of fingers taking part in this gesture. + * If no gesture in progress @c 0 is returned. + **/ + quint32 fingerCount() const; + + /** + * The Surface on which this gesture is performed. + * If no gesture is progress the returned pointer is null. + **/ + QPointer surface() const; + + operator zwp_pointer_gesture_swipe_v1*(); + operator zwp_pointer_gesture_swipe_v1*() const; + +Q_SIGNALS: + /** + * A gesture got started. + * @param serial Unique serial for this start gesture event. + * @param time Timestamp in milliseconds granularity + * @see updated + * @see ended + * @see cancelled + **/ + void started(quint32 serial, quint32 time); + + /** + * A gesture got updated. + * @param delta relative coordinates of the logical center of the gesture compared to the previous event + * @param time Timestamp in milliseconds granularity + * @see started + * @see ended + * @see cancelled + **/ + void updated(const QSizeF &delta, quint32 time); + + /** + * A gesture ended. + * + * @param serial Unique serial for this end gesture event. + * @param time Timestamp in milliseconds granularity + * @see started + * @see updated + * @see cancelled + **/ + void ended(quint32 serial, quint32 time); + + /** + * A gesture got cancelled by the Wayland compositor. + * + * @param serial Unique serial for this cancel gesture event. + * @param time Timestamp in milliseconds granularity + * @see started + * @see updated + * @see ended + **/ + void cancelled(quint32 serial, quint32 time); + +private: + friend class PointerGestures; + explicit PointerSwipeGesture(QObject *parent = nullptr); + class Private; + QScopedPointer d; +}; + +/** + * This class is a wrapper for the zwp_pointer_gesture_pinch_v1 protocol. + * + * A PointerPinchGesture object notifies a client about a multi-finger pinch + * gesture detected on an indirect input device such as a touchpad. + * The gesture is usually initiated by multiple fingers moving towards + * each other or away from each other, or by two or more fingers rotating + * around a logical center of gravity. The precise conditions of when + * such a gesture is detected are implementation-dependent. + * + * A gesture consists of three stages: begin, update (optional) and end. + * There cannot be multiple simultaneous pinch or swipe gestures on a + * same pointer/seat, how compositors prevent these situations is + * implementation-dependent. + * + * A gesture may be cancelled by the compositor or the hardware. + * Clients should not consider performing permanent or irreversible + * actions until the end of a gesture has been received. + * + * @see PointerGestures + * @see PointerSwipeGesture + * @since 5.28 + **/ +class KWAYLANDCLIENT_EXPORT PointerPinchGesture : public QObject +{ + Q_OBJECT +public: + virtual ~PointerPinchGesture(); + + /** + * Setup this PointerPinchGesture to manage the @p pointerpinchgesture. + * When using PointerGestures::createPointerPinchGesture there is no need to call this + * method. + **/ + void setup(zwp_pointer_gesture_pinch_v1 *pointerpinchgesture); + /** + * @returns @c true if managing a zwp_pointer_gesture_pinch_v1. + **/ + bool isValid() const; + /** + * Releases the zwp_pointer_gesture_pinch_v1 interface. + * After the interface has been released the PointerPinchGesture instance is no + * longer valid and can be setup with another zwp_pointer_gesture_pinch_v1 interface. + **/ + void release(); + /** + * Destroys the data held by this PointerPinchGesture. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new zwp_pointer_gesture_pinch_v1 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, pointerpinchgesture, &PointerPinchGesture::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * The number of fingers taking part in this gesture. + * If no gesture in progress @c 0 is returned. + **/ + quint32 fingerCount() const; + + /** + * The Surface on which this gesture is performed. + * If no gesture is progress the returned pointer is null. + **/ + QPointer surface() const; + + operator zwp_pointer_gesture_pinch_v1*(); + operator zwp_pointer_gesture_pinch_v1*() const; + +Q_SIGNALS: + /** + * A gesture got started. + * @param serial Unique serial for this start gesture event. + * @param time Timestamp in milliseconds granularity + * @see updated + * @see ended + * @see cancelled + **/ + void started(quint32 serial, quint32 time); + + /** + * A gesture got updated. + * @param delta relative coordinates of the logical center of the gesture compared to the previous event + * @param scale an absolute scale compared to the start + * @param rotation relative angle in degrees clockwise compared to the previous start or update event. + * @param time Timestamp in milliseconds granularity + * @see started + * @see ended + * @see cancelled + **/ + void updated(const QSizeF &delta, qreal scale, qreal rotation, quint32 time); + + /** + * A gesture ended. + * + * @param serial Unique serial for this end gesture event. + * @param time Timestamp in milliseconds granularity + * @see started + * @see updated + * @see cancelled + **/ + void ended(quint32 serial, quint32 time); + + /** + * A gesture got cancelled by the Wayland compositor. + * + * @param serial Unique serial for this cancel gesture event. + * @param time Timestamp in milliseconds granularity + * @see started + * @see updated + * @see ended + **/ + void cancelled(quint32 serial, quint32 time); + +private: + friend class PointerGestures; + explicit PointerPinchGesture(QObject *parent = nullptr); + class Private; + QScopedPointer d; +}; + + +} +} + +#endif diff --git a/src/client/pointergestures.cpp b/src/client/pointergestures.cpp new file mode 100644 --- /dev/null +++ b/src/client/pointergestures.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** +Copyright 2016 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "pointergestures.h" +#include "pointer.h" +#include "event_queue.h" +#include "surface.h" +#include "wayland_pointer_p.h" + +#include + +#include + +namespace KWayland +{ +namespace Client +{ + +class PointerGestures::Private +{ +public: + Private() = default; + + WaylandPointer pointergestures; + EventQueue *queue = nullptr; +}; + +PointerGestures::PointerGestures(QObject *parent) + : QObject(parent) + , d(new Private) +{ +} + +PointerGestures::~PointerGestures() +{ + release(); +} + +void PointerGestures::setup(zwp_pointer_gestures_v1 *pointergestures) +{ + Q_ASSERT(pointergestures); + Q_ASSERT(!d->pointergestures); + d->pointergestures.setup(pointergestures); +} + +void PointerGestures::release() +{ + d->pointergestures.release(); +} + +void PointerGestures::destroy() +{ + d->pointergestures.destroy(); +} + +PointerGestures::operator zwp_pointer_gestures_v1*() { + return d->pointergestures; +} + +PointerGestures::operator zwp_pointer_gestures_v1*() const { + return d->pointergestures; +} + +bool PointerGestures::isValid() const +{ + return d->pointergestures.isValid(); +} + +void PointerGestures::setEventQueue(EventQueue *queue) +{ + d->queue = queue; +} + +EventQueue *PointerGestures::eventQueue() +{ + return d->queue; +} + +PointerSwipeGesture *PointerGestures::createSwipeGesture(Pointer *pointer, QObject *parent) +{ + Q_ASSERT(isValid()); + PointerSwipeGesture *p = new PointerSwipeGesture(parent); + auto w = zwp_pointer_gestures_v1_get_swipe_gesture(d->pointergestures, *pointer); + if (d->queue) { + d->queue->addProxy(w); + } + p->setup(w); + return p; +} + +PointerPinchGesture *PointerGestures::createPinchGesture(Pointer *pointer, QObject *parent) +{ + Q_ASSERT(isValid()); + PointerPinchGesture *p = new PointerPinchGesture(parent); + auto w = zwp_pointer_gestures_v1_get_pinch_gesture(d->pointergestures, *pointer); + if (d->queue) { + d->queue->addProxy(w); + } + p->setup(w); + return p; +} + +class PointerSwipeGesture::Private +{ +public: + Private(PointerSwipeGesture *q); + + void setup(zwp_pointer_gesture_swipe_v1 *pg); + + WaylandPointer pointerswipegesture; + quint32 fingerCount = 0; + QPointer surface; + +private: + static void beginCallback(void *data, zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t serial, uint32_t time, wl_surface *surface, uint32_t fingers); + static void updateCallback(void *data, zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy); + static void endCallback(void *data, zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t serial, uint32_t time, int32_t cancelled); + + PointerSwipeGesture *q; + static const zwp_pointer_gesture_swipe_v1_listener s_listener; +}; + +const zwp_pointer_gesture_swipe_v1_listener PointerSwipeGesture::Private::s_listener = { + beginCallback, + updateCallback, + endCallback +}; + +void PointerSwipeGesture::Private::beginCallback(void *data, zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t serial, uint32_t time, wl_surface *surface, uint32_t fingers) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->pointerswipegesture == zwp_pointer_gesture_swipe_v1); + p->fingerCount = fingers; + p->surface = QPointer(Surface::get(surface)); + emit p->q->started(serial, time); +} + +void PointerSwipeGesture::Private::updateCallback(void *data, zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->pointerswipegesture == zwp_pointer_gesture_swipe_v1); + emit p->q->updated(QSizeF(wl_fixed_to_double(dx), wl_fixed_to_double(dy)), time); +} + +void PointerSwipeGesture::Private::endCallback(void *data, zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t serial, uint32_t time, int32_t cancelled) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->pointerswipegesture == zwp_pointer_gesture_swipe_v1); + if (cancelled) { + emit p->q->cancelled(serial, time); + } else { + emit p->q->ended(serial, time); + } + p->fingerCount = 0; + p->surface.clear(); +} + +PointerSwipeGesture::Private::Private(PointerSwipeGesture *q) + : q(q) +{ +} + +PointerSwipeGesture::PointerSwipeGesture(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +PointerSwipeGesture::~PointerSwipeGesture() +{ + release(); +} + +quint32 PointerSwipeGesture::fingerCount() const +{ + return d->fingerCount; +} + +QPointer PointerSwipeGesture::surface() const +{ + return d->surface; +} + +void PointerSwipeGesture::Private::setup(zwp_pointer_gesture_swipe_v1 *pg) +{ + Q_ASSERT(pg); + Q_ASSERT(!pointerswipegesture); + pointerswipegesture.setup(pg); + zwp_pointer_gesture_swipe_v1_add_listener(pointerswipegesture, &s_listener, this); +} + +void PointerSwipeGesture::setup(zwp_pointer_gesture_swipe_v1 *pointerswipegesture) +{ + d->setup(pointerswipegesture); +} + +void PointerSwipeGesture::release() +{ + d->pointerswipegesture.release(); +} + +void PointerSwipeGesture::destroy() +{ + d->pointerswipegesture.destroy(); +} + +PointerSwipeGesture::operator zwp_pointer_gesture_swipe_v1*() { + return d->pointerswipegesture; +} + +PointerSwipeGesture::operator zwp_pointer_gesture_swipe_v1*() const { + return d->pointerswipegesture; +} + +bool PointerSwipeGesture::isValid() const +{ + return d->pointerswipegesture.isValid(); +} + +class PointerPinchGesture::Private +{ +public: + Private(PointerPinchGesture *q); + + void setup(zwp_pointer_gesture_pinch_v1 *pg); + + WaylandPointer pointerpinchgesture; + quint32 fingerCount = 0; + QPointer surface; + +private: + static void beginCallback(void *data, zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, wl_surface *surface, uint32_t fingers); + static void updateCallback(void *data, zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation); + static void endCallback(void *data, zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled); + + PointerPinchGesture *q; + static const zwp_pointer_gesture_pinch_v1_listener s_listener; +}; + +const zwp_pointer_gesture_pinch_v1_listener PointerPinchGesture::Private::s_listener = { + beginCallback, + updateCallback, + endCallback +}; + +void PointerPinchGesture::Private::beginCallback(void *data, zwp_pointer_gesture_pinch_v1 *pg, uint32_t serial, uint32_t time, wl_surface *surface, uint32_t fingers) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->pointerpinchgesture == pg); + p->fingerCount = fingers; + p->surface = QPointer(Surface::get(surface)); + emit p->q->started(serial, time); +} + +void PointerPinchGesture::Private::updateCallback(void *data, zwp_pointer_gesture_pinch_v1 *pg, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->pointerpinchgesture == pg); + emit p->q->updated(QSizeF(wl_fixed_to_double(dx), wl_fixed_to_double(dy)), wl_fixed_to_double(scale), wl_fixed_to_double(rotation), time); +} + +void PointerPinchGesture::Private::endCallback(void *data, zwp_pointer_gesture_pinch_v1 *pg, uint32_t serial, uint32_t time, int32_t cancelled) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->pointerpinchgesture == pg); + if (cancelled) { + emit p->q->cancelled(serial, time); + } else { + emit p->q->ended(serial, time); + } + p->fingerCount = 0; + p->surface.clear(); +} + +PointerPinchGesture::Private::Private(PointerPinchGesture *q) + : q(q) +{ +} + +PointerPinchGesture::PointerPinchGesture(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +PointerPinchGesture::~PointerPinchGesture() +{ + release(); +} + +void PointerPinchGesture::Private::setup(zwp_pointer_gesture_pinch_v1 *pg) +{ + Q_ASSERT(pg); + Q_ASSERT(!pointerpinchgesture); + pointerpinchgesture.setup(pg); + zwp_pointer_gesture_pinch_v1_add_listener(pointerpinchgesture, &s_listener, this); +} + +void PointerPinchGesture::setup(zwp_pointer_gesture_pinch_v1 *pointerpinchgesture) +{ + d->setup(pointerpinchgesture); +} + +void PointerPinchGesture::release() +{ + d->pointerpinchgesture.release(); +} + +void PointerPinchGesture::destroy() +{ + d->pointerpinchgesture.destroy(); +} + +PointerPinchGesture::operator zwp_pointer_gesture_pinch_v1*() { + return d->pointerpinchgesture; +} + +PointerPinchGesture::operator zwp_pointer_gesture_pinch_v1*() const { + return d->pointerpinchgesture; +} + +bool PointerPinchGesture::isValid() const +{ + return d->pointerpinchgesture.isValid(); +} + +quint32 PointerPinchGesture::fingerCount() const +{ + return d->fingerCount; +} + +QPointer PointerPinchGesture::surface() const +{ + return d->surface; +} + +} +} + diff --git a/src/client/protocols/pointer-gestures-unstable-v1.xml b/src/client/protocols/pointer-gestures-unstable-v1.xml new file mode 100644 --- /dev/null +++ b/src/client/protocols/pointer-gestures-unstable-v1.xml @@ -0,0 +1,177 @@ + + + + + + A global interface to provide semantic touchpad gestures for a given + pointer. + + Two gestures are currently supported: swipe and zoom/rotate. + All gestures follow a three-stage cycle: begin, update, end and + are identified by a unique id. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + Create a swipe gesture object. See the + wl_pointer_gesture_swipe interface for details. + + + + + + + + Create a pinch gesture object. See the + wl_pointer_gesture_pinch interface for details. + + + + + + + + + A swipe gesture object notifies a client about a multi-finger swipe + gesture detected on an indirect input device such as a touchpad. + The gesture is usually initiated by multiple fingers moving in the + same direction but once initiated the direction may change. + The precise conditions of when such a gesture is detected are + implementation-dependent. + + A gesture consists of three stages: begin, update (optional) and end. + There cannot be multiple simultaneous pinch or swipe gestures on a + same pointer/seat, how compositors prevent these situations is + implementation-dependent. + + A gesture may be cancelled by the compositor or the hardware. + Clients should not consider performing permanent or irreversible + actions until the end of a gesture has been received. + + + + + + + + + This event is sent when a multi-finger swipe gesture is detected + on the device. + + + + + + + + + + This event is sent when a multi-finger swipe gesture changes the + position of the logical center. + + The dx and dy coordinates are relative coordinates of the logical + center of the gesture compared to the previous event. + + + + + + + + + This event is sent when a multi-finger swipe gesture ceases to + be valid. This may happen when one or more fingers are lifted or + the gesture is cancelled. + + When a gesture is cancelled, the client should undo state changes + caused by this gesture. What causes a gesture to be cancelled is + implementation-dependent. + + + + + + + + + + A pinch gesture object notifies a client about a multi-finger pinch + gesture detected on an indirect input device such as a touchpad. + The gesture is usually initiated by multiple fingers moving towards + each other or away from each other, or by two or more fingers rotating + around a logical center of gravity. The precise conditions of when + such a gesture is detected are implementation-dependent. + + A gesture consists of three stages: begin, update (optional) and end. + There cannot be multiple simultaneous pinch or swipe gestures on a + same pointer/seat, how compositors prevent these situations is + implementation-dependent. + + A gesture may be cancelled by the compositor or the hardware. + Clients should not consider performing permanent or irreversible + actions until the end of a gesture has been received. + + + + + + + + + This event is sent when a multi-finger pinch gesture is detected + on the device. + + + + + + + + + + This event is sent when a multi-finger pinch gesture changes the + position of the logical center, the rotation or the relative scale. + + The dx and dy coordinates are relative coordinates in the + surface coordinate space of the logical center of the gesture. + + The scale factor is an absolute scale compared to the + pointer_gesture_pinch.begin event, e.g. a scale of 2 means the fingers + are now twice as far apart as on pointer_gesture_pinch.begin. + + The rotation is the relative angle in degrees clockwise compared to the previous + pointer_gesture_pinch.begin or pointer_gesture_pinch.update event. + + + + + + + + + + + This event is sent when a multi-finger pinch gesture ceases to + be valid. This may happen when one or more fingers are lifted or + the gesture is cancelled. + + When a gesture is cancelled, the client should undo state changes + caused by this gesture. What causes a gesture to be cancelled is + implementation-dependent. + + + + + + + + diff --git a/src/client/registry.h b/src/client/registry.h --- a/src/client/registry.h +++ b/src/client/registry.h @@ -51,6 +51,7 @@ struct org_kde_kwin_server_decoration_manager; struct xdg_shell; struct zwp_relative_pointer_manager_v1; +struct zwp_pointer_gestures_v1; namespace KWayland { @@ -70,6 +71,7 @@ class Output; class PlasmaShell; class PlasmaWindowManagement; +class PointerGestures; class Seat; class ShadowManager; class BlurManager; @@ -144,7 +146,8 @@ TextInputManagerUnstableV0, ///< Refers to wl_text_input_manager, @since 5.23 TextInputManagerUnstableV2, ///< Refers to zwp_text_input_manager_v2, @since 5.23 XdgShellUnstableV5, ///< Refers to xdg_shell (unstable version 5), @since 5.25 - RelativePointerManagerUnstableV1 ///< Refers to zwp_relative_pointer_manager_v1, @since 5.28 + RelativePointerManagerUnstableV1, ///< Refers to zwp_relative_pointer_manager_v1, @since 5.28 + PointerGesturesUnstableV1 /// Refers to zwp_pointer_gestures_v1, @since 5.28 }; explicit Registry(QObject *parent = nullptr); virtual ~Registry(); @@ -489,6 +492,16 @@ * @since 5.28 **/ zwp_relative_pointer_manager_v1 *bindRelativePointerManagerUnstableV1(uint32_t name, uint32_t version) const; + /** + * Binds the zwp_pointer_gestures_v1 with @p name and @p version. + * If the @p name does not exist or is not for the pointer gestures interface in unstable version 1, + * @c null will be returned. + * + * Prefer using createPointerGestures instead. + * @see createPointerGestures + * @since 5.28 + **/ + zwp_pointer_gestures_v1 *bindPointerGesturesUnstableV1(uint32_t name, uint32_t version) const; ///@} /** @@ -863,6 +876,24 @@ * @since 5.28 **/ RelativePointerManager *createRelativePointerManager(quint32 name, quint32 version, QObject *parent = nullptr); + /** + * Creates a PointerGestures and sets it up to manage the interface identified by + * @p name and @p version. + * + * This factory method supports the following interfaces: + * @li zwp_pointer_gestures_v1 + * + * If @p name is for one of the supported interfaces the corresponding manager will be created, + * otherwise @c null will be returned. + * + * @param name The name of the interface to bind + * @param version The version of the interface to use + * @param parent The parent for the PointerGestures + * + * @returns The created PointerGestures + * @since 5.28 + **/ + PointerGestures *createPointerGestures(quint32 name, quint32 version, QObject *parent = nullptr); ///@} /** @@ -1038,6 +1069,13 @@ * @since 5.28 **/ void relativePointerManagerUnstableV1Announced(quint32 name, quint32 version); + /** + * Emitted whenever a zwp_pointer_gestures_v1 interface gets announced. + * @param name The name for the announced interface + * @param version The maximum supported version of the announced interface + * @since 5.28 + **/ + void pointerGesturesUnstableV1Announced(quint32 name, quint32 version); ///@} /** * @name Interface removed signals. @@ -1179,6 +1217,12 @@ * @since 5.28 **/ void relativePointerManagerUnstableV1Removed(quint32 name); + /** + * Emitted whenever a zwp_pointer_gestures_v1 interface gets removed. + * @param name The name for the removed interface + * @since 5.28 + **/ + void pointerGesturesUnstableV1Removed(quint32 name); ///@} /** * Generic announced signal which gets emitted whenever an interface gets diff --git a/src/client/registry.cpp b/src/client/registry.cpp --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -33,6 +33,7 @@ #include "output.h" #include "plasmashell.h" #include "plasmawindowmanagement.h" +#include "pointergestures.h" #include "seat.h" #include "shadow.h" #include "blur.h" @@ -68,6 +69,7 @@ #include #include #include +#include /***** * How to add another interface: @@ -263,6 +265,13 @@ &zwp_relative_pointer_manager_v1_interface, &Registry::relativePointerManagerUnstableV1Announced, &Registry::relativePointerManagerUnstableV1Removed + }}, + {Registry::Interface::PointerGesturesUnstableV1, { + 1, + QByteArrayLiteral("zwp_pointer_gestures_v1"), + &zwp_pointer_gestures_v1_interface, + &Registry::pointerGesturesUnstableV1Announced, + &Registry::pointerGesturesUnstableV1Removed }} }; @@ -559,6 +568,7 @@ BIND(TextInputManagerUnstableV2, zwp_text_input_manager_v2) BIND(XdgShellUnstableV5, xdg_shell) BIND(RelativePointerManagerUnstableV1, zwp_relative_pointer_manager_v1) +BIND(PointerGesturesUnstableV1, zwp_pointer_gestures_v1) BIND2(ShadowManager, Shadow, org_kde_kwin_shadow_manager) BIND2(BlurManager, Blur, org_kde_kwin_blur_manager) BIND2(ContrastManager, Contrast, org_kde_kwin_contrast_manager) @@ -648,6 +658,16 @@ } } +PointerGestures *Registry::createPointerGestures(quint32 name, quint32 version, QObject *parent) +{ + switch (d->interfaceForName(name)) { + case Interface::PointerGesturesUnstableV1: + return d->create(name, version, parent, &Registry::bindPointerGesturesUnstableV1); + default: + return nullptr; + } +} + namespace { static const wl_interface *wlInterface(Registry::Interface interface) { diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -21,6 +21,8 @@ pointer_interface.cpp plasmashell_interface.cpp plasmawindowmanagement_interface.cpp + pointergestures_interface.cpp + pointergestures_interface_v1.cpp qtsurfaceextension_interface.cpp region_interface.cpp relativepointer_interface.cpp @@ -127,6 +129,11 @@ BASENAME xdg-shell-v5 ) +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${KWAYLAND_SOURCE_DIR}/src/client/protocols/pointer-gestures-unstable-v1.xml + BASENAME pointer-gestures-unstable-v1 +) + add_library(KF5WaylandServer ${SERVER_LIB_SRCS}) generate_export_header(KF5WaylandServer BASE_NAME @@ -184,6 +191,7 @@ outputmanagement_interface.h output_interface.h pointer_interface.h + pointergestures_interface.h plasmashell_interface.h plasmawindowmanagement_interface.h qtsurfaceextension_interface.h diff --git a/src/server/display.h b/src/server/display.h --- a/src/server/display.h +++ b/src/server/display.h @@ -76,6 +76,8 @@ class XdgShellInterface; enum class RelativePointerInterfaceVersion; class RelativePointerManagerInterface; +enum class PointerGesturesInterfaceVersion; +class PointerGesturesInterface; /** * @brief Class holding the Wayland server display loop. @@ -200,6 +202,14 @@ RelativePointerManagerInterface *createRelativePointerManager(const RelativePointerInterfaceVersion &version, QObject *parent = nullptr); /** + * Creates the PointerGesturesInterface in interface @p version + * + * @returns The created manager object + * @since 5.28 + **/ + PointerGesturesInterface *createPointerGestures(const PointerGesturesInterfaceVersion &version, QObject *parent = nullptr); + + /** * Gets the ClientConnection for the given @p client. * If there is no ClientConnection yet for the given @p client, it will be created. * @param client The native client for which the ClientConnection is retrieved diff --git a/src/server/display.cpp b/src/server/display.cpp --- a/src/server/display.cpp +++ b/src/server/display.cpp @@ -30,6 +30,7 @@ #include "output_interface.h" #include "plasmashell_interface.h" #include "plasmawindowmanagement_interface.h" +#include "pointergestures_interface_p.h" #include "qtsurfaceextension_interface.h" #include "seat_interface.h" #include "shadow_interface.h" @@ -382,6 +383,18 @@ return r; } +PointerGesturesInterface *Display::createPointerGestures(const PointerGesturesInterfaceVersion &version, QObject *parent) +{ + PointerGesturesInterface *p = nullptr; + switch (version) { + case PointerGesturesInterfaceVersion::UnstableV1: + p = new PointerGesturesUnstableV1Interface(this, parent); + break; + } + connect(this, &Display::aboutToTerminate, p, [p] { delete p; }); + return p; +} + void Display::createShm() { Q_ASSERT(d->display); diff --git a/src/server/pointer_interface.h b/src/server/pointer_interface.h --- a/src/server/pointer_interface.h +++ b/src/server/pointer_interface.h @@ -30,6 +30,7 @@ { class Cursor; +class PointerGesturesUnstableV1Interface; class RelativePointerManagerUnstableV1Interface; class SeatInterface; class SurfaceInterface; @@ -76,6 +77,7 @@ void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds); friend class SeatInterface; friend class RelativePointerManagerUnstableV1Interface; + friend class PointerGesturesUnstableV1Interface; explicit PointerInterface(SeatInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; diff --git a/src/server/pointer_interface.cpp b/src/server/pointer_interface.cpp --- a/src/server/pointer_interface.cpp +++ b/src/server/pointer_interface.cpp @@ -19,6 +19,7 @@ *********************************************************************/ #include "pointer_interface.h" #include "pointer_interface_p.h" +#include "pointergestures_interface_p.h" #include "resource_p.h" #include "relativepointer_interface_p.h" #include "seat_interface.h" @@ -88,6 +89,27 @@ ); } + +void PointerInterface::Private::registerSwipeGesture(PointerSwipeGestureInterface *gesture) +{ + swipeGestures << gesture; + QObject::connect(gesture, &QObject::destroyed, q, + [this, gesture] { + swipeGestures.removeOne(gesture); + } + ); +} + +void PointerInterface::Private::registerPinchGesture(PointerPinchGestureInterface *gesture) +{ + pinchGestures << gesture; + QObject::connect(gesture, &QObject::destroyed, q, + [this, gesture] { + pinchGestures.removeOne(gesture); + } + ); +} + namespace { static QPointF surfacePosition(SurfaceInterface *surface) { if (surface && surface->subSurface()) { @@ -108,6 +130,86 @@ wl_fixed_from_double(adjustedPos.x()), wl_fixed_from_double(adjustedPos.y())); } +void PointerInterface::Private::startSwipeGesture(quint32 serial, quint32 fingerCount) +{ + if (swipeGestures.isEmpty()) { + return; + } + for (auto it = swipeGestures.constBegin(), end = swipeGestures.constEnd(); it != end; it++) { + (*it)->start(serial, fingerCount); + } +} + +void PointerInterface::Private::updateSwipeGesture(const QSizeF &delta) +{ + if (swipeGestures.isEmpty()) { + return; + } + for (auto it = swipeGestures.constBegin(), end = swipeGestures.constEnd(); it != end; it++) { + (*it)->update(delta); + } +} + +void PointerInterface::Private::endSwipeGesture(quint32 serial) +{ + if (swipeGestures.isEmpty()) { + return; + } + for (auto it = swipeGestures.constBegin(), end = swipeGestures.constEnd(); it != end; it++) { + (*it)->end(serial); + } +} + +void PointerInterface::Private::cancelSwipeGesture(quint32 serial) +{ + if (swipeGestures.isEmpty()) { + return; + } + for (auto it = swipeGestures.constBegin(), end = swipeGestures.constEnd(); it != end; it++) { + (*it)->cancel(serial); + } +} + +void PointerInterface::Private::startPinchGesture(quint32 serial, quint32 fingerCount) +{ + if (pinchGestures.isEmpty()) { + return; + } + for (auto it = pinchGestures.constBegin(), end = pinchGestures.constEnd(); it != end; it++) { + (*it)->start(serial, fingerCount); + } +} + +void PointerInterface::Private::updatePinchGesture(const QSizeF &delta, qreal scale, qreal rotation) +{ + if (pinchGestures.isEmpty()) { + return; + } + for (auto it = pinchGestures.constBegin(), end = pinchGestures.constEnd(); it != end; it++) { + (*it)->update(delta, scale, rotation); + } +} + +void PointerInterface::Private::endPinchGesture(quint32 serial) +{ + if (pinchGestures.isEmpty()) { + return; + } + for (auto it = pinchGestures.constBegin(), end = pinchGestures.constEnd(); it != end; it++) { + (*it)->end(serial); + } +} + +void PointerInterface::Private::cancelPinchGesture(quint32 serial) +{ + if (pinchGestures.isEmpty()) { + return; + } + for (auto it = pinchGestures.constBegin(), end = pinchGestures.constEnd(); it != end; it++) { + (*it)->cancel(serial); + } +} + #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_pointer_interface PointerInterface::Private::s_interface = { setCursorCallback, diff --git a/src/server/pointer_interface_p.h b/src/server/pointer_interface_p.h --- a/src/server/pointer_interface_p.h +++ b/src/server/pointer_interface_p.h @@ -29,6 +29,8 @@ { namespace Server { +class PointerPinchGestureInterface; +class PointerSwipeGestureInterface; class RelativePointerInterface; class PointerInterface::Private : public Resource::Private @@ -42,11 +44,25 @@ QMetaObject::Connection destroyConnection; Cursor *cursor = nullptr; QVector relativePointers; + QVector swipeGestures; + QVector pinchGestures; void sendLeave(SurfaceInterface *surface, quint32 serial); void sendEnter(SurfaceInterface *surface, const QPointF &parentSurfacePosition, quint32 serial); void registerRelativePointer(RelativePointerInterface *relativePointer); + void registerSwipeGesture(PointerSwipeGestureInterface *gesture); + void registerPinchGesture(PointerPinchGestureInterface *gesture); + + void startSwipeGesture(quint32 serial, quint32 fingerCount); + void updateSwipeGesture(const QSizeF &delta); + void endSwipeGesture(quint32 serial); + void cancelSwipeGesture(quint32 serial); + + void startPinchGesture(quint32 serial, quint32 fingerCount); + void updatePinchGesture(const QSizeF &delta, qreal scale, qreal rotation); + void endPinchGesture(quint32 serial); + void cancelPinchGesture(quint32 serial); private: PointerInterface *q_func() { diff --git a/src/server/pointergestures_interface.h b/src/server/pointergestures_interface.h new file mode 100644 --- /dev/null +++ b/src/server/pointergestures_interface.h @@ -0,0 +1,75 @@ +/**************************************************************************** +Copyright 2016 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_SERVER_POINTERGESTURES_H +#define KWAYLAND_SERVER_POINTERGESTURES_H + +#include "global.h" + +#include + +namespace KWayland +{ +namespace Server +{ + +/** + * Enum describing the interface versions the PointerGesturesInterface can support. + * + * @since 5.28 + **/ +enum class PointerGesturesInterfaceVersion { + /** + * zwp_pointer_gestures_v1, zwp_pointer_gesture_swipe_v1 and zwp_pointer_gesture_pinch_v1 + **/ + UnstableV1 +}; + +/** + * Manager object for the PointerGestures. + * + * Creates and manages pointer swipe and pointer pinch gestures which are + * reported to the SeatInterface. + * + * @see Display::createPointerGestures + * @since 5.28 + **/ +class KWAYLANDSERVER_EXPORT PointerGesturesInterface : public Global +{ + Q_OBJECT +public: + virtual ~PointerGesturesInterface(); + + /** + * @returns The interface version used by this PointerGesturesInterface + **/ + PointerGesturesInterfaceVersion interfaceVersion() const; + +protected: + class Private; + explicit PointerGesturesInterface(Private *d, QObject *parent = nullptr); + +private: + Private *d_func() const; +}; + +} +} + +#endif diff --git a/src/server/pointergestures_interface.cpp b/src/server/pointergestures_interface.cpp new file mode 100644 --- /dev/null +++ b/src/server/pointergestures_interface.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +Copyright 2016 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "pointergestures_interface_p.h" + +namespace KWayland +{ +namespace Server +{ + +PointerGesturesInterface::Private::Private(PointerGesturesInterfaceVersion interfaceVersion, PointerGesturesInterface *q, Display *d, const wl_interface *interface, quint32 version) + : Global::Private(d, interface, version) + , interfaceVersion(interfaceVersion) + , q(q) +{ +} + +PointerGesturesInterface::PointerGesturesInterface(Private *d, QObject *parent) + : Global(d, parent) +{ +} + +PointerGesturesInterface::~PointerGesturesInterface() = default; + +PointerGesturesInterfaceVersion PointerGesturesInterface::interfaceVersion() const +{ + Q_D(); + return d->interfaceVersion; +} + +PointerGesturesInterface::Private *PointerGesturesInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +PointerSwipeGestureInterface::Private::Private(PointerSwipeGestureInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation, PointerInterface *pointer) + : Resource::Private(q, c, parentResource, interface, implementation) + , pointer(pointer) +{ +} + +PointerSwipeGestureInterface::Private::~Private() +{ + if (resource) { + wl_resource_destroy(resource); + resource = nullptr; + } +} + +PointerSwipeGestureInterface::PointerSwipeGestureInterface(Private *p, QObject *parent) + : Resource(p, parent) +{ +} + +PointerSwipeGestureInterface::~PointerSwipeGestureInterface() = default; + +PointerSwipeGestureInterface::Private *PointerSwipeGestureInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +PointerPinchGestureInterface::Private::Private(PointerPinchGestureInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation, PointerInterface *pointer) + : Resource::Private(q, c, parentResource, interface, implementation) + , pointer(pointer) +{ +} + +PointerPinchGestureInterface::Private::~Private() +{ + if (resource) { + wl_resource_destroy(resource); + resource = nullptr; + } +} + +PointerPinchGestureInterface::PointerPinchGestureInterface(Private *p, QObject *parent) + : Resource(p, parent) +{ +} + +PointerPinchGestureInterface::~PointerPinchGestureInterface() = default; + +PointerPinchGestureInterface::Private *PointerPinchGestureInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +} +} diff --git a/src/server/pointergestures_interface_p.h b/src/server/pointergestures_interface_p.h new file mode 100644 --- /dev/null +++ b/src/server/pointergestures_interface_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +Copyright 2016 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_SERVER_POINTERGESTURES_P_H +#define KWAYLAND_SERVER_POINTERGESTURES_P_H + +#include "pointergestures_interface.h" +#include "resource_p.h" +#include "global_p.h" + +namespace KWayland +{ +namespace Server +{ + +class PointerInterface; + +class PointerGesturesInterface::Private : public Global::Private +{ +public: + PointerGesturesInterfaceVersion interfaceVersion; + +protected: + Private(PointerGesturesInterfaceVersion interfaceVersion, PointerGesturesInterface *q, Display *d, const wl_interface *interface, quint32 version); + PointerGesturesInterface *q; +}; + +class PointerGesturesUnstableV1Interface : public PointerGesturesInterface +{ + Q_OBJECT +public: + explicit PointerGesturesUnstableV1Interface(Display *display, QObject *parent = nullptr); + virtual ~PointerGesturesUnstableV1Interface(); + +private: + class Private; +}; + +class PointerSwipeGestureInterface : public Resource +{ + Q_OBJECT +public: + virtual ~PointerSwipeGestureInterface(); + + virtual void start(quint32 serial, quint32 fingerCount) = 0; + virtual void update(const QSizeF &delta) = 0; + virtual void end(quint32 serial) = 0; + virtual void cancel(quint32 serial) = 0; + +protected: + class Private; + explicit PointerSwipeGestureInterface(Private *p, QObject *parent = nullptr); + +private: + Private *d_func() const; +}; + +class PointerSwipeGestureInterface::Private : public Resource::Private +{ +public: + ~Private(); + + PointerInterface *pointer; + +protected: + Private(PointerSwipeGestureInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation, PointerInterface *pointer); + +private: + PointerSwipeGestureInterface *q_func() { + return reinterpret_cast(q); + } +}; + +class PointerPinchGestureInterface : public Resource +{ + Q_OBJECT +public: + virtual ~PointerPinchGestureInterface(); + + virtual void start(quint32 serial, quint32 fingerCount) = 0; + virtual void update(const QSizeF &delta, qreal scale, qreal rotation) = 0; + virtual void end(quint32 serial) = 0; + virtual void cancel(quint32 serial) = 0; + +protected: + class Private; + explicit PointerPinchGestureInterface(Private *p, QObject *parent = nullptr); + +private: + Private *d_func() const; +}; + +class PointerPinchGestureInterface::Private : public Resource::Private +{ +public: + ~Private(); + + PointerInterface *pointer; + +protected: + Private(PointerPinchGestureInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation, PointerInterface *pointer); + +private: + PointerPinchGestureInterface *q_func() { + return reinterpret_cast(q); + } +}; + +class PointerSwipeGestureUnstableV1Interface : public PointerSwipeGestureInterface +{ + Q_OBJECT +public: + explicit PointerSwipeGestureUnstableV1Interface(PointerGesturesUnstableV1Interface *parent, wl_resource *parentResource, PointerInterface *pointer); + virtual ~PointerSwipeGestureUnstableV1Interface(); + + void start(quint32 serial, quint32 fingerCount) override; + void update(const QSizeF &delta) override; + void end(quint32 serial) override; + void cancel(quint32 serial) override; + +private: + friend class PointerGesturesUnstableV1Interface; + class Private; + Private *d_func() const; +}; + +class PointerPinchGestureUnstableV1Interface : public PointerPinchGestureInterface +{ + Q_OBJECT +public: + explicit PointerPinchGestureUnstableV1Interface(PointerGesturesUnstableV1Interface *parent, wl_resource *parentResource, PointerInterface *pointer); + virtual ~PointerPinchGestureUnstableV1Interface(); + + void start(quint32 serial, quint32 fingerCount) override; + void update(const QSizeF &delta, qreal scale, qreal rotation) override; + void end(quint32 serial) override; + void cancel(quint32 serial) override; + +private: + friend class PointerGesturesUnstableV1Interface; + + class Private; + Private *d_func() const; +}; + +} +} + +#endif diff --git a/src/server/pointergestures_interface_v1.cpp b/src/server/pointergestures_interface_v1.cpp new file mode 100644 --- /dev/null +++ b/src/server/pointergestures_interface_v1.cpp @@ -0,0 +1,298 @@ +/**************************************************************************** +Copyright 2016 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "pointergestures_interface_p.h" +#include "display.h" +#include "pointer_interface_p.h" +#include "resource_p.h" +#include "seat_interface.h" +#include "surface_interface.h" + +#include + +namespace KWayland +{ +namespace Server +{ + +class PointerGesturesUnstableV1Interface::Private : public PointerGesturesInterface::Private +{ +public: + Private(PointerGesturesUnstableV1Interface *q, Display *d); + +private: + void bind(wl_client *client, uint32_t version, uint32_t id) override; + + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static void getSwipeGestureCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * pointer); + static void getPinchGestureCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * pointer); + + PointerGesturesUnstableV1Interface *q; + static const struct zwp_pointer_gestures_v1_interface s_interface; + static const quint32 s_version; +}; + +const quint32 PointerGesturesUnstableV1Interface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwp_pointer_gestures_v1_interface PointerGesturesUnstableV1Interface::Private::s_interface = { + getSwipeGestureCallback, + getPinchGestureCallback +}; +#endif + +void PointerGesturesUnstableV1Interface::Private::getSwipeGestureCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *pointer) +{ + PointerInterface *p = PointerInterface::get(pointer); + if (!p) { + // TODO: raise error? + return; + } + auto m = cast(resource); + auto *g = new PointerSwipeGestureUnstableV1Interface(m->q, resource, p); + g->d->create(m->display->getConnection(client), version, id); + p->d_func()->registerSwipeGesture(g); +} + +void PointerGesturesUnstableV1Interface::Private::getPinchGestureCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * pointer) +{ + PointerInterface *p = PointerInterface::get(pointer); + if (!p) { + // TODO: raise error? + return; + } + auto m = cast(resource); + auto *g = new PointerPinchGestureUnstableV1Interface(m->q, resource, p); + g->d->create(m->display->getConnection(client), version, id); + p->d_func()->registerPinchGesture(g); +} + +PointerGesturesUnstableV1Interface::Private::Private(PointerGesturesUnstableV1Interface *q, Display *d) + : PointerGesturesInterface::Private(PointerGesturesInterfaceVersion::UnstableV1, q, d, &zwp_pointer_gestures_v1_interface, s_version) + , q(q) +{ +} + +void PointerGesturesUnstableV1Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&zwp_pointer_gestures_v1_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); + // TODO: should we track? +} + +void PointerGesturesUnstableV1Interface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) + // TODO: implement? +} + +PointerGesturesUnstableV1Interface::PointerGesturesUnstableV1Interface(Display *display, QObject *parent) + : PointerGesturesInterface(new Private(this, display), parent) +{ +} + +PointerGesturesUnstableV1Interface::~PointerGesturesUnstableV1Interface() = default; + +class PointerSwipeGestureUnstableV1Interface::Private : public PointerSwipeGestureInterface::Private +{ +public: + Private(PointerSwipeGestureUnstableV1Interface *q, PointerGesturesUnstableV1Interface *c, wl_resource *parentResource, PointerInterface *pointer); + ~Private(); + + void end(quint32 serial, bool end); + +private: + + PointerSwipeGestureUnstableV1Interface *q_func() { + return reinterpret_cast(q); + } + + static const struct zwp_pointer_gesture_swipe_v1_interface s_interface; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwp_pointer_gesture_swipe_v1_interface PointerSwipeGestureUnstableV1Interface::Private::s_interface = { + resourceDestroyedCallback +}; +#endif + +PointerSwipeGestureUnstableV1Interface::Private::Private(PointerSwipeGestureUnstableV1Interface *q, PointerGesturesUnstableV1Interface *c, wl_resource *parentResource, PointerInterface *pointer) + : PointerSwipeGestureInterface::Private(q, c, parentResource, &zwp_pointer_gesture_swipe_v1_interface, &s_interface, pointer) +{ +} + +PointerSwipeGestureUnstableV1Interface::Private::~Private() = default; + +PointerSwipeGestureUnstableV1Interface::PointerSwipeGestureUnstableV1Interface(PointerGesturesUnstableV1Interface *parent, wl_resource *parentResource, PointerInterface *pointer) + : PointerSwipeGestureInterface(new Private(this, parent, parentResource, pointer)) +{ +} + +PointerSwipeGestureUnstableV1Interface::~PointerSwipeGestureUnstableV1Interface() = default; + +void PointerSwipeGestureUnstableV1Interface::start(quint32 serial, quint32 fingerCount) +{ + Q_D(); + SeatInterface *seat = qobject_cast(d->pointer->global()); + if (!seat) { + return; + } + if (!seat->focusedPointerSurface()) { + return; + } + zwp_pointer_gesture_swipe_v1_send_begin(resource(), serial, seat->timestamp(), seat->focusedPointerSurface()->resource(), fingerCount); +} + +void PointerSwipeGestureUnstableV1Interface::update(const QSizeF &delta) +{ + Q_D(); + SeatInterface *seat = qobject_cast(d->pointer->global()); + if (!seat) { + return; + } + zwp_pointer_gesture_swipe_v1_send_update(resource(), seat->timestamp(), + wl_fixed_from_double(delta.width()), wl_fixed_from_double(delta.height())); +} + +void PointerSwipeGestureUnstableV1Interface::Private::end(quint32 serial, bool cancel) +{ + SeatInterface *seat = qobject_cast(pointer->global()); + if (!seat) { + return; + } + zwp_pointer_gesture_swipe_v1_send_end(resource, serial, seat->timestamp(), uint32_t(cancel)); +} + +void PointerSwipeGestureUnstableV1Interface::end(quint32 serial) +{ + Q_D(); + d->end(serial, false); +} + +void PointerSwipeGestureUnstableV1Interface::cancel(quint32 serial) +{ + Q_D(); + d->end(serial, true); +} + +PointerSwipeGestureUnstableV1Interface::Private *PointerSwipeGestureUnstableV1Interface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +class PointerPinchGestureUnstableV1Interface::Private : public PointerPinchGestureInterface::Private +{ +public: + Private(PointerPinchGestureUnstableV1Interface *q, PointerGesturesUnstableV1Interface *c, wl_resource *parentResource, PointerInterface *pointer); + ~Private(); + + void end(quint32 serial, bool end); + +private: + + PointerPinchGestureUnstableV1Interface *q_func() { + return reinterpret_cast(q); + } + + static const struct zwp_pointer_gesture_pinch_v1_interface s_interface; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwp_pointer_gesture_pinch_v1_interface PointerPinchGestureUnstableV1Interface::Private::s_interface = { + resourceDestroyedCallback +}; +#endif + +PointerPinchGestureUnstableV1Interface::Private::Private(PointerPinchGestureUnstableV1Interface *q, PointerGesturesUnstableV1Interface *c, wl_resource *parentResource, PointerInterface *pointer) + : PointerPinchGestureInterface::Private(q, c, parentResource, &zwp_pointer_gesture_pinch_v1_interface, &s_interface, pointer) +{ +} + +PointerPinchGestureUnstableV1Interface::Private::~Private() = default; + +PointerPinchGestureUnstableV1Interface::PointerPinchGestureUnstableV1Interface(PointerGesturesUnstableV1Interface *parent, wl_resource *parentResource, PointerInterface *pointer) + : PointerPinchGestureInterface(new Private(this, parent, parentResource, pointer)) +{ +} + +PointerPinchGestureUnstableV1Interface::~PointerPinchGestureUnstableV1Interface() = default; + +void PointerPinchGestureUnstableV1Interface::start(quint32 serial, quint32 fingerCount) +{ + Q_D(); + SeatInterface *seat = qobject_cast(d->pointer->global()); + if (!seat) { + return; + } + if (!seat->focusedPointerSurface()) { + return; + } + zwp_pointer_gesture_pinch_v1_send_begin(resource(), serial, seat->timestamp(), seat->focusedPointerSurface()->resource(), fingerCount); +} + +void PointerPinchGestureUnstableV1Interface::update(const QSizeF &delta, qreal scale, qreal rotation) +{ + Q_D(); + SeatInterface *seat = qobject_cast(d->pointer->global()); + if (!seat) { + return; + } + zwp_pointer_gesture_pinch_v1_send_update(resource(), seat->timestamp(), + wl_fixed_from_double(delta.width()), wl_fixed_from_double(delta.height()), + wl_fixed_from_double(scale), wl_fixed_from_double(rotation)); +} + +void PointerPinchGestureUnstableV1Interface::Private::end(quint32 serial, bool cancel) +{ + SeatInterface *seat = qobject_cast(pointer->global()); + if (!seat) { + return; + } + zwp_pointer_gesture_pinch_v1_send_end(resource, serial, seat->timestamp(), uint32_t(cancel)); +} + +void PointerPinchGestureUnstableV1Interface::end(quint32 serial) +{ + Q_D(); + d->end(serial, false); +} + +void PointerPinchGestureUnstableV1Interface::cancel(quint32 serial) +{ + Q_D(); + d->end(serial, true); +} + +PointerPinchGestureUnstableV1Interface::Private *PointerPinchGestureUnstableV1Interface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +} +} + diff --git a/src/server/seat_interface.h b/src/server/seat_interface.h --- a/src/server/seat_interface.h +++ b/src/server/seat_interface.h @@ -403,6 +403,115 @@ * @since 5.28 **/ void relativePointerMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds); + + /** + * Starts a multi-finger swipe gesture for the currently focused pointer surface. + * + * Such gestures are normally reported through dedicated input devices such as touchpads. + * + * The gesture is usually initiated by multiple fingers moving in the + * same direction but once initiated the direction may change. + * The precise conditions of when such a gesture is detected are + * implementation-dependent. + * + * Only one gesture (either swipe or pinch) can be active at a given time. + * + * @param fingerCount The number of fingers involved in this multi-finger touchpad gesture + * + * @see PointerGesturesInterface + * @see focusedPointerSurface + * @see updatePointerSwipeGesture + * @see endPointerSwipeGesture + * @see cancelPointerSwipeGesture + * @see startPointerPinchGesture + * @since 5.28 + **/ + void startPointerSwipeGesture(quint32 fingerCount); + + /** + * The position of the logical center of the currently active multi-finger swipe gesture changes. + * + * @param delta coordinates are relative coordinates of the logical center of the gesture compared to the previous event. + * @see startPointerSwipeGesture + * @see endPointerSwipeGesture + * @see cancelPointerSwipeGesture + * @since 5.28 + **/ + void updatePointerSwipeGesture(const QSizeF &delta); + + /** + * The multi-finger swipe gesture ended. This may happen when one or more fingers are lifted. + * @see startPointerSwipeGesture + * @see updatePointerSwipeGesture + * @see cancelPointerSwipeGesture + * @see 5.28 + **/ + void endPointerSwipeGesture(); + + /** + * The multi-finger swipe gestures ended and got cancelled by the Wayland compositor. + * @see startPointerSwipeGesture + * @see updatePointerSwipeGesture + * @see endPointerSwipeGesture + * @since 5.28 + **/ + void cancelPointerSwipeGesture(); + + /** + * Starts a multi-finch pinch gesture for the currently focused pointer surface. + * + * Such gestures are normally reported through dedicated input devices such as touchpads. + * + * The gesture is usually initiated by multiple fingers moving towards + * each other or away from each other, or by two or more fingers rotating + * around a logical center of gravity. The precise conditions of when + * such a gesture is detected are implementation-dependent. + * + * Only one gesture (either swipe or pinch) can be active at a given time. + * + * @param fingerCount The number of fingers involved in this multi-touch touchpad gesture + * + * @see PointerGesturesInterface + * @see focusedPointerSurface + * @see updatePointerPinchGesture + * @see endPointerPinchGesture + * @see cancelPointerPinchGesture + * @see startPointerSwipeGesture + * @since 5.28 + **/ + void startPointerPinchGesture(quint32 fingerCount); + + /** + * The position of the logical center, the rotation or the relative scale of this + * multi-finger pinch gesture changes. + * + * @param delta coordinates are relative coordinates of the logical center of the gesture compared to the previous event. + * @param scale an absolute scale compared to the gesture start + * @param rotation relative angle in degrees clockwise compared to the previous start of update + * @see startPointerPinchGesture + * @see endPointerPinchGesture + * @see cancelPointerPinchGesture + * @since 5.28 + **/ + void updatePointerPinchGesture(const QSizeF &delta, qreal scale, qreal rotation); + + /** + * + * @see startPointerPinchGesture + * @see updatePointerPinchGesture + * @see cancelPointerPinchGesture + * @since 5.28 + **/ + void endPointerPinchGesture(); + + /** + * + * @see startPointerPinchGesture + * @see updatePointerPinchGesture + * @see endPointerPinchGesture + * @since 5.28 + **/ + void cancelPointerPinchGesture(); ///@} /** diff --git a/src/server/seat_interface.cpp b/src/server/seat_interface.cpp --- a/src/server/seat_interface.cpp +++ b/src/server/seat_interface.cpp @@ -871,6 +871,104 @@ } } +void SeatInterface::startPointerSwipeGesture(quint32 fingerCount) +{ + Q_D(); + if (d->globalPointer.gestureInProgress) { + return; + } + d->globalPointer.gestureInProgress = true; + if (d->globalPointer.focus.surface) { + const quint32 serial = d->display->nextSerial(); + for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { + (*it)->d_func()->startSwipeGesture(serial, fingerCount); + } + } +} + +void SeatInterface::updatePointerSwipeGesture(const QSizeF &delta) +{ + Q_D(); + if (d->globalPointer.focus.surface) { + for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { + (*it)->d_func()->updateSwipeGesture(delta); + } + } +} + +void SeatInterface::endPointerSwipeGesture() +{ + Q_D(); + if (d->globalPointer.focus.surface) { + const quint32 serial = d->display->nextSerial(); + for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { + (*it)->d_func()->endSwipeGesture(serial); + } + } + d->globalPointer.gestureInProgress = false; +} + +void SeatInterface::cancelPointerSwipeGesture() +{ + Q_D(); + if (d->globalPointer.focus.surface) { + const quint32 serial = d->display->nextSerial(); + for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { + (*it)->d_func()->cancelSwipeGesture(serial); + } + } + d->globalPointer.gestureInProgress = false; +} + +void SeatInterface::startPointerPinchGesture(quint32 fingerCount) +{ + Q_D(); + if (d->globalPointer.gestureInProgress) { + return; + } + d->globalPointer.gestureInProgress = true; + if (d->globalPointer.focus.surface) { + const quint32 serial = d->display->nextSerial(); + for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { + (*it)->d_func()->startPinchGesture(serial, fingerCount); + } + } +} + +void SeatInterface::updatePointerPinchGesture(const QSizeF &delta, qreal scale, qreal rotation) +{ + Q_D(); + if (d->globalPointer.focus.surface) { + for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { + (*it)->d_func()->updatePinchGesture(delta, scale, rotation); + } + } +} + +void SeatInterface::endPointerPinchGesture() +{ + Q_D(); + if (d->globalPointer.focus.surface) { + const quint32 serial = d->display->nextSerial(); + for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { + (*it)->d_func()->endPinchGesture(serial); + } + } + d->globalPointer.gestureInProgress = false; +} + +void SeatInterface::cancelPointerPinchGesture() +{ + Q_D(); + if (d->globalPointer.focus.surface) { + const quint32 serial = d->display->nextSerial(); + for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { + (*it)->d_func()->cancelPinchGesture(serial); + } + } + d->globalPointer.gestureInProgress = false; +} + void SeatInterface::keyPressed(quint32 key) { Q_D(); diff --git a/src/server/seat_interface_p.h b/src/server/seat_interface_p.h --- a/src/server/seat_interface_p.h +++ b/src/server/seat_interface_p.h @@ -84,6 +84,7 @@ quint32 serial = 0; }; Focus focus; + bool gestureInProgress = false; }; Pointer globalPointer; void updatePointerButtonSerial(quint32 button, quint32 serial); diff --git a/src/tools/mapping.txt b/src/tools/mapping.txt --- a/src/tools/mapping.txt +++ b/src/tools/mapping.txt @@ -49,3 +49,6 @@ xdg_popup;XdgPopupV5 zwp_relative_pointer_manager_v1;RelativePointerManagerUnstableV1 zwp_relative_pointer_v1;RelativePointerUnstableV1 +zwp_pointer_gestures_v1;PointerGesturesUnstableV1 +zwp_pointer_gesture_swipe_v1;PointerSwipeGestureUnstableV1 +zwp_pointer_gesture_pinch_v1;PointerPinchGestureUnstableV1