From 083ece74feedb93c0ed208017658c85a4bb1e532 Mon Sep 17 00:00:00 2001
From: Laszlo Agocs <laszlo.p.agocs@nokia.com>
Date: Tue, 7 Feb 2012 11:39:49 +0200
Subject: [PATCH] Add mouse event synthesizing to the touch extension protocol.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The compositor can now be configured to tell the clients to generate
mouse events when receiving touch events. In touchscreen-only systems
this can be used to reduce the number of Wayland events.

Change-Id: I231a15cd4ed463ee81c510c082a270efa255a1f3
Reviewed-by: Jørgen Lind <jorgen.lind@nokia.com>
---
 examples/qwindow-compositor/qopenglwindow.cpp |  8 +++++
 examples/qwindow-compositor/qopenglwindow.h   |  3 +-
 .../qwindow-compositor/qwindowcompositor.cpp  |  5 +++
 extensions/touch-extension.xml                |  8 +++++
 .../compositor_api/waylandcompositor.cpp      |  5 +++
 .../compositor_api/waylandcompositor.h        |  7 ++++
 .../wayland_wrapper/wlcompositor.cpp          |  6 ++++
 src/compositor/wayland_wrapper/wlcompositor.h |  1 +
 src/compositor/wayland_wrapper/wltouch.cpp    |  4 ++-
 src/compositor/wayland_wrapper/wltouch.h      |  6 ++--
 .../platforms/wayland/qwaylandtouch.cpp       | 36 +++++++++++++++----
 src/plugins/platforms/wayland/qwaylandtouch.h |  6 ++++
 12 files changed, 85 insertions(+), 10 deletions(-)

diff --git a/examples/qwindow-compositor/qopenglwindow.cpp b/examples/qwindow-compositor/qopenglwindow.cpp
index d70c4a96..473e4c15 100644
--- a/examples/qwindow-compositor/qopenglwindow.cpp
+++ b/examples/qwindow-compositor/qopenglwindow.cpp
@@ -39,6 +39,7 @@
 ****************************************************************************/
 
 #include "qopenglwindow.h"
+#include <QTouchEvent>
 
 QOpenGLWindow::QOpenGLWindow(const QSurfaceFormat &format, const QRect &geometry)
     : m_format(format)
@@ -51,3 +52,10 @@ QOpenGLWindow::QOpenGLWindow(const QSurfaceFormat &format, const QRect &geometry
     m_context->setFormat(format);
     m_context->create();
 }
+
+void QOpenGLWindow::touchEvent(QTouchEvent *event)
+{
+    // Do not want any automatically synthesized mouse events
+    // so make sure the touch is always accepted.
+    event->accept();
+}
diff --git a/examples/qwindow-compositor/qopenglwindow.h b/examples/qwindow-compositor/qopenglwindow.h
index 0d1cfbf7..2ba883cf 100644
--- a/examples/qwindow-compositor/qopenglwindow.h
+++ b/examples/qwindow-compositor/qopenglwindow.h
@@ -53,7 +53,8 @@ public:
     QOpenGLContext* context() { return m_context; }
     bool makeCurrent() { return m_context->makeCurrent(this); }
     void swapBuffers() { m_context->swapBuffers(this); }
-
+protected:
+    void touchEvent(QTouchEvent *event);
 private:
     QOpenGLContext *m_context;
     QSurfaceFormat m_format;
diff --git a/examples/qwindow-compositor/qwindowcompositor.cpp b/examples/qwindow-compositor/qwindowcompositor.cpp
index 90192f98..a7132101 100644
--- a/examples/qwindow-compositor/qwindowcompositor.cpp
+++ b/examples/qwindow-compositor/qwindowcompositor.cpp
@@ -58,6 +58,11 @@ QWindowCompositor::QWindowCompositor(QOpenGLWindow *window)
     // using a custom protocol extension.
     // enableTouchExtension();
 
+    // Enable the following to have mouse events generated from touch
+    // on client side. This is not the same as synthesizing mouse events
+    // in the compositor because it avoids sending data through the wire.
+    // configureTouchExtension(WaylandCompositor::TouchExtMouseFromTouch);
+
     enableSubSurfaceExtension();
     m_window->makeCurrent();
 
diff --git a/extensions/touch-extension.xml b/extensions/touch-extension.xml
index 268d70a6..d1baa7ba 100644
--- a/extensions/touch-extension.xml
+++ b/extensions/touch-extension.xml
@@ -56,6 +56,14 @@
         <arg name="rawdata" type="array" />
       </event>
 
+      <enum name="flags">
+        <entry name="mouse_from_touch" value="0x1" />
+      </enum>
+
+      <event name="configure">
+        <arg name="flags" type="uint" />
+      </event>
+
       <request name="dummy">
       </request>
 
diff --git a/src/compositor/compositor_api/waylandcompositor.cpp b/src/compositor/compositor_api/waylandcompositor.cpp
index eee6805f..c86698bd 100644
--- a/src/compositor/compositor_api/waylandcompositor.cpp
+++ b/src/compositor/compositor_api/waylandcompositor.cpp
@@ -193,3 +193,8 @@ void WaylandCompositor::enableTouchExtension()
 {
     m_compositor->enableTouchExtension();
 }
+
+void WaylandCompositor::configureTouchExtension(TouchExtensionFlags flags)
+{
+    m_compositor->configureTouchExtension(flags);
+}
diff --git a/src/compositor/compositor_api/waylandcompositor.h b/src/compositor/compositor_api/waylandcompositor.h
index f4d64992..3cc255a8 100644
--- a/src/compositor/compositor_api/waylandcompositor.h
+++ b/src/compositor/compositor_api/waylandcompositor.h
@@ -95,6 +95,11 @@ public:
     void enableSubSurfaceExtension();
 
     void enableTouchExtension();
+    enum TouchExtensionFlag {
+        TouchExtMouseFromTouch = 0x01
+    };
+    Q_DECLARE_FLAGS(TouchExtensionFlags, TouchExtensionFlag)
+    void configureTouchExtension(TouchExtensionFlags flags);
 
 private:
     static void retainedSelectionChanged(QMimeData *mimeData, void *param);
@@ -104,4 +109,6 @@ private:
     QByteArray m_socket_name;
 };
 
+Q_DECLARE_OPERATORS_FOR_FLAGS(WaylandCompositor::TouchExtensionFlags)
+
 #endif // QTCOMP_H
diff --git a/src/compositor/wayland_wrapper/wlcompositor.cpp b/src/compositor/wayland_wrapper/wlcompositor.cpp
index b28f8f8b..75e5eb95 100644
--- a/src/compositor/wayland_wrapper/wlcompositor.cpp
+++ b/src/compositor/wayland_wrapper/wlcompositor.cpp
@@ -384,6 +384,12 @@ void Compositor::enableTouchExtension()
     }
 }
 
+void Compositor::configureTouchExtension(int flags)
+{
+    if (m_touchExtension)
+        m_touchExtension->setFlags(flags);
+}
+
 void Compositor::setRetainedSelectionWatcher(RetainedSelectionFunc func, void *param)
 {
     m_retainNotify = func;
diff --git a/src/compositor/wayland_wrapper/wlcompositor.h b/src/compositor/wayland_wrapper/wlcompositor.h
index fd0e3061..3d4185f6 100644
--- a/src/compositor/wayland_wrapper/wlcompositor.h
+++ b/src/compositor/wayland_wrapper/wlcompositor.h
@@ -119,6 +119,7 @@ public:
 
     void enableTouchExtension();
     TouchExtensionGlobal *touchExtension() { return m_touchExtension; }
+    void configureTouchExtension(int flags);
 
     bool isDragging() const;
     void sendDragMoveEvent(const QPoint &global, const QPoint &local, Surface *surface);
diff --git a/src/compositor/wayland_wrapper/wltouch.cpp b/src/compositor/wayland_wrapper/wltouch.cpp
index f94c5af0..06499b1a 100644
--- a/src/compositor/wayland_wrapper/wltouch.cpp
+++ b/src/compositor/wayland_wrapper/wltouch.cpp
@@ -56,7 +56,8 @@ const struct wl_touch_extension_interface TouchExtensionGlobal::touch_interface
 static const int maxRawPos = 24;
 
 TouchExtensionGlobal::TouchExtensionGlobal(Compositor *compositor)
-    : m_compositor(compositor)
+    : m_compositor(compositor),
+      m_flags(0)
 {
     wl_array_init(&m_rawdata_array);
     m_rawdata_ptr = static_cast<float *>(wl_array_add(&m_rawdata_array, maxRawPos * sizeof(float) * 2));
@@ -86,6 +87,7 @@ void TouchExtensionGlobal::bind_func(wl_client *client, void *data, uint32_t ver
     resource->destroy = destroy_resource;
     TouchExtensionGlobal *self = static_cast<TouchExtensionGlobal *>(resource->data);
     self->m_resources.append(resource);
+    wl_resource_post_event(resource, WL_TOUCH_EXTENSION_CONFIGURE, self->m_flags);
 }
 
 static inline int toFixed(qreal f)
diff --git a/src/compositor/wayland_wrapper/wltouch.h b/src/compositor/wayland_wrapper/wltouch.h
index e1a662c5..0d63defb 100644
--- a/src/compositor/wayland_wrapper/wltouch.h
+++ b/src/compositor/wayland_wrapper/wltouch.h
@@ -59,9 +59,9 @@ public:
 
     void postTouchEvent(QTouchEvent *event, Surface *surface);
 
-private:
-    Compositor *m_compositor;
+    void setFlags(int flags) { m_flags = flags; }
 
+private:
     static void bind_func(struct wl_client *client, void *data,
                           uint32_t version, uint32_t id);
 
@@ -69,6 +69,8 @@ private:
 
     static const struct wl_touch_extension_interface touch_interface;
 
+    Compositor *m_compositor;
+    int m_flags;
     QList<wl_resource *> m_resources;
     wl_array m_rawdata_array;
     float *m_rawdata_ptr;
diff --git a/src/plugins/platforms/wayland/qwaylandtouch.cpp b/src/plugins/platforms/wayland/qwaylandtouch.cpp
index 35310363..82e1519b 100644
--- a/src/plugins/platforms/wayland/qwaylandtouch.cpp
+++ b/src/plugins/platforms/wayland/qwaylandtouch.cpp
@@ -48,6 +48,8 @@ QWaylandTouchExtension::QWaylandTouchExtension(QWaylandDisplay *display, uint32_
 {
     mDisplay = display;
     mPointsLeft = 0;
+    mFlags = 0;
+
     mTouch = static_cast<struct wl_touch_extension *>(wl_display_bind(display->wl_display(), id, &wl_touch_extension_interface));
     wl_touch_extension_add_listener(mTouch, &touch_listener, this);
 
@@ -92,6 +94,7 @@ void QWaylandTouchExtension::handle_touch(void *data, wl_touch_extension *ext, u
         qWarning("wl_touch_extension: handle_touch: No pointer focus");
         return;
     }
+    self->mTargetWindow = win->window();
 
     QWindowSystemInterface::TouchPoint tp;
     tp.id = id;
@@ -107,7 +110,7 @@ void QWaylandTouchExtension::handle_touch(void *data, wl_touch_extension *ext, u
     // Got surface-relative coords but need a (virtual) screen position.
     QPointF relPos = QPointF(fromFixed(x), fromFixed(y));
     QPointF delta = relPos - relPos.toPoint();
-    tp.area.moveCenter(win->window()->mapToGlobal(relPos.toPoint()) + delta);
+    tp.area.moveCenter(self->mTargetWindow->mapToGlobal(relPos.toPoint()) + delta);
 
     tp.normalPosition.setX(fromFixed(normalized_x));
     tp.normalPosition.setY(fromFixed(normalized_y));
@@ -159,20 +162,41 @@ void QWaylandTouchExtension::sendTouchEvent()
 
     QWindowSystemInterface::handleTouchEvent(0, mTimestamp, mTouchDevice, mTouchPoints);
 
-    bool allReleased = true;
+    Qt::TouchPointStates states = 0;
     for (int i = 0; i < mTouchPoints.count(); ++i)
-        if (mTouchPoints.at(i).state != Qt::TouchPointReleased) {
-            allReleased = false;
-            break;
+        states |= mTouchPoints.at(i).state;
+
+    if (mFlags & WL_TOUCH_EXTENSION_FLAGS_MOUSE_FROM_TOUCH) {
+        if (states == Qt::TouchPointPressed)
+            mMouseSourceId = mTouchPoints.first().id;
+        for (int i = 0; i < mTouchPoints.count(); ++i) {
+            const QWindowSystemInterface::TouchPoint &tp(mTouchPoints.at(i));
+            if (tp.id == mMouseSourceId) {
+                Qt::MouseButtons buttons = tp.state == Qt::TouchPointReleased ? Qt::NoButton : Qt::LeftButton;
+                QPointF globalPos = tp.area.center();
+                QPointF delta = globalPos - globalPos.toPoint();
+                QPointF localPos = mTargetWindow->mapFromGlobal(globalPos.toPoint()) + delta;
+                QWindowSystemInterface::handleMouseEvent(0, mTimestamp, localPos, globalPos, buttons);
+                break;
+            }
         }
+    }
 
     mPrevTouchPoints = mTouchPoints;
     mTouchPoints.clear();
 
-    if (allReleased)
+    if (states == Qt::TouchPointReleased)
         mPrevTouchPoints.clear();
 }
 
+void QWaylandTouchExtension::handle_configure(void *data, wl_touch_extension *ext, uint32_t flags)
+{
+    Q_UNUSED(ext);
+    QWaylandTouchExtension *self = static_cast<QWaylandTouchExtension *>(data);
+    self->mFlags = flags;
+}
+
 const struct wl_touch_extension_listener QWaylandTouchExtension::touch_listener = {
     QWaylandTouchExtension::handle_touch,
+    QWaylandTouchExtension::handle_configure
 };
diff --git a/src/plugins/platforms/wayland/qwaylandtouch.h b/src/plugins/platforms/wayland/qwaylandtouch.h
index ab24c26f..b29d136c 100644
--- a/src/plugins/platforms/wayland/qwaylandtouch.h
+++ b/src/plugins/platforms/wayland/qwaylandtouch.h
@@ -73,6 +73,9 @@ private:
                              int32_t velocity_y,
                              uint32_t flags,
                              struct wl_array *rawdata);
+    static void handle_configure(void *data,
+                                 struct wl_touch_extension *ext,
+                                 uint32_t flags);
 
     void sendTouchEvent();
 
@@ -81,6 +84,9 @@ private:
     QTouchDevice *mTouchDevice;
     uint32_t mTimestamp;
     int mPointsLeft;
+    uint32_t mFlags;
+    int mMouseSourceId;
+    QWindow *mTargetWindow;
 };
 
 #endif // QWAYLANDTOUCH_H
-- 
GitLab