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 @@ -27,6 +27,7 @@ #include "../../src/client/registry.h" #include "../../src/client/output.h" #include "../../src/client/seat.h" +#include "../../src/client/relativepointer.h" #include "../../src/client/server_decoration.h" #include "../../src/client/shell.h" #include "../../src/client/subcompositor.h" @@ -47,13 +48,15 @@ #include "../../src/server/outputdevice_interface.h" #include "../../src/server/textinput_interface.h" #include "../../src/server/xdgshell_interface.h" +#include "../../src/server/relativepointer_interface.h" // Wayland #include #include #include #include #include #include +#include class TestWaylandRegistry : public QObject { @@ -80,6 +83,7 @@ void testBindTextInputManagerUnstableV0(); void testBindTextInputManagerUnstableV2(); void testBindXdgShellUnstableV5(); + void testBindRelativePointerManagerUnstableV1(); void testGlobalSync(); void testGlobalSyncThreaded(); void testRemoval(); @@ -101,6 +105,7 @@ KWayland::Server::TextInputManagerInterface *m_textInputManagerV0; KWayland::Server::TextInputManagerInterface *m_textInputManagerV2; KWayland::Server::XdgShellInterface *m_xdgShellUnstableV5; + KWayland::Server::RelativePointerManagerInterface *m_relativePointerV1; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-registry-0"); @@ -120,6 +125,7 @@ , m_textInputManagerV0(nullptr) , m_textInputManagerV2(nullptr) , m_xdgShellUnstableV5(nullptr) + , m_relativePointerV1(nullptr) { } @@ -161,6 +167,9 @@ m_xdgShellUnstableV5 = m_display->createXdgShell(KWayland::Server::XdgShellInterfaceVersion::UnstableV5); m_xdgShellUnstableV5->create(); QCOMPARE(m_xdgShellUnstableV5->interfaceVersion(), KWayland::Server::XdgShellInterfaceVersion::UnstableV5); + m_relativePointerV1 = m_display->createRelativePointerManager(KWayland::Server::RelativePointerInterfaceVersion::UnstableV1); + m_relativePointerV1->create(); + QCOMPARE(m_relativePointerV1->interfaceVersion(), KWayland::Server::RelativePointerInterfaceVersion::UnstableV1); } void TestWaylandRegistry::cleanup() @@ -303,6 +312,11 @@ TEST_BIND(KWayland::Client::Registry::Interface::XdgShellUnstableV5, SIGNAL(xdgShellUnstableV5Announced(quint32,quint32)), bindXdgShellUnstableV5, xdg_shell_destroy) } +void TestWaylandRegistry::testBindRelativePointerManagerUnstableV1() +{ + TEST_BIND(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1, SIGNAL(relativePointerManagerUnstableV1Announced(quint32,quint32)), bindRelativePointerManagerUnstableV1, zwp_relative_pointer_manager_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 @@ -30,6 +30,7 @@ #include "../../src/client/pointer.h" #include "../../src/client/surface.h" #include "../../src/client/registry.h" +#include "../../src/client/relativepointer.h" #include "../../src/client/seat.h" #include "../../src/client/shm_pool.h" #include "../../src/client/subcompositor.h" @@ -41,6 +42,7 @@ #include "../../src/server/display.h" #include "../../src/server/keyboard_interface.h" #include "../../src/server/pointer_interface.h" +#include "../../src/server/relativepointer_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/subcompositor_interface.h" #include "../../src/server/surface_interface.h" @@ -87,11 +89,13 @@ KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::SeatInterface *m_seatInterface; KWayland::Server::SubCompositorInterface *m_subCompositorInterface; + KWayland::Server::RelativePointerManagerInterface *m_relativePointerManagerInterface; 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::EventQueue *m_queue; QThread *m_thread; }; @@ -104,11 +108,13 @@ , m_compositorInterface(nullptr) , m_seatInterface(nullptr) , m_subCompositorInterface(nullptr) + , m_relativePointerManagerInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_seat(nullptr) , m_shm(nullptr) , m_subCompositor(nullptr) + , m_relativePointerManager(nullptr) , m_queue(nullptr) , m_thread(nullptr) { @@ -134,6 +140,11 @@ m_subCompositorInterface->create(); QVERIFY(m_subCompositorInterface->isValid()); + m_relativePointerManagerInterface = m_display->createRelativePointerManager(RelativePointerInterfaceVersion::UnstableV1, m_display); + QVERIFY(m_relativePointerManagerInterface); + m_relativePointerManagerInterface->create(); + QVERIFY(m_relativePointerManagerInterface->isValid()); + // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); @@ -182,10 +193,19 @@ registry.interface(KWayland::Client::Registry::Interface::SubCompositor).version, this); QVERIFY(m_subCompositor->isValid()); + + m_relativePointerManager = registry.createRelativePointerManager(registry.interface(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1).name, + registry.interface(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1).version, + this); + QVERIFY(m_relativePointerManager->isValid()); } void TestWaylandSeat::cleanup() { + if (m_relativePointerManager) { + delete m_relativePointerManager; + m_relativePointerManager = nullptr; + } if (m_subCompositor) { delete m_subCompositor; m_subCompositor = nullptr; @@ -226,6 +246,9 @@ delete m_subCompositorInterface; m_subCompositorInterface = nullptr; + delete m_relativePointerManagerInterface; + m_relativePointerManagerInterface = nullptr; + delete m_display; m_display = nullptr; } @@ -342,6 +365,8 @@ Pointer *p = m_seat->createPointer(m_seat); const Pointer &cp = *p; QVERIFY(p->isValid()); + QScopedPointer relativePointer(m_relativePointerManager->createRelativePointer(p)); + QVERIFY(relativePointer->isValid()); QSignalSpy pointerCreatedSpy(m_seatInterface, SIGNAL(pointerCreated(KWayland::Server::PointerInterface*))); QVERIFY(pointerCreatedSpy.isValid()); // once the pointer is created it should be set as the focused pointer @@ -372,6 +397,9 @@ QSignalSpy buttonSpy(p, SIGNAL(buttonStateChanged(quint32,quint32,quint32,KWayland::Client::Pointer::ButtonState))); QVERIFY(buttonSpy.isValid()); + QSignalSpy relativeMotionSpy(relativePointer.data(), &RelativePointer::relativeMotion); + QVERIFY(relativeMotionSpy.isValid()); + QVERIFY(!p->enteredSurface()); QVERIFY(!cp.enteredSurface()); m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(10, 15)); @@ -393,6 +421,14 @@ QCOMPARE(motionSpy.first().first().toPoint(), QPoint(0, 1)); QCOMPARE(motionSpy.first().last().value(), quint32(1)); + // test relative motion + m_seatInterface->relativePointerMotion(QSizeF(1, 2), QSizeF(3, 4), quint64(-1)); + QVERIFY(relativeMotionSpy.wait()); + QCOMPARE(relativeMotionSpy.count(), 1); + QCOMPARE(relativeMotionSpy.first().at(0).toSizeF(), QSizeF(1, 2)); + QCOMPARE(relativeMotionSpy.first().at(1).toSizeF(), QSizeF(3, 4)); + QCOMPARE(relativeMotionSpy.first().at(2).value(), quint64(-1)); + // test axis m_seatInterface->setTimestamp(2); m_seatInterface->pointerAxis(Qt::Horizontal, 10); @@ -460,13 +496,25 @@ QVERIFY(!p->enteredSurface()); QVERIFY(!cp.enteredSurface()); + // now a relative motion should not be sent to the relative pointer + m_seatInterface->relativePointerMotion(QSizeF(1, 2), QSizeF(3, 4), quint64(-1)); + QVERIFY(!relativeMotionSpy.wait()); + // enter it again m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(0, 0)); QCOMPARE(focusedPointerChangedSpy.count(), 6); QVERIFY(enteredSpy.wait()); QCOMPARE(p->enteredSurface(), s); QCOMPARE(cp.enteredSurface(), s); + // send another relative motion event + m_seatInterface->relativePointerMotion(QSizeF(4, 5), QSizeF(6, 7), quint64(1)); + QVERIFY(relativeMotionSpy.wait()); + QCOMPARE(relativeMotionSpy.count(), 2); + QCOMPARE(relativeMotionSpy.last().at(0).toSizeF(), QSizeF(4, 5)); + QCOMPARE(relativeMotionSpy.last().at(1).toSizeF(), QSizeF(6, 7)); + QCOMPARE(relativeMotionSpy.last().at(2).value(), quint64(1)); + // destroy the focused pointer QSignalSpy unboundSpy(serverPointer, &Resource::unbound); QVERIFY(unboundSpy.isValid()); @@ -1368,6 +1416,7 @@ connect(m_connection, &ConnectionThread::connectionDied, m_seat, &Seat::destroy); 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_queue, &EventQueue::destroy); QVERIFY(m_seat->isValid()); @@ -1378,6 +1427,7 @@ m_compositorInterface = nullptr; m_seatInterface = nullptr; m_subCompositorInterface = nullptr; + m_relativePointerManagerInterface = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the seat should be destroyed; diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -33,6 +33,7 @@ plasmawindowmodel.cpp region.cpp registry.cpp + relativepointer.cpp seat.cpp server_decoration.cpp shadow.cpp @@ -123,6 +124,10 @@ PROTOCOL ${KWAYLAND_SOURCE_DIR}/src/client/protocols/xdg-shell-unstable-v5.xml BASENAME xdg-shell-v5 ) +ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS + PROTOCOL ${KWAYLAND_SOURCE_DIR}/src/client/protocols/relative-pointer-unstable-v1.xml + BASENAME relativepointer-unstable-v1 +) add_library(KF5WaylandClient ${CLIENT_LIB_SRCS}) generate_export_header(KF5WaylandClient @@ -180,6 +185,7 @@ plasmawindowmodel.h region.h registry.h + relativepointer.h seat.h server_decoration.h shadow.h diff --git a/src/client/protocols/relative-pointer-unstable-v1.xml b/src/client/protocols/relative-pointer-unstable-v1.xml new file mode 100644 --- /dev/null +++ b/src/client/protocols/relative-pointer-unstable-v1.xml @@ -0,0 +1,138 @@ + + + + + Copyright © 2014 Jonas Ådahl + Copyright © 2015 Red Hat Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol specifies a set of interfaces used for making clients able to + receive relative pointer events not obstructed by barriers (such as the + monitor edge or other pointer barriers). + + To start receiving relative pointer events, a client must first bind the + global interface "wp_relative_pointer_manager" which, if a compositor + supports relative pointer motion events, is exposed by the registry. After + having created the relative pointer manager proxy object, the client uses + it to create the actual relative pointer object using the + "get_relative_pointer" request given a wl_pointer. The relative pointer + motion events will then, when applicable, be transmitted via the proxy of + the newly created relative pointer object. See the documentation of the + relative pointer interface for more details. + + 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. + + + + + A global interface used for getting the relative pointer object for a + given pointer. + + + + + Used by the client to notify the server that it will no longer use this + relative pointer manager object. + + + + + + Create a relative pointer interface given a wl_pointer object. See the + wp_relative_pointer interface for more details. + + + + + + + + + + A wp_relative_pointer object is an extension to the wl_pointer interface + used for emitting relative pointer events. It shares the same focus as + wl_pointer objects of the same seat and will only emit events when it has + focus. + + + + + + + + + Relative x/y pointer motion from the pointer of the seat associated with + this object. + + A relative motion is in the same dimension as regular wl_pointer motion + events, except they do not represent an absolute position. For example, + moving a pointer from (x, y) to (x', y') would have the equivalent + relative motion (x' - x, y' - y). If a pointer motion caused the + absolute pointer position to be clipped by for example the edge of the + monitor, the relative motion is unaffected by the clipping and will + represent the unclipped motion. + + This event also contains non-accelerated motion deltas. The + non-accelerated delta is, when applicable, the regular pointer motion + delta as it was before having applied motion acceleration and other + transformations such as normalization. + + Note that the non-accelerated delta does not represent 'raw' events as + they were read from some device. Pointer motion acceleration is device- + and configuration-specific and non-accelerated deltas and accelerated + deltas may have the same value on some devices. + + Relative motions are not coupled to wl_pointer.motion events, and can be + sent in combination with such events, but also independently. There may + also be scenarios where wl_pointer.motion is sent, but there is no + relative motion. The order of an absolute and relative motion event + originating from the same physical motion is not guaranteed. + + If the client needs button events or focus state, it can receive them + from a wl_pointer object of the same seat that the wp_relative_pointer + object is associated with. + + + + + + + + + + + + diff --git a/src/client/registry.h b/src/client/registry.h --- a/src/client/registry.h +++ b/src/client/registry.h @@ -50,6 +50,7 @@ struct org_kde_plasma_window_management; struct org_kde_kwin_server_decoration_manager; struct xdg_shell; +struct zwp_relative_pointer_manager_v1; namespace KWayland { @@ -82,6 +83,7 @@ class TextInputManagerUnstableV0; class TextInputManagerUnstableV2; class XdgShell; +class RelativePointerManager; /** * @short Wrapper for the wl_registry interface. @@ -141,7 +143,8 @@ ServerSideDecorationManager, ///< Refers to org_kde_kwin_server_decoration_manager 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 + XdgShellUnstableV5, ///< Refers to xdg_shell (unstable version 5), @since 5.25 + RelativePointerManagerUnstableV1 ///< Refers to zwp_relative_pointer_manager_v1, @since 5.28 }; explicit Registry(QObject *parent = nullptr); virtual ~Registry(); @@ -476,6 +479,16 @@ * @since 5.25 **/ xdg_shell *bindXdgShellUnstableV5(uint32_t name, uint32_t version) const; + /** + * Binds the zwp_relative_pointer_manager_v1 with @p name and @p version. + * If the @p name does not exist or is not for the relative pointer interface in unstable version 1, + * @c null will be returned. + * + * Prefer using createRelativePointerManager instead. + * @see createRelativePointerManager + * @since 5.28 + **/ + zwp_relative_pointer_manager_v1 *bindRelativePointerManagerUnstableV1(uint32_t name, uint32_t version) const; ///@} /** @@ -832,6 +845,24 @@ * @since 5.25 **/ XdgShell *createXdgShell(quint32 name, quint32 version, QObject *parent = nullptr); + /** + * Creates a RelativePointerManager and sets it up to manage the interface identified by + * @p name and @p version. + * + * This factory method supports the following interfaces: + * @li zwp_relative_pointer_manager_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 RelativePointerManager + * + * @returns The created RelativePointerManager + * @since 5.28 + **/ + RelativePointerManager *createRelativePointerManager(quint32 name, quint32 version, QObject *parent = nullptr); ///@} /** @@ -1000,6 +1031,13 @@ * @since 5.25 **/ void xdgShellUnstableV5Announced(quint32 name, quint32 version); + /** + * Emitted whenever a zwp_relative_pointer_manager_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 relativePointerManagerUnstableV1Announced(quint32 name, quint32 version); ///@} /** * @name Interface removed signals. @@ -1135,6 +1173,12 @@ * @since 5.25 **/ void xdgShellUnstableV5Removed(quint32 name); + /** + * Emitted whenever a zwp_relative_pointer_manager_v1 interface gets removed. + * @param name The name for the removed interface + * @since 5.28 + **/ + void relativePointerManagerUnstableV1Removed(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 @@ -37,6 +37,7 @@ #include "shadow.h" #include "blur.h" #include "contrast.h" +#include "relativepointer.h" #include "server_decoration.h" #include "slide.h" #include "shell.h" @@ -66,6 +67,7 @@ #include #include #include +#include /***** * How to add another interface: @@ -254,6 +256,13 @@ &xdg_shell_interface, &Registry::xdgShellUnstableV5Announced, &Registry::xdgShellUnstableV5Removed + }}, + {Registry::Interface::RelativePointerManagerUnstableV1, { + 1, + QByteArrayLiteral("zwp_relative_pointer_manager_v1"), + &zwp_relative_pointer_manager_v1_interface, + &Registry::relativePointerManagerUnstableV1Announced, + &Registry::relativePointerManagerUnstableV1Removed }} }; @@ -549,6 +558,7 @@ BIND(TextInputManagerUnstableV0, wl_text_input_manager) BIND(TextInputManagerUnstableV2, zwp_text_input_manager_v2) BIND(XdgShellUnstableV5, xdg_shell) +BIND(RelativePointerManagerUnstableV1, zwp_relative_pointer_manager_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) @@ -628,6 +638,16 @@ } } +RelativePointerManager *Registry::createRelativePointerManager(quint32 name, quint32 version, QObject *parent) +{ + switch (d->interfaceForName(name)) { + case Interface::RelativePointerManagerUnstableV1: + return d->create(name, version, parent, &Registry::bindRelativePointerManagerUnstableV1); + default: + return nullptr; + } +} + namespace { static const wl_interface *wlInterface(Registry::Interface interface) { diff --git a/src/client/relativepointer.h b/src/client/relativepointer.h new file mode 100644 --- /dev/null +++ b/src/client/relativepointer.h @@ -0,0 +1,238 @@ +/**************************************************************************** +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_RELATIVEPOINTER_H +#define KWAYLAND_CLIENT_RELATIVEPOINTER_H + +#include + +#include + +struct zwp_relative_pointer_manager_v1; +struct zwp_relative_pointer_v1; + +namespace KWayland +{ +namespace Client +{ + +class EventQueue; +class Pointer; +class RelativePointer; + +/** + * @short Wrapper for the zwp_relative_pointer_manager_v1 interface. + * + * This class provides a convenient wrapper for the zwp_relative_pointer_manager_v1 interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the RelativePointerManager interface: + * @code + * RelativePointerManager *c = registry->createRelativePointerManagerUnstableV1(name, version); + * @endcode + * + * This creates the RelativePointerManager and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * RelativePointerManager *c = new RelativePointerManager; + * c->setup(registry->RelativePointerManager(name, version)); + * @endcode + * + * The RelativePointerManager can be used as a drop-in replacement for any zwp_relative_pointer_manager_v1 + * pointer as it provides matching cast operators. + * + * @see Registry + * @since 5.28 + **/ +class KWAYLANDCLIENT_EXPORT RelativePointerManager : public QObject +{ + Q_OBJECT +public: + /** + * Creates a new RelativePointerManager. + * Note: after constructing the RelativePointerManager it is not yet valid and one needs + * to call setup. In order to get a ready to use RelativePointerManager prefer using + * Registry::createRelativePointerManagerUnstableV1. + **/ + explicit RelativePointerManager(QObject *parent = nullptr); + virtual ~RelativePointerManager(); + + /** + * Setup this RelativePointerManagerUnstableV1 to manage the @p relativepointermanagerunstablev1. + * When using Registry::createRelativePointerManagerUnstableV1 there is no need to call this + * method. + **/ + void setup(zwp_relative_pointer_manager_v1 *relativepointermanagerunstablev1); + /** + * @returns @c true if managing a zwp_relative_pointer_manager_v1. + **/ + bool isValid() const; + /** + * Releases the zwp_relative_pointer_manager_v1 interface. + * After the interface has been released the RelativePointerManagerUnstableV1 instance is no + * longer valid and can be setup with another zwp_relative_pointer_manager_v1 interface. + **/ + void release(); + /** + * Destroys the data held by this RelativePointerManagerUnstableV1. + * 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_relative_pointer_manager_v1 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, relativepointermanagerunstablev1, &RelativePointerManagerUnstableV1::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the @p queue to use for creating objects with this RelativePointerManagerUnstableV1. + **/ + void setEventQueue(EventQueue *queue); + /** + * @returns The event queue to use for creating objects with this RelativePointerManagerUnstableV1. + **/ + EventQueue *eventQueue(); + + /** + * Creates a RelativePointer for the given @p pointer. + **/ + RelativePointer *createRelativePointer(Pointer *pointer, QObject *parent = nullptr); + + operator zwp_relative_pointer_manager_v1*(); + operator zwp_relative_pointer_manager_v1*() const; + +Q_SIGNALS: + /** + * The corresponding global for this interface on the Registry got removed. + * + * This signal gets only emitted if the RelativePointerManagerUnstableV1 got created by + * Registry::createRelativePointerManagerUnstableV1 + **/ + void removed(); + +private: + class Private; + QScopedPointer d; +}; + +/** + * @short Wrapper for the zwp_relative_pointer_v1 interface. + * + * The RelativePointer is an extension to the Pointer used for emitting + * relative pointer events. It shares the same focus as Pointer of the same Seat + * and will only emit events when it has focus. + * + * @since 5.28 + **/ +class KWAYLANDCLIENT_EXPORT RelativePointer : public QObject +{ + Q_OBJECT +public: + virtual ~RelativePointer(); + + /** + * Setup this RelativePointerUnstableV1 to manage the @p relativepointerunstablev1. + * When using RelativePointerManagerUnstableV1::createRelativePointerUnstableV1 there is no need to call this + * method. + **/ + void setup(zwp_relative_pointer_v1 *relativepointerunstablev1); + /** + * @returns @c true if managing a zwp_relative_pointer_v1. + **/ + bool isValid() const; + /** + * Releases the zwp_relative_pointer_v1 interface. + * After the interface has been released the RelativePointerUnstableV1 instance is no + * longer valid and can be setup with another zwp_relative_pointer_v1 interface. + **/ + void release(); + /** + * Destroys the data held by this RelativePointerUnstableV1. + * 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_relative_pointer_v1 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, relativepointerunstablev1, &RelativePointerUnstableV1::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + operator zwp_relative_pointer_v1*(); + operator zwp_relative_pointer_v1*() const; + +Q_SIGNALS: + /** + * A relative motion event. + * + * A relative motion is in the same dimension as regular motion events, + * except they do not represent an absolute position. For example, + * moving a pointer from (x, y) to (x', y') would have the equivalent + * relative motion (x' - x, y' - y). If a pointer motion caused the + * absolute pointer position to be clipped by for example the edge of the + * monitor, the relative motion is unaffected by the clipping and will + * represent the unclipped motion. + * + * This signal also contains non-accelerated motion deltas (@p deltaNonAccelerated). + * The non-accelerated delta is, when applicable, the regular pointer motion + * delta as it was before having applied motion acceleration and other + * transformations such as normalization. + * + * Note that the non-accelerated delta does not represent 'raw' events as + * they were read from some device. Pointer motion acceleration is device- + * and configuration-specific and non-accelerated deltas and accelerated + * deltas may have the same value on some devices. + * + * Relative motions are not coupled to Pointer motion events, + * and can be sent in combination with such events, but also independently. There may + * also be scenarios where Pointer motion is sent, but there is no + * relative motion. The order of an absolute and relative motion event + * originating from the same physical motion is not guaranteed. + * + * @param delta Motion vector + * @param deltaNonAccelerated non-accelerated motion vector + * @param microseconds timestamp with microseconds granularity + **/ + void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 timestamp); + +private: + friend class RelativePointerManager; + explicit RelativePointer(QObject *parent = nullptr); + class Private; + QScopedPointer d; +}; + + +} +} + +#endif diff --git a/src/client/relativepointer.cpp b/src/client/relativepointer.cpp new file mode 100644 --- /dev/null +++ b/src/client/relativepointer.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +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 "relativepointer.h" +#include "event_queue.h" +#include "pointer.h" +#include "wayland_pointer_p.h" +#include +#include + +namespace KWayland +{ +namespace Client +{ + +class RelativePointerManager::Private +{ +public: + Private() = default; + + WaylandPointer relativepointermanagerunstablev1; + EventQueue *queue = nullptr; +}; + +RelativePointerManager::RelativePointerManager(QObject *parent) + : QObject(parent) + , d(new Private) +{ +} + +RelativePointerManager::~RelativePointerManager() +{ + release(); +} + +void RelativePointerManager::setup(zwp_relative_pointer_manager_v1 *relativepointermanagerunstablev1) +{ + Q_ASSERT(relativepointermanagerunstablev1); + Q_ASSERT(!d->relativepointermanagerunstablev1); + d->relativepointermanagerunstablev1.setup(relativepointermanagerunstablev1); +} + +void RelativePointerManager::release() +{ + d->relativepointermanagerunstablev1.release(); +} + +void RelativePointerManager::destroy() +{ + d->relativepointermanagerunstablev1.destroy(); +} + +void RelativePointerManager::setEventQueue(EventQueue *queue) +{ + d->queue = queue; +} + +EventQueue *RelativePointerManager::eventQueue() +{ + return d->queue; +} + +RelativePointerManager::operator zwp_relative_pointer_manager_v1*() { + return d->relativepointermanagerunstablev1; +} + +RelativePointerManager::operator zwp_relative_pointer_manager_v1*() const { + return d->relativepointermanagerunstablev1; +} + +bool RelativePointerManager::isValid() const +{ + return d->relativepointermanagerunstablev1.isValid(); +} + +RelativePointer *RelativePointerManager::createRelativePointer(Pointer *pointer, QObject *parent) +{ + Q_ASSERT(isValid()); + RelativePointer *p = new RelativePointer(parent); + auto w = zwp_relative_pointer_manager_v1_get_relative_pointer(d->relativepointermanagerunstablev1, *pointer); + if (d->queue) { + d->queue->addProxy(w); + } + p->setup(w); + return p; +} + +class RelativePointer::Private +{ +public: + Private(RelativePointer *q); + + void setup(zwp_relative_pointer_v1 *relativepointerunstablev1); + + WaylandPointer relativepointerunstablev1; + +private: + static void relativeMotionCallback(void *data, zwp_relative_pointer_v1 *zwp_relative_pointer_v1, + uint32_t utime_hi, uint32_t utime_lo, + wl_fixed_t dx, wl_fixed_t dy, + wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel); + + RelativePointer *q; + + static const zwp_relative_pointer_v1_listener s_listener; +}; + +RelativePointer::Private::Private(RelativePointer *q) + : q(q) +{ +} + +const zwp_relative_pointer_v1_listener RelativePointer::Private::s_listener = { + relativeMotionCallback +}; + +void RelativePointer::Private::relativeMotionCallback(void *data, zwp_relative_pointer_v1 *zwp_relative_pointer_v1, + uint32_t utime_hi, uint32_t utime_lo, + wl_fixed_t dx, wl_fixed_t dy, + wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->relativepointerunstablev1 == zwp_relative_pointer_v1); + const QSizeF delta(wl_fixed_to_double(dx), wl_fixed_to_double(dy)); + const QSizeF deltaNonAccel(wl_fixed_to_double(dx_unaccel), wl_fixed_to_double(dy_unaccel)); + const quint64 timestamp = quint64(utime_lo) | (quint64(utime_hi) << 32); + emit p->q->relativeMotion(delta, deltaNonAccel, timestamp); +} + +void RelativePointer::Private::setup(zwp_relative_pointer_v1 *v1) +{ + Q_ASSERT(v1); + Q_ASSERT(!relativepointerunstablev1); + relativepointerunstablev1.setup(v1); + zwp_relative_pointer_v1_add_listener(relativepointerunstablev1, &s_listener, this); +} + +RelativePointer::RelativePointer(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +RelativePointer::~RelativePointer() +{ + release(); +} + +void RelativePointer::setup(zwp_relative_pointer_v1 *relativepointerunstablev1) +{ + d->setup(relativepointerunstablev1); +} + +void RelativePointer::release() +{ + d->relativepointerunstablev1.release(); +} + +void RelativePointer::destroy() +{ + d->relativepointerunstablev1.destroy(); +} + +RelativePointer::operator zwp_relative_pointer_v1*() { + return d->relativepointerunstablev1; +} + +RelativePointer::operator zwp_relative_pointer_v1*() const { + return d->relativepointerunstablev1; +} + +bool RelativePointer::isValid() const +{ + return d->relativepointerunstablev1.isValid(); +} + +} +} diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -23,6 +23,8 @@ plasmawindowmanagement_interface.cpp qtsurfaceextension_interface.cpp region_interface.cpp + relativepointer_interface.cpp + relativepointer_interface_v1.cpp resource.cpp seat_interface.cpp slide_interface.cpp @@ -96,6 +98,11 @@ ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${KWAYLAND_SOURCE_DIR}/src/client/protocols/relative-pointer-unstable-v1.xml + BASENAME relativepointer-unstable-v1 +) + +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWAYLAND_SOURCE_DIR}/src/client/protocols/slide.xml BASENAME slide ) @@ -180,6 +187,7 @@ plasmawindowmanagement_interface.h qtsurfaceextension_interface.h region_interface.h + relativepointer_interface.h resource.h seat_interface.h server_decoration_interface.h diff --git a/src/server/display.h b/src/server/display.h --- a/src/server/display.h +++ b/src/server/display.h @@ -74,6 +74,8 @@ class XdgShellV5Interface; enum class XdgShellInterfaceVersion; class XdgShellInterface; +enum class RelativePointerInterfaceVersion; +class RelativePointerManagerInterface; /** * @brief Class holding the Wayland server display loop. @@ -190,6 +192,14 @@ XdgShellInterface *createXdgShell(const XdgShellInterfaceVersion &version, QObject *parent = nullptr); /** + * Creates the RelativePointerManagerInterface in interface @p version + * + * @returns The created manager object + * @since 5.28 + **/ + RelativePointerManagerInterface *createRelativePointerManager(const RelativePointerInterfaceVersion &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 @@ -35,6 +35,7 @@ #include "shadow_interface.h" #include "blur_interface.h" #include "contrast_interface.h" +#include "relativepointer_interface_p.h" #include "server_decoration_interface.h" #include "slide_interface.h" #include "shell_interface.h" @@ -369,6 +370,18 @@ return x; } +RelativePointerManagerInterface *Display::createRelativePointerManager(const RelativePointerInterfaceVersion &version, QObject *parent) +{ + RelativePointerManagerInterface *r = nullptr; + switch (version) { + case RelativePointerInterfaceVersion::UnstableV1: + r = new RelativePointerManagerUnstableV1Interface(this, parent); + break; + } + connect(this, &Display::aboutToTerminate, r, [r] { delete r; }); + return r; +} + 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 RelativePointerManagerUnstableV1Interface; class SeatInterface; class SurfaceInterface; @@ -55,6 +56,12 @@ **/ Cursor *cursor() const; + /** + * @returns The PointerInterface for the @p native resource. + * @since 5.28 + **/ + static PointerInterface *get(wl_resource *native); + Q_SIGNALS: /** * Signal emitted whenever the Cursor changes. @@ -66,7 +73,9 @@ void buttonPressed(quint32 button, quint32 serial); void buttonReleased(quint32 button, quint32 serial); void axis(Qt::Orientation orientation, quint32 delta); + void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds); friend class SeatInterface; + friend class RelativePointerManagerUnstableV1Interface; 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 @@ -20,6 +20,7 @@ #include "pointer_interface.h" #include "pointer_interface_p.h" #include "resource_p.h" +#include "relativepointer_interface_p.h" #include "seat_interface.h" #include "display.h" #include "subcompositor_interface.h" @@ -77,6 +78,16 @@ } } +void PointerInterface::Private::registerRelativePointer(RelativePointerInterface *relativePointer) +{ + relativePointers << relativePointer; + QObject::connect(relativePointer, &QObject::destroyed, q, + [this, relativePointer] { + relativePointers.removeOne(relativePointer); + } + ); +} + namespace { static QPointF surfacePosition(SurfaceInterface *surface) { if (surface && surface->subSurface()) { @@ -211,11 +222,28 @@ return d->cursor; } +void PointerInterface::relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) +{ + Q_D(); + if (d->relativePointers.isEmpty()) { + return; + } + for (auto it = d->relativePointers.constBegin(), end = d->relativePointers.constEnd(); it != end; it++) { + (*it)->relativeMotion(delta, deltaNonAccelerated, microseconds); + } + client()->flush(); +} + PointerInterface::Private *PointerInterface::d_func() const { return reinterpret_cast(d.data()); } +PointerInterface *PointerInterface::get(wl_resource *native) +{ + return Private::get(native); +} + Cursor::Private::Private(Cursor *q, PointerInterface *pointer) : pointer(pointer) , q(q) 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 @@ -28,6 +28,7 @@ { namespace Server { +class RelativePointerInterface; class PointerInterface::Private : public Resource::Private { @@ -39,10 +40,13 @@ QPointer focusedChildSurface; QMetaObject::Connection destroyConnection; Cursor *cursor = nullptr; + QVector relativePointers; void sendLeave(SurfaceInterface *surface, quint32 serial); void sendEnter(SurfaceInterface *surface, const QPointF &parentSurfacePosition, quint32 serial); + void registerRelativePointer(RelativePointerInterface *relativePointer); + private: PointerInterface *q_func() { return reinterpret_cast(q); diff --git a/src/server/relativepointer_interface.h b/src/server/relativepointer_interface.h new file mode 100644 --- /dev/null +++ b/src/server/relativepointer_interface.h @@ -0,0 +1,73 @@ +/**************************************************************************** +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_RELATIVE_POINTER_H +#define KWAYLAND_SERVER_RELATIVE_POINTER_H + +#include "global.h" + +#include + +namespace KWayland +{ +namespace Server +{ + +class Display; + +enum class RelativePointerInterfaceVersion { + /** + * zwp_relative_pointer_manager_v1 and zwp_relative_pointer_v1 + **/ + UnstableV1 +}; + +/** + * Manager object to create relative pointer interfaces. + * + * Once created the interaction happens through the SeatInterface class + * which automatically delegates relative motion events to the created relative pointer + * interfaces. + * + * @see SeatInterface::relativePointerMotion + * @since 5.28 + **/ +class KWAYLANDSERVER_EXPORT RelativePointerManagerInterface : public Global +{ + Q_OBJECT +public: + virtual ~RelativePointerManagerInterface(); + + /** + * @returns The interface version used by this RelativePointerManagerInterface + **/ + RelativePointerInterfaceVersion interfaceVersion() const; + +protected: + class Private; + explicit RelativePointerManagerInterface(Private *d, QObject *parent = nullptr); + +private: + Private *d_func() const; +}; + +} +} + +#endif diff --git a/src/server/relativepointer_interface.cpp b/src/server/relativepointer_interface.cpp new file mode 100644 --- /dev/null +++ b/src/server/relativepointer_interface.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +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 "relativepointer_interface_p.h" + +namespace KWayland +{ +namespace Server +{ + +RelativePointerManagerInterface::Private::Private(RelativePointerInterfaceVersion interfaceVersion, RelativePointerManagerInterface *q, Display *d, const wl_interface *interface, quint32 version) + : Global::Private(d, interface, version) + , interfaceVersion(interfaceVersion) + , q(q) +{ +} + +RelativePointerManagerInterface::RelativePointerManagerInterface(Private *d, QObject *parent) + : Global(d, parent) +{ +} + +RelativePointerManagerInterface::~RelativePointerManagerInterface() = default; + +RelativePointerInterfaceVersion RelativePointerManagerInterface::interfaceVersion() const +{ + Q_D(); + return d->interfaceVersion; +} + +RelativePointerManagerInterface::Private *RelativePointerManagerInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +RelativePointerInterface::Private::Private(RelativePointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation) + : Resource::Private(q, c, parentResource, interface, implementation) +{ +} + +RelativePointerInterface::Private::~Private() +{ + if (resource) { + wl_resource_destroy(resource); + resource = nullptr; + } +} + +RelativePointerInterface::RelativePointerInterface(Private *p, QObject *parent) + : Resource(p, parent) +{ +} + +RelativePointerInterface::~RelativePointerInterface() = default; + +void RelativePointerInterface::relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) +{ + Q_D(); + d->relativeMotion(delta, deltaNonAccelerated, microseconds); +} + +RelativePointerInterface::Private *RelativePointerInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +} +} diff --git a/src/server/relativepointer_interface_p.h b/src/server/relativepointer_interface_p.h new file mode 100644 --- /dev/null +++ b/src/server/relativepointer_interface_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +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_RELATIVEPOINTER_INTERFACE_P_H +#define KWAYLAND_SERVER_RELATIVEPOINTER_INTERFACE_P_H +#include "relativepointer_interface.h" +#include "resource_p.h" +#include "global_p.h" + + +namespace KWayland +{ +namespace Server +{ + +class RelativePointerManagerInterface::Private : public Global::Private +{ +public: + RelativePointerInterfaceVersion interfaceVersion; + +protected: + Private(RelativePointerInterfaceVersion interfaceVersion, RelativePointerManagerInterface *q, Display *d, const wl_interface *interface, quint32 version); + RelativePointerManagerInterface *q; +}; + +class RelativePointerManagerUnstableV1Interface : public RelativePointerManagerInterface +{ + Q_OBJECT +public: + explicit RelativePointerManagerUnstableV1Interface(Display *display, QObject *parent = nullptr); + virtual ~RelativePointerManagerUnstableV1Interface(); + +private: + class Private; +}; + +class RelativePointerInterface : public Resource +{ + Q_OBJECT +public: + virtual ~RelativePointerInterface(); + void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds); + +protected: + class Private; + explicit RelativePointerInterface(Private *p, QObject *parent = nullptr); + +private: + Private *d_func() const; +}; + +class RelativePointerInterface::Private : public Resource::Private +{ +public: + ~Private(); + virtual void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) = 0; + +protected: + Private(RelativePointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation); + +private: + RelativePointerInterface *q_func() { + return reinterpret_cast(q); + } +}; + +class RelativePointerUnstableV1Interface : public RelativePointerInterface +{ + Q_OBJECT +public: + virtual ~RelativePointerUnstableV1Interface(); + +private: + explicit RelativePointerUnstableV1Interface(RelativePointerManagerUnstableV1Interface *parent, wl_resource *parentResource); + friend class RelativePointerManagerUnstableV1Interface; + + class Private; + Private *d_func() const; +}; + +} +} + +#endif diff --git a/src/server/relativepointer_interface_v1.cpp b/src/server/relativepointer_interface_v1.cpp new file mode 100644 --- /dev/null +++ b/src/server/relativepointer_interface_v1.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +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 "relativepointer_interface_p.h" +#include "display.h" +#include "pointer_interface_p.h" +#include +#include + +namespace KWayland +{ +namespace Server +{ + +class RelativePointerManagerUnstableV1Interface::Private : public RelativePointerManagerInterface::Private +{ +public: + Private(RelativePointerManagerUnstableV1Interface *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 destroyCallback(wl_client *client, wl_resource *resource); + static void getRelativePointerCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * pointer); + + RelativePointerManagerUnstableV1Interface *q; + + static const struct zwp_relative_pointer_manager_v1_interface s_interface; + static const quint32 s_version; +}; + +const quint32 RelativePointerManagerUnstableV1Interface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwp_relative_pointer_manager_v1_interface RelativePointerManagerUnstableV1Interface::Private::s_interface = { + destroyCallback, + getRelativePointerCallback +}; +#endif + +void RelativePointerManagerUnstableV1Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + wl_resource_destroy(resource); +} + +void RelativePointerManagerUnstableV1Interface::Private::getRelativePointerCallback(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 *r = new RelativePointerUnstableV1Interface(m->q, resource); + r->d->create(m->display->getConnection(client), version, id); + p->d_func()->registerRelativePointer(r); +} + +RelativePointerManagerUnstableV1Interface::Private::Private(RelativePointerManagerUnstableV1Interface *q, Display *d) + : RelativePointerManagerInterface::Private(RelativePointerInterfaceVersion::UnstableV1, q, d, &zwp_relative_pointer_manager_v1_interface, s_version) + , q(q) +{ +} + +void RelativePointerManagerUnstableV1Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&zwp_relative_pointer_manager_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 RelativePointerManagerUnstableV1Interface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) + // TODO: implement? +} + +RelativePointerManagerUnstableV1Interface::RelativePointerManagerUnstableV1Interface(Display *display, QObject *parent) + : RelativePointerManagerInterface(new Private(this, display), parent) +{ +} + +RelativePointerManagerUnstableV1Interface::~RelativePointerManagerUnstableV1Interface() = default; + +class RelativePointerUnstableV1Interface::Private : public RelativePointerInterface::Private +{ +public: + Private(RelativePointerUnstableV1Interface *q, RelativePointerManagerUnstableV1Interface *c, wl_resource *parentResource); + ~Private(); + void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) override; + +private: + RelativePointerUnstableV1Interface *q_func() { + return reinterpret_cast(q); + } + + static const struct zwp_relative_pointer_v1_interface s_interface; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwp_relative_pointer_v1_interface RelativePointerUnstableV1Interface::Private::s_interface = { + resourceDestroyedCallback +}; +#endif + +RelativePointerUnstableV1Interface::Private::Private(RelativePointerUnstableV1Interface *q, RelativePointerManagerUnstableV1Interface *c, wl_resource *parentResource) + : RelativePointerInterface::Private(q, c, parentResource, &zwp_relative_pointer_v1_interface, &s_interface) +{ +} + +RelativePointerUnstableV1Interface::Private::~Private() = default; + +void RelativePointerUnstableV1Interface::Private::relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) +{ + if (!resource) { + return; + } + zwp_relative_pointer_v1_send_relative_motion(resource, (microseconds >> 32), microseconds, + wl_fixed_from_double(delta.width()), + wl_fixed_from_double(delta.height()), + wl_fixed_from_double(deltaNonAccelerated.width()), + wl_fixed_from_double(deltaNonAccelerated.height())); +} + +RelativePointerUnstableV1Interface::RelativePointerUnstableV1Interface(RelativePointerManagerUnstableV1Interface *parent, wl_resource *parentResource) + : RelativePointerInterface(new Private(this, parent, parentResource)) +{ +} + +RelativePointerUnstableV1Interface::~RelativePointerUnstableV1Interface() = default; + +} +} 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 @@ -367,6 +367,42 @@ * @since 5.6 **/ bool hasImplicitPointerGrab(quint32 serial) const; + + /** + * A relative motion is in the same dimension as regular motion events, + * except they do not represent an absolute position. For example, + * moving a pointer from (x, y) to (x', y') would have the equivalent + * relative motion (x' - x, y' - y). If a pointer motion caused the + * absolute pointer position to be clipped by for example the edge of the + * monitor, the relative motion is unaffected by the clipping and will + * represent the unclipped motion. + * + * This method also contains non-accelerated motion deltas (@p deltaNonAccelerated). + * The non-accelerated delta is, when applicable, the regular pointer motion + * delta as it was before having applied motion acceleration and other + * transformations such as normalization. + * + * Note that the non-accelerated delta does not represent 'raw' events as + * they were read from some device. Pointer motion acceleration is device- + * and configuration-specific and non-accelerated deltas and accelerated + * deltas may have the same value on some devices. + * + * Relative motions are not coupled to wl_pointer.motion events (see @link{setPointerPos}, + * and can be sent in combination with such events, but also independently. There may + * also be scenarios where wl_pointer.motion is sent, but there is no + * relative motion. The order of an absolute and relative motion event + * originating from the same physical motion is not guaranteed. + * + * Sending relative pointer events only makes sense if the RelativePointerManagerInterface + * is created on the Display. + * + * @param delta Motion vector + * @param deltaNonAccelerated non-accelerated motion vector + * @param microseconds timestamp with microseconds granularity + * @see setPointerPos + * @since 5.28 + **/ + void relativePointerMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds); ///@} /** 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 @@ -846,6 +846,14 @@ return it.value(); } +void SeatInterface::relativePointerMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) +{ + Q_D(); + if (d->globalPointer.focus.pointer && d->globalPointer.focus.surface) { + d->globalPointer.focus.pointer->relativeMotion(delta, deltaNonAccelerated, microseconds); + } +} + void SeatInterface::keyPressed(quint32 key) { Q_D(); diff --git a/src/tools/mapping.txt b/src/tools/mapping.txt --- a/src/tools/mapping.txt +++ b/src/tools/mapping.txt @@ -47,3 +47,5 @@ xdg_shell;XdgShellV5 xdg_surface;XdgSurfaceV5 xdg_popup;XdgPopupV5 +zwp_relative_pointer_manager_v1;RelativePointerManagerUnstableV1 +zwp_relative_pointer_v1;RelativePointerUnstableV1