diff --git a/examples/qwindow-compositor/qopenglwindow.cpp b/examples/qwindow-compositor/qopenglwindow.cpp
index d70c4a9656bc71665c22d733003db501798e075c..473e4c151961f348847bc33247387adb8a5757fe 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 0d1cfbf7a3c6f77b78338f1b087d1d3d35288d7b..2ba883cf03c23860c58c145b7f62b13420223e89 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 90192f98d3148ff8f4171f2387c57423ae468a9b..a7132101e428ee6c40a3cfa9a4a6f38da8679290 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 268d70a68cca9d67cbaed321633f5ad45a02b5a6..d1baa7ba90f40ae2190d7e0b4873a1a1c70dbfbf 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 eee6805f07169677fb065225c9613c5a8c78cc65..c86698bd4e14b4451f5f37c066d25653b4ef5440 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 f4d64992c0dab6730effb248f12d31d4363f62c8..3cc255a8bd9760d5a012b6377038e53682c8ffc8 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 b28f8f8bde0e965a97c57c0244d0f8ccde1c97dc..75e5eb954efa2ceb8eaca46daa41c4d9f82c6546 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 fd0e306177eaa43a1aeca210a804a2171b3c45f8..3d4185f698884789eaada82ad9f683aa981d626f 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 f94c5af0ecd3b691e681ad763af0b04f91544f90..06499b1ae2a4c3388a2612804cd4a2b07d43b60b 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 e1a662c59490872e5f704eade1d0ecae02f3d048..0d63defb7e14681362536737489b91a259ee0c21 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 353103630605961c8528a818762433792e3b535c..82e1519b3ba5acc9ca60e4f5f71dd13de3883fbb 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 ab24c26fdd8dcdc3926179e7d3c6c35b6521c931..b29d136cb1da6ab60b3224b11ccdc3226e07afca 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