Commit afdd72ab authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

Rewrite wl_pointer implementation with new approach

With this design, a single PointerInterface manages multiple wl_pointer
objects. This makes the API tidier and allows implementing things such as
keyboard grabs more easier.

In addition to that, the PointerInterface doesn't inject its own frame
events anymore. It's up to the compositor to decide when it has to be
sent. However, the PointerInterface may still send a frame event if the
pointer focus changes.

Besides re-writing the pointer interface, this change, unfortunately,
also affects the implementation of pointer-gestures and relative-pointer
protocols because previously they were coupled to individual instances
of PointerInterface.
parent 2b855f6b
......@@ -254,12 +254,14 @@ void TestDataDevice::testDrag()
if (!hasGrab) {
// in case we don't have grab, still generate a pointer serial to make it more interesting
m_seatInterface->pointerButtonPressed(Qt::LeftButton);
m_seatInterface->pointerFrame();
}
if (hasPointerFocus) {
m_seatInterface->setFocusedPointerSurface(surfaceInterface);
}
if (hasGrab) {
m_seatInterface->pointerButtonPressed(Qt::LeftButton);
m_seatInterface->pointerFrame();
}
// TODO: This test would be better, if it could also test that a client trying to guess
......@@ -333,12 +335,14 @@ void TestDataDevice::testDragInternally()
if (!hasGrab) {
// in case we don't have grab, still generate a pointer serial to make it more interesting
m_seatInterface->pointerButtonPressed(Qt::LeftButton);
m_seatInterface->pointerFrame();
}
if (hasPointerFocus) {
m_seatInterface->setFocusedPointerSurface(surfaceInterface);
}
if (hasGrab) {
m_seatInterface->pointerButtonPressed(Qt::LeftButton);
m_seatInterface->pointerFrame();
}
// TODO: This test would be better, if it could also test that a client trying to guess
......
......@@ -209,6 +209,7 @@ void TestDragAndDrop::testPointerDragAndDrop()
m_seatInterface->setFocusedPointerSurface(serverSurface);
m_seatInterface->setTimestamp(2);
m_seatInterface->pointerButtonPressed(1);
m_seatInterface->pointerFrame();
QVERIFY(buttonPressSpy.wait());
QCOMPARE(buttonPressSpy.first().at(1).value<quint32>(), quint32(2));
......@@ -256,6 +257,7 @@ void TestDragAndDrop::testPointerDragAndDrop()
// simulate motion
m_seatInterface->setTimestamp(3);
m_seatInterface->setPointerPos(QPointF(3, 3));
m_seatInterface->pointerFrame();
QVERIFY(dragMotionSpy.wait());
QCOMPARE(dragMotionSpy.count(), 1);
QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(3, 3));
......@@ -268,6 +270,7 @@ void TestDragAndDrop::testPointerDragAndDrop()
QVERIFY(droppedSpy.isValid());
m_seatInterface->setTimestamp(4);
m_seatInterface->pointerButtonReleased(1);
m_seatInterface->pointerFrame();
QVERIFY(sourceDropSpy.isEmpty());
QVERIFY(droppedSpy.wait());
QCOMPARE(sourceDropSpy.count(), 1);
......@@ -405,6 +408,7 @@ void TestDragAndDrop::testDragAndDropWithCancelByDestroyDataSource()
m_seatInterface->setFocusedPointerSurface(serverSurface);
m_seatInterface->setTimestamp(2);
m_seatInterface->pointerButtonPressed(1);
m_seatInterface->pointerFrame();
QVERIFY(buttonPressSpy.wait());
QCOMPARE(buttonPressSpy.first().at(1).value<quint32>(), quint32(2));
......@@ -452,6 +456,7 @@ void TestDragAndDrop::testDragAndDropWithCancelByDestroyDataSource()
// simulate motion
m_seatInterface->setTimestamp(3);
m_seatInterface->setPointerPos(QPointF(3, 3));
m_seatInterface->pointerFrame();
QVERIFY(dragMotionSpy.wait());
QCOMPARE(dragMotionSpy.count(), 1);
QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(3, 3));
......@@ -472,6 +477,7 @@ void TestDragAndDrop::testDragAndDropWithCancelByDestroyDataSource()
QVERIFY(droppedSpy.isValid());
m_seatInterface->setTimestamp(4);
m_seatInterface->pointerButtonReleased(1);
m_seatInterface->pointerFrame();
QVERIFY(!droppedSpy.wait(500));
// verify that we did not get any further input events
......@@ -511,7 +517,8 @@ void TestDragAndDrop::testPointerEventsIgnored()
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->setPointerPos(QPointF(10, 10));
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->pointerAxis(Qt::Vertical, 5);
m_seatInterface->pointerAxis(Qt::Vertical, 5, 1, PointerAxisSource::Wheel);
m_seatInterface->pointerFrame();
// verify that we have those
QVERIFY(axisSpy.wait());
QCOMPARE(axisSpy.count(), 1);
......@@ -523,6 +530,7 @@ void TestDragAndDrop::testPointerEventsIgnored()
// let's start the drag
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->pointerButtonPressed(1);
m_seatInterface->pointerFrame();
QVERIFY(buttonSpy.wait());
QCOMPARE(buttonSpy.count(), 1);
m_dataDevice->startDrag(buttonSpy.first().first().value<quint32>(), m_dataSource, s.data());
......@@ -531,24 +539,32 @@ void TestDragAndDrop::testPointerEventsIgnored()
// now simulate all the possible pointer interactions
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->pointerButtonPressed(2);
m_seatInterface->pointerFrame();
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->pointerButtonReleased(2);
m_seatInterface->pointerFrame();
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->pointerAxis(Qt::Vertical, 5);
m_seatInterface->pointerAxis(Qt::Vertical, 5, 1, PointerAxisSource::Wheel);
m_seatInterface->pointerFrame();
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->pointerAxis(Qt::Horizontal, 5);
m_seatInterface->pointerAxis(Qt::Vertical, 5, 1, PointerAxisSource::Wheel);
m_seatInterface->pointerFrame();
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->setFocusedPointerSurface(nullptr);
m_seatInterface->pointerFrame();
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->setFocusedPointerSurface(serverSurface);
m_seatInterface->pointerFrame();
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->setPointerPos(QPointF(50, 50));
m_seatInterface->pointerFrame();
// last but not least, simulate the drop
QSignalSpy cancelledSpy(m_dataSource, &DataSource::cancelled);
QVERIFY(cancelledSpy.isValid());
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->pointerButtonReleased(1);
m_seatInterface->pointerFrame();
QVERIFY(cancelledSpy.wait());
// all the changes should have been ignored
......
......@@ -225,11 +225,13 @@ void TestPointerConstraints::testLockPointer()
QSignalSpy pointerMotionSpy(m_pointer, &Pointer::motion);
QVERIFY(pointerMotionSpy.isValid());
m_seatInterface->setPointerPos(QPoint(0, 1));
m_seatInterface->pointerFrame();
QVERIFY(pointerMotionSpy.wait());
serverLockedPointer->setLocked(true);
QCOMPARE(serverLockedPointer->isLocked(), true);
m_seatInterface->setPointerPos(QPoint(1, 1));
m_seatInterface->pointerFrame();
QCOMPARE(lockedChangedSpy.count(), 1);
QCOMPARE(pointerMotionSpy.count(), 1);
QVERIFY(lockedSpy.isEmpty());
......@@ -257,6 +259,7 @@ void TestPointerConstraints::testLockPointer()
// now motion should work again
m_seatInterface->setPointerPos(QPoint(0, 1));
m_seatInterface->pointerFrame();
QVERIFY(pointerMotionSpy.wait());
QCOMPARE(pointerMotionSpy.count(), 2);
......
This diff is collapsed.
......@@ -102,8 +102,7 @@ void TestWaylandServerSeat::testPointerButton()
display.addSocketName(s_socketName);
display.start();
SeatInterface *seat = new SeatInterface(&display);
PointerInterface *pointer = seat->focusedPointer();
QVERIFY(!pointer);
seat->setHasPointer(true);
// no button pressed yet, should be released and no serial
QVERIFY(!seat->isPointerButtonPressed(0));
......@@ -113,6 +112,7 @@ void TestWaylandServerSeat::testPointerButton()
// mark the button as pressed
seat->pointerButtonPressed(0);
seat->pointerFrame();
QVERIFY(seat->isPointerButtonPressed(0));
QCOMPARE(seat->pointerButtonSerial(0), display.serial());
......@@ -122,6 +122,7 @@ void TestWaylandServerSeat::testPointerButton()
// release it again
seat->pointerButtonReleased(0);
seat->pointerFrame();
QVERIFY(!seat->isPointerButtonPressed(0));
QCOMPARE(seat->pointerButtonSerial(0), display.serial());
}
......@@ -132,22 +133,24 @@ void TestWaylandServerSeat::testPointerPos()
display.addSocketName(s_socketName);
display.start();
SeatInterface *seat = new SeatInterface(&display);
seat->setHasPointer(true);
QSignalSpy seatPosSpy(seat, SIGNAL(pointerPosChanged(QPointF)));
QVERIFY(seatPosSpy.isValid());
PointerInterface *pointer = seat->focusedPointer();
QVERIFY(!pointer);
QCOMPARE(seat->pointerPos(), QPointF());
seat->setPointerPos(QPointF(10, 15));
seat->pointerFrame();
QCOMPARE(seat->pointerPos(), QPointF(10, 15));
QCOMPARE(seatPosSpy.count(), 1);
QCOMPARE(seatPosSpy.first().first().toPointF(), QPointF(10, 15));
seat->setPointerPos(QPointF(10, 15));
seat->pointerFrame();
QCOMPARE(seatPosSpy.count(), 1);
seat->setPointerPos(QPointF(5, 7));
seat->pointerFrame();
QCOMPARE(seat->pointerPos(), QPointF(5, 7));
QCOMPARE(seatPosSpy.count(), 2);
QCOMPARE(seatPosSpy.first().first().toPointF(), QPointF(10, 15));
......
This diff is collapsed.
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 Adrien Faveraux <ad1rie3@hotmail.fr>
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
......@@ -8,93 +10,106 @@
#include <KWaylandServer/kwaylandserver_export.h>
#include "resource.h"
#include <QObject>
struct wl_resource;
namespace KWaylandServer
{
class CursorPrivate;
class Cursor;
class PointerInterfacePrivate;
class SeatInterface;
class SurfaceInterface;
enum class PointerAxisSource;
/**
* @brief Resource for the wl_pointer interface.
*
* @see SeatInterface
**/
class KWAYLANDSERVER_EXPORT PointerInterface : public Resource
* The PointerInterface class represents one or more input devices such as mice, which control
* the pointer location. It corresponds to the Wayland interface @c wl_pointer.
*/
class KWAYLANDSERVER_EXPORT PointerInterface : public QObject
{
Q_OBJECT
public:
virtual ~PointerInterface();
~PointerInterface() override;
/**
* @returns the focused SurfaceInterface on this pointer resource, if any.
**/
* Returns the focused pointer surface. Note that the returned value may be different
* from SurfaceInterface::focusedSurfacePointerSurface() because this function returns
* the effective focused surface.
*/
SurfaceInterface *focusedSurface() const;
/**
* The Cursor set on this PointerInterface. Might be @c null.
* @since 5.3
**/
* Sets the effective focused pointer surface to @a surface. The @a position indicates
* where the pointer has entered the surface.
*/
void setFocusedSurface(SurfaceInterface *surface, const QPointF &position, quint32 serial);
Cursor *cursor() const;
/**
* Returns the seat to which this pointer belongs to.
*/
SeatInterface *seat() const;
/**
* @returns The PointerInterface for the @p native resource.
* @since 5.28
**/
*/
static PointerInterface *get(wl_resource *native);
void sendPressed(quint32 button, quint32 serial);
void sendReleased(quint32 button, quint32 serial);
void sendAxis(Qt::Orientation orientation, qreal delta, qint32 discreteDelta, PointerAxisSource source);
void sendMotion(const QPointF &position);
void sendFrame();
Q_SIGNALS:
/**
* Signal emitted whenever the Cursor changes.
**/
* This signal is emitted whenever the cursor surface changes. As long as there is no
* any focused surface, the cursor cannot be changed.
*/
void cursorChanged();
/**
* This signal is emitted whenever the focused pointer surface changes.
*/
void focusedSurfaceChanged();
private:
void setFocusedSurface(SurfaceInterface *surface, quint32 serial);
void buttonPressed(quint32 button, quint32 serial);
void buttonReleased(quint32 button, quint32 serial);
void axis(Qt::Orientation orientation, qreal delta, qint32 discreteDelta, PointerAxisSource source);
void axis(Qt::Orientation orientation, quint32 delta);
void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds);
explicit PointerInterface(SeatInterface *seat);
QScopedPointer<PointerInterfacePrivate> d;
friend class SeatInterface;
friend class RelativePointerV1Interface;
friend class PointerPinchGestureV1Interface;
friend class PointerSwipeGestureV1Interface;
explicit PointerInterface(SeatInterface *parent, wl_resource *parentResource);
class Private;
Private *d_func() const;
friend class PointerInterfacePrivate;
};
/**
* @brief Class encapsulating a Cursor image.
*
* @since 5.3
**/
*/
class KWAYLANDSERVER_EXPORT Cursor : public QObject
{
Q_OBJECT
public:
virtual ~Cursor();
/**
* The hotspot of the cursor image in surface-relative coordinates.
**/
*/
QPoint hotspot() const;
/**
* The entered serial when the Cursor got set.
**/
*/
quint32 enteredSerial() const;
/**
* The PointerInterface this Cursor belongs to.
**/
*/
PointerInterface *pointer() const;
/**
* The SurfaceInterface for the image content of the Cursor.
**/
QPointer<SurfaceInterface> surface() const;
*/
SurfaceInterface *surface() const;
Q_SIGNALS:
void hotspotChanged();
......@@ -103,14 +118,11 @@ Q_SIGNALS:
void changed();
private:
friend class PointerInterface;
Cursor(PointerInterface *parent);
class Private;
const QScopedPointer<Private> d;
QScopedPointer<CursorPrivate> d;
friend class PointerInterfacePrivate;
explicit Cursor(PointerInterface *parent);
};
}
Q_DECLARE_METATYPE(KWaylandServer::PointerInterface*)
#endif
/*
SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 Adrien Faveraux <ad1rie3@hotmail.fr>
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef WAYLAND_SERVER_POINTER_INTERFACE_P_H
#define WAYLAND_SERVER_POINTER_INTERFACE_P_H
#include "pointer_interface.h"
#include "resource_p.h"
#include <QPointF>
#include <QPointer>
#include <QVector>
#include "qwayland-server-wayland.h"
namespace KWaylandServer
{
class ClientConnection;
class PointerPinchGestureV1Interface;
class PointerSwipeGestureV1Interface;
class RelativePointerV1Interface;
class PointerInterface::Private : public Resource::Private
class PointerInterfacePrivate : public QtWaylandServer::wl_pointer
{
public:
Private(SeatInterface *parent, wl_resource *parentResource, PointerInterface *q);
static PointerInterfacePrivate *get(PointerInterface *pointer);
PointerInterfacePrivate(PointerInterface *q, SeatInterface *seat);
~PointerInterfacePrivate() override;
QList<Resource *> pointersForClient(ClientConnection *client) const;
PointerInterface *q;
SeatInterface *seat;
SurfaceInterface *focusedSurface = nullptr;
QPointer<SurfaceInterface> focusedChildSurface;
QMetaObject::Connection destroyConnection;
Cursor *cursor = nullptr;
QVector<RelativePointerV1Interface *> relativePointersV1;
QVector<PointerSwipeGestureV1Interface *> swipeGesturesV1;
QVector<PointerPinchGestureV1Interface *> pinchGesturesV1;
QScopedPointer<RelativePointerV1Interface> relativePointersV1;
QScopedPointer<PointerSwipeGestureV1Interface> swipeGesturesV1;
QScopedPointer<PointerPinchGestureV1Interface> pinchGesturesV1;
QPointF lastPosition;
void sendLeave(SurfaceInterface *surface, quint32 serial);
void sendEnter(SurfaceInterface *surface, const QPointF &parentSurfacePosition, quint32 serial);
void sendLeave(quint32 serial);
void sendEnter(const QPointF &parentSurfacePosition, quint32 serial);
void sendFrame();
void registerRelativePointerV1(RelativePointerV1Interface *relativePointer);
void registerSwipeGestureV1(PointerSwipeGestureV1Interface *gesture);
void registerPinchGestureV1(PointerPinchGestureV1Interface *gesture);
void unregisterRelativePointerV1(RelativePointerV1Interface *relativePointer);
void unregisterSwipeGestureV1(PointerSwipeGestureV1Interface *gesture);
void unregisterPinchGestureV1(PointerPinchGestureV1Interface *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() {
return reinterpret_cast<PointerInterface *>(q);
}
void setCursor(quint32 serial, SurfaceInterface *surface, const QPoint &hotspot);
// interface
static void setCursorCallback(wl_client *client, wl_resource *resource, uint32_t serial,
wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y);
static const struct wl_pointer_interface s_interface;
protected:
void pointer_set_cursor(Resource *resource, uint32_t serial,
::wl_resource *surface_resource,
int32_t hotspot_x, int32_t hotspot_y) override;
void pointer_release(Resource *resource) override;
void pointer_bind_resource(Resource *resource) override;
};
}
......
......@@ -6,14 +6,17 @@
*/
#include "pointergestures_v1_interface.h"
#include "clientconnection.h"
#include "display.h"
#include "pointer_interface_p.h"
#include "pointergestures_v1_interface_p.h"
#include "seat_interface.h"
#include "surface_interface.h"
namespace KWaylandServer
{
static const int s_version = 1;
static const int s_version = 2;
PointerGesturesV1InterfacePrivate::PointerGesturesV1InterfacePrivate(Display *display)
: QtWaylandServer::zwp_pointer_gestures_v1(*display, s_version)
......@@ -29,14 +32,8 @@ void PointerGesturesV1InterfacePrivate::zwp_pointer_gestures_v1_get_swipe_gestur
return;
}
wl_resource *swipeResource = wl_resource_create(resource->client(), &zwp_pointer_gesture_swipe_v1_interface,
resource->version(), id);
if (!swipeResource) {
wl_resource_post_no_memory(resource->handle);
return;
}
new PointerSwipeGestureV1Interface(pointer, swipeResource);
PointerSwipeGestureV1Interface *swipeGesture = PointerSwipeGestureV1Interface::get(pointer);
swipeGesture->add(resource->client(), id, resource->version());
}
void PointerGesturesV1InterfacePrivate::zwp_pointer_gestures_v1_get_pinch_gesture(Resource *resource, uint32_t id, struct ::wl_resource *pointer_resource)
......@@ -48,14 +45,8 @@ void PointerGesturesV1InterfacePrivate::zwp_pointer_gestures_v1_get_pinch_gestur
return;
}
wl_resource *pinchResource = wl_resource_create(resource->client(), &zwp_pointer_gesture_pinch_v1_interface,
resource->version(), id);
if (!pinchResource) {
wl_resource_post_no_memory(resource->handle);
return;
}
new PointerPinchGestureV1Interface(pointer, pinchResource);
PointerPinchGestureV1Interface *pinchGesture = PointerPinchGestureV1Interface::get(pointer);
pinchGesture->add(resource->client(), id, resource->version());
}
void PointerGesturesV1InterfacePrivate::zwp_pointer_gestures_v1_release(Resource *resource)
......@@ -73,51 +64,113 @@ PointerGesturesV1Interface::~PointerGesturesV1Interface()
{
}
PointerSwipeGestureV1Interface::PointerSwipeGestureV1Interface(PointerInterface *pointer,
::wl_resource *resource)
: QtWaylandServer::zwp_pointer_gesture_swipe_v1(resource)
, pointer(pointer)
PointerSwipeGestureV1Interface::PointerSwipeGestureV1Interface(PointerInterface *pointer)
: pointer(pointer)
{
pointer->d_func()->registerSwipeGestureV1(this);
}
PointerSwipeGestureV1Interface::~PointerSwipeGestureV1Interface()
PointerSwipeGestureV1Interface *PointerSwipeGestureV1Interface::get(PointerInterface *pointer)
{
if (pointer) {
pointer->d_func()->unregisterSwipeGestureV1(this);
PointerInterfacePrivate *pointerPrivate = PointerInterfacePrivate::get(pointer);
return pointerPrivate->swipeGesturesV1.data();
}
return nullptr;
}
void PointerSwipeGestureV1Interface::zwp_pointer_gesture_swipe_v1_destroy_resource(Resource *resource)
void PointerSwipeGestureV1Interface::zwp_pointer_gesture_swipe_v1_destroy(Resource *resource)
{
Q_UNUSED(resource)
delete this;
wl_resource_destroy(resource->handle);
}
void PointerSwipeGestureV1Interface::zwp_pointer_gesture_swipe_v1_destroy(Resource *resource)
void PointerSwipeGestureV1Interface::sendBegin(quint32 serial, quint32 fingerCount)
{
wl_resource_destroy(resource->handle);
if (focusedClient) {
return;
}
if (!pointer->focusedSurface()) {
return;
}
const SurfaceInterface *focusedSurface = pointer->focusedSurface();
focusedClient = focusedSurface->client();
SeatInterface *seat = pointer->seat();
const QList<Resource *> swipeResources = resourceMap().values(focusedClient->client());
for (Resource *swipeResource : swipeResources) {
if (swipeResource->client() == focusedClient->client()) {
send_begin(swipeResource->handle, serial, seat->timestamp(), focusedSurface->resource(), fingerCount);
}
}
}
PointerPinchGestureV1Interface::PointerPinchGestureV1Interface(PointerInterface *pointer,
::wl_resource *resource)
: QtWaylandServer::zwp_pointer_gesture_pinch_v1(resource)
, pointer(pointer)
void PointerSwipeGestureV1Interface::sendUpdate(const QSizeF &delta)
{
pointer->d_func()->registerPinchGestureV1(this);
if (!focusedClient) {
return;
}
SeatInterface *seat = pointer->seat();
const QList<Resource *> swipeResources = resourceMap().values(focusedClient->client());
for (Resource *swipeResource : swipeResources) {
if (swipeResource->client() == focusedClient->client()) {
send_update(swipeResource->handle, seat->timestamp(),
wl_fixed_from_double(delta.width()), wl_fixed_from_double(delta.height()));
}
}
}
PointerPinchGestureV1Interface::~PointerPinchGestureV1Interface()
void PointerSwipeGestureV1Interface::sendEnd(quint32 serial)
{
if (pointer) {
pointer->d_func()->unregisterPinchGestureV1(this);
if (!focusedClient) {
return;
}