From e7e6d60a206b8fd6bb9170cc750f4d87b28b3c18 Mon Sep 17 00:00:00 2001
From: Johan Klokkahmmer Helsing <johan.helsing@qt.io>
Date: Thu, 29 Sep 2016 14:59:56 +0200
Subject: [PATCH] Compositor: Send touch events to the surface pressed

This changes the API so that QWaylandTouch does not make assumptions
about touch focus, and consequently enables writing compositors where
multiple clients can receive touch at the same time.

Change-Id: I21fe6d9b5ac65e9f910f64cc4a02824119571c52
---
 src/compositor/compositor_api/qwaylandinput.cpp    |  31 +++---
 src/compositor/compositor_api/qwaylandinput.h      |   8 +-
 .../compositor_api/qwaylandquickitem.cpp           |  25 +----
 src/compositor/compositor_api/qwaylandquickitem.h  |   1 -
 src/compositor/compositor_api/qwaylandtouch.cpp    | 109 ++++++---------------
 src/compositor/compositor_api/qwaylandtouch.h      |  14 +--
 src/compositor/compositor_api/qwaylandtouch_p.h    |  23 +----
 src/compositor/extensions/qwlqttouch.cpp           |   4 +-
 src/compositor/extensions/qwlqttouch_p.h           |   2 +-
 9 files changed, 57 insertions(+), 160 deletions(-)

diff --git a/src/compositor/compositor_api/qwaylandinput.cpp b/src/compositor/compositor_api/qwaylandinput.cpp
index 6f0f8c7..ea3f1c2 100644
--- a/src/compositor/compositor_api/qwaylandinput.cpp
+++ b/src/compositor/compositor_api/qwaylandinput.cpp
@@ -243,52 +243,47 @@ void QWaylandInputDevice::sendKeyReleaseEvent(uint code)
  * Sends a touch point event with the given \a id and \a state to the touch device. The position
  * of the touch point is given by \a point.
  */
-void QWaylandInputDevice::sendTouchPointEvent(int id, const QPointF &point, Qt::TouchPointState state)
+void QWaylandInputDevice::sendTouchPointEvent(QWaylandSurface *surface, int id, const QPointF &point, Qt::TouchPointState state)
 {
     Q_D(QWaylandInputDevice);
-    if (d->touch.isNull()) {
+
+    if (d->touch.isNull())
         return;
-    }
-    d->touch->sendTouchPointEvent(id, point,state);
+
+    d->touch->sendTouchPointEvent(surface, id, point,state);
 }
 
 /*!
  * Sends a frame event to the touch device.
  */
-void QWaylandInputDevice::sendTouchFrameEvent()
+void QWaylandInputDevice::sendTouchFrameEvent(QWaylandClient *client)
 {
     Q_D(QWaylandInputDevice);
-    if (!d->touch.isNull()) {
-        d->touch->sendFrameEvent();
-    }
+    if (!d->touch.isNull())
+        d->touch->sendFrameEvent(client);
 }
 
 /*!
  * Sends a cancel event to the touch device.
  */
-void QWaylandInputDevice::sendTouchCancelEvent()
+void QWaylandInputDevice::sendTouchCancelEvent(QWaylandClient *client)
 {
     Q_D(QWaylandInputDevice);
-    if (!d->touch.isNull()) {
-        d->touch->sendCancelEvent();
-    }
+    if (!d->touch.isNull())
+        d->touch->sendCancelEvent(client);
 }
 
 /*!
  * Sends the \a event to the touch device.
  */
-void QWaylandInputDevice::sendFullTouchEvent(QTouchEvent *event)
+void QWaylandInputDevice::sendFullTouchEvent(QWaylandSurface *surface, QTouchEvent *event)
 {
     Q_D(QWaylandInputDevice);
-    if (!mouseFocus()) {
-        qWarning("Cannot send touch event, no pointer focus, fix the compositor");
-        return;
-    }
 
     if (!d->touch)
         return;
 
-    d->touch->sendFullTouchEvent(event);
+    d->touch->sendFullTouchEvent(surface, event);
 }
 
 /*!
diff --git a/src/compositor/compositor_api/qwaylandinput.h b/src/compositor/compositor_api/qwaylandinput.h
index d1cb922..991c91d 100644
--- a/src/compositor/compositor_api/qwaylandinput.h
+++ b/src/compositor/compositor_api/qwaylandinput.h
@@ -93,11 +93,11 @@ public:
     void sendFullKeyEvent(QKeyEvent *event);
     void sendFullKeyEvent(QWaylandSurface *surface, QKeyEvent *event);
 
-    void sendTouchPointEvent(int id, const QPointF &point, Qt::TouchPointState state);
-    void sendTouchFrameEvent();
-    void sendTouchCancelEvent();
+    void sendTouchPointEvent(QWaylandSurface *surface, int id, const QPointF &point, Qt::TouchPointState state);
+    void sendTouchFrameEvent(QWaylandClient *client);
+    void sendTouchCancelEvent(QWaylandClient *client);
 
-    void sendFullTouchEvent(QTouchEvent *event);
+    void sendFullTouchEvent(QWaylandSurface *surface, QTouchEvent *event);
 
     QWaylandPointer *pointer() const;
     //Normally set by the mouse device,
diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp
index 5e90c06..bfaaa27 100644
--- a/src/compositor/compositor_api/qwaylandquickitem.cpp
+++ b/src/compositor/compositor_api/qwaylandquickitem.cpp
@@ -639,18 +639,6 @@ void QWaylandQuickItem::touchEvent(QTouchEvent *event)
     if (d->shouldSendInputEvents() && d->touchEventsEnabled) {
         QWaylandInputDevice *inputDevice = compositor()->inputDeviceFor(event);
 
-        if (event->type() == QEvent::TouchBegin) {
-            QQuickItem *grabber = window()->mouseGrabberItem();
-            if (grabber != this)
-                grabMouse();
-        }
-
-        if (event->type() == QEvent::TouchEnd) {
-            QQuickItem *grabber = window()->mouseGrabberItem();
-            if (grabber == this)
-                ungrabMouse();
-        }
-
         QPoint pointPos;
         const QList<QTouchEvent::TouchPoint> &points = event->touchPoints();
         if (!points.isEmpty())
@@ -665,23 +653,12 @@ void QWaylandQuickItem::touchEvent(QTouchEvent *event)
         if (inputDevice->mouseFocus() != d->view.data()) {
             inputDevice->sendMouseMoveEvent(d->view.data(), pointPos, mapToScene(pointPos));
         }
-        inputDevice->sendFullTouchEvent(event);
+        inputDevice->sendFullTouchEvent(surface(), event);
     } else {
         event->ignore();
     }
 }
 
-/*!
- * \internal
- */
-void QWaylandQuickItem::mouseUngrabEvent()
-{
-    if (surface()) {
-        QTouchEvent e(QEvent::TouchCancel);
-        touchEvent(&e);
-    }
-}
-
 #ifndef QT_NO_IM
 /*!
  * \internal
diff --git a/src/compositor/compositor_api/qwaylandquickitem.h b/src/compositor/compositor_api/qwaylandquickitem.h
index 07b6a40..5ddd70f 100644
--- a/src/compositor/compositor_api/qwaylandquickitem.h
+++ b/src/compositor/compositor_api/qwaylandquickitem.h
@@ -120,7 +120,6 @@ protected:
     void keyReleaseEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
 
     void touchEvent(QTouchEvent *event) Q_DECL_OVERRIDE;
-    void mouseUngrabEvent() Q_DECL_OVERRIDE;
 
 #ifndef QT_NO_IM
     void inputMethodEvent(QInputMethodEvent *event) Q_DECL_OVERRIDE;
diff --git a/src/compositor/compositor_api/qwaylandtouch.cpp b/src/compositor/compositor_api/qwaylandtouch.cpp
index a9b4452..2c3b57f 100644
--- a/src/compositor/compositor_api/qwaylandtouch.cpp
+++ b/src/compositor/compositor_api/qwaylandtouch.cpp
@@ -49,48 +49,32 @@ QT_BEGIN_NAMESPACE
 QWaylandTouchPrivate::QWaylandTouchPrivate(QWaylandTouch *touch, QWaylandInputDevice *seat)
     : wl_touch()
     , seat(seat)
-    , focusResource()
 {
     Q_UNUSED(touch);
 }
 
-void QWaylandTouchPrivate::resetFocusState()
-{
-    focusDestroyListener.reset();
-    focusResource = 0;
-}
-
-void QWaylandTouchPrivate::touch_bind_resource(Resource *resource)
-{
-    focusResource = resource;
-}
-
-void QWaylandTouchPrivate::touch_destroy_resource(Resource *resource)
-{
-    if (focusResource == resource) {
-        resetFocusState();
-    }
-}
-
 void QWaylandTouchPrivate::touch_release(Resource *resource)
 {
     wl_resource_destroy(resource->handle);
 }
 
-void QWaylandTouchPrivate::sendDown(uint32_t time, int touch_id, const QPointF &position)
+void QWaylandTouchPrivate::sendDown(QWaylandSurface *surface, uint32_t time, int touch_id, const QPointF &position)
 {
     Q_Q(QWaylandTouch);
-    if (!focusResource || !q->mouseFocus())
+    auto focusResource = resourceMap().value(surface->client()->client());
+    if (!focusResource)
         return;
 
     uint32_t serial = q->compositor()->nextSerial();
 
-    wl_touch_send_down(focusResource->handle, serial, time, q->mouseFocus()->surfaceResource(), touch_id,
+    wl_touch_send_down(focusResource->handle, serial, time, surface->resource(), touch_id,
                        wl_fixed_from_double(position.x()), wl_fixed_from_double(position.y()));
 }
 
-void QWaylandTouchPrivate::sendUp(uint32_t time, int touch_id)
+void QWaylandTouchPrivate::sendUp(QWaylandClient *client, uint32_t time, int touch_id)
 {
+    auto focusResource = resourceMap().value(client->client());
+
     if (!focusResource)
         return;
 
@@ -98,8 +82,11 @@ void QWaylandTouchPrivate::sendUp(uint32_t time, int touch_id)
 
     wl_touch_send_up(focusResource->handle, serial, time, touch_id);
 }
-void QWaylandTouchPrivate::sendMotion(uint32_t time, int touch_id, const QPointF &position)
+
+void QWaylandTouchPrivate::sendMotion(QWaylandClient *client, uint32_t time, int touch_id, const QPointF &position)
 {
+    auto focusResource = resourceMap().value(client->client());
+
     if (!focusResource)
         return;
 
@@ -123,7 +110,6 @@ void QWaylandTouchPrivate::sendMotion(uint32_t time, int touch_id, const QPointF
 QWaylandTouch::QWaylandTouch(QWaylandInputDevice *inputDevice, QObject *parent)
     : QWaylandObject(*new QWaylandTouchPrivate(this, inputDevice), parent)
 {
-    connect(&d_func()->focusDestroyListener, &QWaylandDestroyListener::fired, this, &QWaylandTouch::focusDestroyed);
 }
 
 /*!
@@ -151,19 +137,19 @@ QWaylandCompositor *QWaylandTouch::compositor() const
  *
  * \sa mouseFocus()
  */
-void QWaylandTouch::sendTouchPointEvent(int id, const QPointF &position, Qt::TouchPointState state)
+void QWaylandTouch::sendTouchPointEvent(QWaylandSurface *surface, int id, const QPointF &position, Qt::TouchPointState state)
 {
     Q_D(QWaylandTouch);
     uint32_t time = compositor()->currentTimeMsecs();
     switch (state) {
     case Qt::TouchPointPressed:
-        d->sendDown(time, id, position);
+        d->sendDown(surface, time, id, position);
         break;
     case Qt::TouchPointMoved:
-        d->sendMotion(time, id, position);
+        d->sendMotion(surface->client(), time, id, position);
         break;
     case Qt::TouchPointReleased:
-        d->sendUp(time, id);
+        d->sendUp(surface->client(), time, id);
         break;
     case Qt::TouchPointStationary:
         // stationary points are not sent through wayland, the client must cache them
@@ -177,21 +163,23 @@ void QWaylandTouch::sendTouchPointEvent(int id, const QPointF &position, Qt::Tou
  * Sends a touch frame event for the touch device. This indicates the end of a
  * contact point list.
  */
-void QWaylandTouch::sendFrameEvent()
+void QWaylandTouch::sendFrameEvent(QWaylandClient *client)
 {
     Q_D(QWaylandTouch);
-    if (d->focusResource)
-        d->send_frame(d->focusResource->handle);
+    auto focusResource = d->resourceMap().value(client->client());
+    if (focusResource)
+        d->send_frame(focusResource->handle);
 }
 
 /*!
  * Sends a touch cancel event for the touch device.
  */
-void QWaylandTouch::sendCancelEvent()
+void QWaylandTouch::sendCancelEvent(QWaylandClient *client)
 {
     Q_D(QWaylandTouch);
-    if (d->focusResource)
-        d->send_cancel(d->focusResource->handle);
+    auto focusResource = d->resourceMap().value(client->client());
+    if (focusResource)
+        d->send_cancel(focusResource->handle);
 }
 
 /*!
@@ -200,16 +188,16 @@ void QWaylandTouch::sendCancelEvent()
  *
  * \sa sendTouchPointEvent(), sendFrameEvent()
  */
-void QWaylandTouch::sendFullTouchEvent(QTouchEvent *event)
+void QWaylandTouch::sendFullTouchEvent(QWaylandSurface *surface, QTouchEvent *event)
 {
     Q_D(QWaylandTouch);
     if (event->type() == QEvent::TouchCancel) {
-        sendCancelEvent();
+        sendCancelEvent(surface->client());
         return;
     }
 
     QtWayland::TouchExtensionGlobal *ext = QtWayland::TouchExtensionGlobal::findIn(d->compositor());
-    if (ext && ext->postTouchEvent(event, d->seat->mouseFocus()))
+    if (ext && ext->postTouchEvent(event, surface))
         return;
 
     const QList<QTouchEvent::TouchPoint> points = event->touchPoints();
@@ -220,9 +208,9 @@ void QWaylandTouch::sendFullTouchEvent(QTouchEvent *event)
     for (int i = 0; i < pointCount; ++i) {
         const QTouchEvent::TouchPoint &tp(points.at(i));
         // Convert the local pos in the compositor window to surface-relative.
-        sendTouchPointEvent(tp.id(), tp.pos(), tp.state());
+        sendTouchPointEvent(surface, tp.id(), tp.pos(), tp.state());
     }
-    sendFrameEvent();
+    sendFrameEvent(surface->client());
 }
 
 /*!
@@ -234,45 +222,4 @@ void QWaylandTouch::addClient(QWaylandClient *client, uint32_t id, uint32_t vers
     d->add(client->client(), id, qMin<uint32_t>(QtWaylandServer::wl_touch::interfaceVersion(), version));
 }
 
-/*!
- * Returns the Wayland resource for this QWaylandTouch.
- */
-struct wl_resource *QWaylandTouch::focusResource() const
-{
-    Q_D(const QWaylandTouch);
-    if (!d->focusResource)
-        return Q_NULLPTR;
-    return d->focusResource->handle;
-}
-
-/*!
- * Returns the view currently holding mouse focus in the input device.
- */
-QWaylandView *QWaylandTouch::mouseFocus() const
-{
-    Q_D(const QWaylandTouch);
-    return d->seat->mouseFocus();
-}
-
-/*!
- * \internal
- */
-void QWaylandTouch::focusDestroyed(void *data)
-{
-    Q_UNUSED(data)
-    Q_D(QWaylandTouch);
-    d->resetFocusState();
-}
-
-/*!
- * \internal
- */
-void QWaylandTouch::mouseFocusChanged(QWaylandView *newFocus, QWaylandView *oldFocus)
-{
-    Q_UNUSED(newFocus);
-    Q_UNUSED(oldFocus);
-    Q_D(QWaylandTouch);
-    d->resetFocusState();
-}
-
 QT_END_NAMESPACE
diff --git a/src/compositor/compositor_api/qwaylandtouch.h b/src/compositor/compositor_api/qwaylandtouch.h
index b6c40ea..f3f4125 100644
--- a/src/compositor/compositor_api/qwaylandtouch.h
+++ b/src/compositor/compositor_api/qwaylandtouch.h
@@ -51,6 +51,7 @@ class QWaylandTouchPrivate;
 class QWaylandInputDevice;
 class QWaylandView;
 class QWaylandClient;
+class QWaylandSurface;
 
 class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandTouch : public QWaylandObject
 {
@@ -62,17 +63,12 @@ public:
     QWaylandInputDevice *inputDevice() const;
     QWaylandCompositor *compositor() const;
 
-    virtual void sendTouchPointEvent(int id, const QPointF &position, Qt::TouchPointState state);
-    virtual void sendFrameEvent();
-    virtual void sendCancelEvent();
-
-    virtual void sendFullTouchEvent(QTouchEvent *event);
-
+    virtual void sendTouchPointEvent(QWaylandSurface *surface, int id, const QPointF &position, Qt::TouchPointState state);
+    virtual void sendFrameEvent(QWaylandClient *client);
+    virtual void sendCancelEvent(QWaylandClient *client);
+    virtual void sendFullTouchEvent(QWaylandSurface *surface, QTouchEvent *event);
     virtual void addClient(QWaylandClient *client, uint32_t id, uint32_t version);
 
-    wl_resource *focusResource() const;
-
-    QWaylandView *mouseFocus() const;
 private:
     void focusDestroyed(void *data);
     void mouseFocusChanged(QWaylandView *newFocus, QWaylandView *oldFocus);
diff --git a/src/compositor/compositor_api/qwaylandtouch_p.h b/src/compositor/compositor_api/qwaylandtouch_p.h
index ea334ee..1a86c5e 100644
--- a/src/compositor/compositor_api/qwaylandtouch_p.h
+++ b/src/compositor/compositor_api/qwaylandtouch_p.h
@@ -71,31 +71,14 @@ public:
 
     QWaylandCompositor *compositor() const { return seat->compositor(); }
 
-    void sendDown(uint32_t time, int touch_id, const QPointF &position);
-    void sendMotion(uint32_t time, int touch_id, const QPointF &position);
-    void sendUp(uint32_t time, int touch_id);
+    void sendDown(QWaylandSurface *surface, uint32_t time, int touch_id, const QPointF &position);
+    void sendMotion(QWaylandClient *client, uint32_t time, int touch_id, const QPointF &position);
+    void sendUp(QWaylandClient *client, uint32_t time, int touch_id);
 
-    void setFocusResource()
-    {
-        if (focusResource)
-            return;
-
-        QWaylandView *mouseFocus = seat->mouseFocus();
-        if (!mouseFocus || !mouseFocus->surface())
-            return;
-
-        focusResource = resourceMap().value(mouseFocus->surface()->waylandClient());
-    }
 private:
-    void resetFocusState();
-    void touch_bind_resource(Resource *resource) Q_DECL_OVERRIDE;
-    void touch_destroy_resource(Resource *resource) Q_DECL_OVERRIDE;
     void touch_release(Resource *resource) Q_DECL_OVERRIDE;
 
     QWaylandInputDevice *seat;
-
-    Resource *focusResource;
-    QWaylandDestroyListener focusDestroyListener;
 };
 
 QT_END_NAMESPACE
diff --git a/src/compositor/extensions/qwlqttouch.cpp b/src/compositor/extensions/qwlqttouch.cpp
index 17246b3..0d1120a 100644
--- a/src/compositor/extensions/qwlqttouch.cpp
+++ b/src/compositor/extensions/qwlqttouch.cpp
@@ -64,14 +64,14 @@ static inline int toFixed(qreal f)
     return int(f * 10000);
 }
 
-bool TouchExtensionGlobal::postTouchEvent(QTouchEvent *event, QWaylandView *view)
+bool TouchExtensionGlobal::postTouchEvent(QTouchEvent *event, QWaylandSurface *surface)
 {
     const QList<QTouchEvent::TouchPoint> points = event->touchPoints();
     const int pointCount = points.count();
     if (!pointCount)
         return false;
 
-    wl_client *surfaceClient = view->surface()->client()->client();
+    wl_client *surfaceClient = surface->client()->client();
     uint32_t time = m_compositor->currentTimeMsecs();
     const int rescount = m_resources.count();
 
diff --git a/src/compositor/extensions/qwlqttouch_p.h b/src/compositor/extensions/qwlqttouch_p.h
index 32d7658..de720b7 100644
--- a/src/compositor/extensions/qwlqttouch_p.h
+++ b/src/compositor/extensions/qwlqttouch_p.h
@@ -76,7 +76,7 @@ public:
     TouchExtensionGlobal(QWaylandCompositor *compositor);
     ~TouchExtensionGlobal();
 
-    bool postTouchEvent(QTouchEvent *event, QWaylandView *view);
+    bool postTouchEvent(QTouchEvent *event, QWaylandSurface *surface);
 
     void setBehviorFlags(BehaviorFlags flags);
     BehaviorFlags behaviorFlags() const { return m_flags; }
-- 
2.10.0

