diff --git a/configure b/configure
index de0a1bd5d7d16b5b8807a01d38a464bb1b09a324..7251e8e673bb776eb900a13d322438a8130e1784 100755
--- a/configure
+++ b/configure
@@ -5358,6 +5358,14 @@ fi
 # auto-detect XInput2 support
 if [ "$CFG_XINPUT2" != "no" ]; then
     if compileTest x11/xinput2 "XInput2"; then
+        if [ -n "$PKG_CONFIG" ] && $PKG_CONFIG --exists xi 2>/dev/null; then
+            QMAKE_LIBXI_VERSION_MAJOR=`$PKG_CONFIG --modversion xi 2>/dev/null | cut -d . -f 1`
+            QMAKE_LIBXI_VERSION_MINOR=`$PKG_CONFIG --modversion xi 2>/dev/null | cut -d . -f 2`
+            QMAKE_LIBXI_VERSION_PATCH=`$PKG_CONFIG --modversion xi 2>/dev/null | cut -d . -f 3`
+            QMakeVar set QMAKE_LIBXI_VERSION_MAJOR "$QMAKE_LIBXI_VERSION_MAJOR"
+            QMakeVar set QMAKE_LIBXI_VERSION_MINOR "$QMAKE_LIBXI_VERSION_MINOR"
+            QMakeVar set QMAKE_LIBXI_VERSION_PATCH "$QMAKE_LIBXI_VERSION_PATCH"
+        fi
         CFG_XINPUT2=yes
         CFG_XINPUT=no
     else
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 92d064df9b35e3cd283337396bb9f02798cd9c29..80c844e6587fc018821a6cf9db70598a7d18d36e 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -70,7 +70,6 @@
 #endif
 
 #if defined(XCB_USE_XINPUT2)
-#include <X11/extensions/XInput2.h>
 #include <X11/extensions/XI2proto.h>
 #endif
 
@@ -457,6 +456,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
     , m_focusWindow(0)
     , m_systemTrayTracker(0)
     , m_glIntegration(Q_NULLPTR)
+    , m_xiGrab(false)
 {
 #ifdef XCB_USE_XLIB
     Display *dpy = XOpenDisplay(m_displayName.constData());
@@ -909,7 +909,7 @@ static Qt::MouseButtons translateMouseButtons(int s)
     return ret;
 }
 
-static Qt::MouseButton translateMouseButton(xcb_button_t s)
+Qt::MouseButton QXcbConnection::translateMouseButton(xcb_button_t s)
 {
     switch (s) {
     case 1: return Qt::LeftButton;
@@ -944,39 +944,6 @@ static Qt::MouseButton translateMouseButton(xcb_button_t s)
     }
 }
 
-void QXcbConnection::handleButtonPress(xcb_generic_event_t *ev)
-{
-    xcb_button_press_event_t *event = (xcb_button_press_event_t *)ev;
-
-    // the event explicitly contains the state of the three first buttons,
-    // the rest we need to manage ourselves
-    m_buttons = (m_buttons & ~0x7) | translateMouseButtons(event->state);
-    m_buttons |= translateMouseButton(event->detail);
-    qCDebug(lcQpaXInput, "xcb: pressed mouse button %d, button state %X", event->detail, static_cast<unsigned int>(m_buttons));
-}
-
-void QXcbConnection::handleButtonRelease(xcb_generic_event_t *ev)
-{
-    xcb_button_release_event_t *event = (xcb_button_release_event_t *)ev;
-
-    // the event explicitly contains the state of the three first buttons,
-    // the rest we need to manage ourselves
-    m_buttons = (m_buttons & ~0x7) | translateMouseButtons(event->state);
-    m_buttons &= ~translateMouseButton(event->detail);
-    qCDebug(lcQpaXInput, "xcb: released mouse button %d, button state %X", event->detail, static_cast<unsigned int>(m_buttons));
-}
-
-void QXcbConnection::handleMotionNotify(xcb_generic_event_t *ev)
-{
-    xcb_motion_notify_event_t *event = (xcb_motion_notify_event_t *)ev;
-
-    m_buttons = (m_buttons & ~0x7) | translateMouseButtons(event->state);
-#ifdef Q_XCB_DEBUG
-    qCDebug(lcQpaXInput, "xcb: moved mouse to %4d, %4d; button state %X",
-           event->event_x, event->event_y, static_cast<unsigned int>(m_buttons));
-#endif
-}
-
 #ifndef QT_NO_XKB
 namespace {
     typedef union {
@@ -1018,18 +985,35 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
         switch (response_type) {
         case XCB_EXPOSE:
             HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent);
-        case XCB_BUTTON_PRESS:
-            m_keyboard->updateXKBStateFromCore(((xcb_button_press_event_t *)event)->state);
-            handleButtonPress(event);
+
+        // press/release/motion is only delivered here when XI 2.2+ is _not_ in use
+        case XCB_BUTTON_PRESS: {
+            xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event;
+            m_keyboard->updateXKBStateFromCore(ev->state);
+            // the event explicitly contains the state of the three first buttons,
+            // the rest we need to manage ourselves
+            m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
+            m_buttons |= translateMouseButton(ev->detail);
+            qCDebug(lcQpaXInput, "legacy mouse press, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttons));
             HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent);
-        case XCB_BUTTON_RELEASE:
-            m_keyboard->updateXKBStateFromCore(((xcb_button_release_event_t *)event)->state);
-            handleButtonRelease(event);
+        }
+        case XCB_BUTTON_RELEASE: {
+            xcb_button_release_event_t *ev = (xcb_button_release_event_t *)event;
+            m_keyboard->updateXKBStateFromCore(ev->state);
+            m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
+            m_buttons &= ~translateMouseButton(ev->detail);
+            qCDebug(lcQpaXInput, "legacy mouse release, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttons));
             HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent);
-        case XCB_MOTION_NOTIFY:
-            m_keyboard->updateXKBStateFromCore(((xcb_motion_notify_event_t *)event)->state);
-            handleMotionNotify(event);
+        }
+        case XCB_MOTION_NOTIFY: {
+            xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event;
+            m_keyboard->updateXKBStateFromCore(ev->state);
+            m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
+            qCDebug(lcQpaXInput, "legacy mouse move %d,%d button %d state %X", ev->event_x, ev->event_y,
+                    ev->detail, static_cast<unsigned int>(m_buttons));
             HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent);
+        }
+
         case XCB_CONFIGURE_NOTIFY:
             HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent);
         case XCB_MAP_NOTIFY:
@@ -1090,6 +1074,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
             break;
 #if defined(XCB_USE_XINPUT2)
         case XCB_GE_GENERIC:
+            // Here the windowEventListener is invoked from xi2HandleEvent()
             if (m_xi2Enabled)
                 xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event));
             break;
@@ -1931,6 +1916,12 @@ void QXcbConnection::initializeXKB()
 #endif
 }
 
+bool QXcbConnection::xi2MouseEvents() const
+{
+    static bool mouseViaXI2 = !qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE");
+    return mouseViaXI2;
+}
+
 #if defined(XCB_USE_XINPUT2)
 static int xi2ValuatorOffset(unsigned char *maskPtr, int maskLen, int number)
 {
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index e4274eca4d1475c544e5570c35278872c213ab8a..2005ae0701700d93a6a22f61fae834d79b1a3cf1 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -69,7 +69,8 @@
 #endif
 #endif
 struct XInput2TouchDeviceData;
-#endif
+#endif // XCB_USE_XINPUT2
+
 struct xcb_randr_get_output_info_reply_t;
 
 //#define Q_XCB_DEBUG
@@ -347,6 +348,7 @@ public:
     virtual void handleFocusInEvent(const xcb_focus_in_event_t *) {}
     virtual void handleFocusOutEvent(const xcb_focus_out_event_t *) {}
     virtual void handlePropertyNotifyEvent(const xcb_property_notify_event_t *) {}
+    virtual void handleXIMouseEvent(xcb_ge_event_t *) {}
 
     virtual QXcbWindow *toWindow() { return 0; }
 };
@@ -413,14 +415,14 @@ public:
     void xi2Select(xcb_window_t window);
 #endif
 #ifdef XCB_USE_XINPUT21
-    bool isUsingXInput21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
+    bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
 #else
-    bool isUsingXInput21() const { return false; }
+    bool isAtLeastXI21() const { return false; }
 #endif
 #ifdef XCB_USE_XINPUT22
-    bool isUsingXInput22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
+    bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
 #else
-    bool isUsingXInput22() const { return false; }
+    bool isAtLeastXI22() const { return false; }
 #endif
 
     void sync();
@@ -457,7 +459,9 @@ public:
 
     xcb_timestamp_t getTimestamp();
 
+    void setButton(Qt::MouseButton button, bool down) { if (down) m_buttons |= button; else m_buttons &= ~button; }
     Qt::MouseButtons buttons() const { return m_buttons; }
+    Qt::MouseButton translateMouseButton(xcb_button_t s);
 
     QXcbWindow *focusWindow() const { return m_focusWindow; }
     void setFocusWindow(QXcbWindow *);
@@ -477,11 +481,19 @@ public:
     void handleEnterEvent(const xcb_enter_notify_event_t *);
 #endif
 
+#ifdef XCB_USE_XINPUT22
+    bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab);
+#endif
+    Qt::MouseButton xiToQtMouseButton(uint32_t b);
+
     QXcbEventReader *eventReader() const { return m_reader; }
 
     bool canGrab() const { return m_canGrabServer; }
 
     QXcbGlIntegration *glIntegration() const { return m_glIntegration; }
+
+    bool xi2MouseEvents() const;
+
 protected:
     bool event(QEvent *e) Q_DECL_OVERRIDE;
 
@@ -509,9 +521,6 @@ private:
     bool checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output);
     void initializeScreens();
     void updateScreens(const xcb_randr_notify_event_t *event);
-    void handleButtonPress(xcb_generic_event_t *event);
-    void handleButtonRelease(xcb_generic_event_t *event);
-    void handleMotionNotify(xcb_generic_event_t *event);
 
     bool m_xi2Enabled;
     int m_xi2Minor;
@@ -524,6 +533,9 @@ private:
     void xi2HandleHierachyEvent(void *event);
     void xi2HandleDeviceChangedEvent(void *event);
     int m_xiOpCode, m_xiEventBase, m_xiErrorBase;
+#ifdef XCB_USE_XINPUT22
+    void xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow);
+#endif // XCB_USE_XINPUT22
 #ifndef QT_NO_TABLETEVENT
     struct TabletData {
         TabletData() : deviceId(0), pointerType(QTabletEvent::UnknownPointer),
@@ -543,10 +555,10 @@ private:
         };
         QHash<int, ValuatorClassInfo> valuatorInfo;
     };
-    bool xi2HandleTabletEvent(void *event, TabletData *tabletData);
+    bool xi2HandleTabletEvent(void *event, TabletData *tabletData, QXcbWindowEventListener *eventListener);
     void xi2ReportTabletEvent(TabletData &tabletData, void *event);
     QVector<TabletData> m_tabletData;
-#endif
+#endif // !QT_NO_TABLETEVENT
     struct ScrollingDevice {
         ScrollingDevice() : deviceId(0), verticalIndex(0), horizontalIndex(0), orientations(0), legacyOrientations(0) { }
         int deviceId;
@@ -559,9 +571,7 @@ private:
     void updateScrollingDevice(ScrollingDevice& scrollingDevice, int num_classes, void *classes);
     void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice);
     QHash<int, ScrollingDevice> m_scrollingDevices;
-#endif // XCB_USE_XINPUT2
 
-#if defined(XCB_USE_XINPUT2)
     static bool xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value);
     static bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode);
 #endif
@@ -633,6 +643,7 @@ private:
     QByteArray m_startupId;
     QXcbSystemTrayTracker *m_systemTrayTracker;
     QXcbGlIntegration *m_glIntegration;
+    bool m_xiGrab;
 
     friend class QXcbEventReader;
 };
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index 1848fc14f04958a208f32e5a41c3d4be45e13c64..0e8a162a7d4a7ac5ddd56e3cbd6bf063536e61ce 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -275,34 +275,37 @@ void QXcbConnection::xi2Select(xcb_window_t window)
     unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
 
 #ifdef XCB_USE_XINPUT22
-    if (isUsingXInput22()) {
+    if (isAtLeastXI22()) {
         bitMask |= XI_TouchBeginMask;
         bitMask |= XI_TouchUpdateMask;
         bitMask |= XI_TouchEndMask;
+        bitMask |= XI_PropertyEventMask; // for tablets
+        if (xi2MouseEvents()) {
+            // We want both mouse and touch through XI2 if touch is supported (>= 2.2).
+            // The plain xcb press and motion events will not be delivered after this.
+            bitMask |= XI_ButtonPressMask;
+            bitMask |= XI_ButtonReleaseMask;
+            bitMask |= XI_MotionMask;
+            qCDebug(lcQpaXInput, "XInput 2.2: Selecting press/release/motion events in addition to touch");
+        }
         XIEventMask mask;
         mask.mask_len = sizeof(bitMask);
         mask.mask = xiBitMask;
-        if (!m_touchDevices.isEmpty()) {
-            // If we select for touch events on the master pointer, XInput2
-            // will not synthesize mouse events. This means Qt must do it,
-            // which is also preferable, since Qt can control better when
-            // to do so.
-            mask.deviceid = XIAllMasterDevices;
-            Status result = XISelectEvents(xDisplay, window, &mask, 1);
-            if (result != Success)
-                qCDebug(lcQpaXInput, "XInput 2.2: failed to select touch events, window %x, result %d", window, result);
-        }
+        // When xi2MouseEvents() is true (the default), pointer emulation for touch and tablet
+        // events will get disabled. This is preferable for touch, as Qt Quick handles touch events
+        // directly while for others QtGui synthesizes mouse events, not so much for tablets. For
+        // the latter we will synthesize the events ourselves.
+        mask.deviceid = XIAllMasterDevices;
+        Status result = XISelectEvents(xDisplay, window, &mask, 1);
+        if (result != Success)
+            qCDebug(lcQpaXInput, "XInput 2.2: failed to select pointer/touch events, window %x, result %d", window, result);
     }
 #endif // XCB_USE_XINPUT22
 
+    const bool pointerSelected = isAtLeastXI22() && xi2MouseEvents();
     QSet<int> tabletDevices;
 #ifndef QT_NO_TABLETEVENT
-    // For each tablet, select some additional event types.
-    // Press, motion, etc. events must never be selected for _all_ devices
-    // as that would render the standard XCB_MOTION_NOTIFY and
-    // similar handlers useless and we have no intention to infect
-    // all the pure xcb code with Xlib-based XI2.
-    if (!m_tabletData.isEmpty()) {
+    if (!m_tabletData.isEmpty() && !pointerSelected) {
         unsigned int tabletBitMask;
         unsigned char *xiTabletBitMask = reinterpret_cast<unsigned char *>(&tabletBitMask);
         QVector<XIEventMask> xiEventMask(m_tabletData.count());
@@ -323,7 +326,8 @@ void QXcbConnection::xi2Select(xcb_window_t window)
 
 #ifdef XCB_USE_XINPUT21
     // Enable each scroll device
-    if (!m_scrollingDevices.isEmpty()) {
+    if (!m_scrollingDevices.isEmpty() && !pointerSelected) {
+        // Only when XI2 mouse events are not enabled, otherwise motion and release are selected already.
         QVector<XIEventMask> xiEventMask(m_scrollingDevices.size());
         unsigned int scrollBitMask;
         unsigned char *xiScrollBitMask = reinterpret_cast<unsigned char *>(&scrollBitMask);
@@ -425,7 +429,7 @@ XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
                     dev->size.width() > 10000 || dev->size.height() > 10000)
                 dev->size = QSizeF(130, 110);
         }
-        if (!isUsingXInput22() || type == QTouchDevice::TouchPad)
+        if (!isAtLeastXI22() || type == QTouchDevice::TouchPad)
             caps |= QTouchDevice::MouseEmulation;
 
         if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
@@ -447,7 +451,7 @@ XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
 }
 
 #if defined(XCB_USE_XINPUT21) || !defined(QT_NO_TABLETEVENT)
-static qreal fixed1616ToReal(FP1616 val)
+static inline qreal fixed1616ToReal(FP1616 val)
 {
     return qreal(val) / 0x10000;
 }
@@ -468,155 +472,280 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
 {
     if (xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) {
         xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
+        int sourceDeviceId = xiEvent->deviceid; // may be the master id
+        xXIDeviceEvent *xiDeviceEvent = 0;
+        QXcbWindowEventListener *eventListener = 0;
 
-        if (xiEvent->evtype == XI_HierarchyChanged) {
+        switch (xiEvent->evtype) {
+        case XI_ButtonPress:
+        case XI_ButtonRelease:
+        case XI_Motion:
+        case XI_TouchBegin:
+        case XI_TouchUpdate:
+        case XI_TouchEnd:
+        {
+            xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
+            eventListener = windowEventListenerFromId(xiDeviceEvent->event);
+            if (eventListener) {
+                long result = 0;
+                if (eventListener->handleGenericEvent(reinterpret_cast<xcb_generic_event_t *>(event), &result))
+                    return;
+            }
+            sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master
+            break;
+        }
+        case XI_HierarchyChanged:
             xi2HandleHierachyEvent(xiEvent);
             return;
-        }
-        if (xiEvent->evtype == XI_DeviceChanged) {
+        case XI_DeviceChanged:
             xi2HandleDeviceChangedEvent(xiEvent);
             return;
+        default:
+            break;
         }
 
 #ifndef QT_NO_TABLETEVENT
         for (int i = 0; i < m_tabletData.count(); ++i) {
-            if (m_tabletData.at(i).deviceId == xiEvent->deviceid) {
-                if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i]))
+            if (m_tabletData.at(i).deviceId == sourceDeviceId) {
+                if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i], eventListener))
                     return;
             }
         }
 #endif // QT_NO_TABLETEVENT
 
 #ifdef XCB_USE_XINPUT21
-        QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(xiEvent->deviceid);
+        QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(sourceDeviceId);
         if (device != m_scrollingDevices.end())
             xi2HandleScrollEvent(xiEvent, device.value());
 #endif // XCB_USE_XINPUT21
 
 #ifdef XCB_USE_XINPUT22
-        if (xiEvent->evtype == XI_TouchBegin || xiEvent->evtype == XI_TouchUpdate || xiEvent->evtype == XI_TouchEnd) {
-            xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
-            if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
-                qCDebug(lcQpaXInput, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f",
-                        event->event_type, xiEvent->sequenceNumber, xiDeviceEvent->detail,
-                        fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y),
-                        fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y) );
-
-            if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) {
-                XInput2TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
-                Q_ASSERT(dev);
-                const bool firstTouch = m_touchPoints.isEmpty();
-                if (xiEvent->evtype == XI_TouchBegin) {
-                    QWindowSystemInterface::TouchPoint tp;
-                    tp.id = xiDeviceEvent->detail % INT_MAX;
-                    tp.state = Qt::TouchPointPressed;
-                    tp.pressure = -1.0;
-                    m_touchPoints[tp.id] = tp;
-                }
-                QWindowSystemInterface::TouchPoint &touchPoint = m_touchPoints[xiDeviceEvent->detail];
-                qreal x = fixed1616ToReal(xiDeviceEvent->root_x);
-                qreal y = fixed1616ToReal(xiDeviceEvent->root_y);
-                qreal nx = -1.0, ny = -1.0, d = 0.0;
-                QXcbScreen* screen = m_screens.at(0);
-                for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
-                    XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
-                    if (classinfo->type == XIValuatorClass) {
-                        XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
-                        int n = vci->number;
-                        double value;
-                        if (!xi2GetValuatorValueIfSet(xiDeviceEvent, n, &value))
-                            continue;
-                        if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
-                            qCDebug(lcQpaXInput, "   valuator %20s value %lf from range %lf -> %lf",
-                                    atomName(vci->label).constData(), value, vci->min, vci->max );
-                        if (vci->label == atom(QXcbAtom::RelX)) {
-                            nx = valuatorNormalized(value, vci);
-                        } else if (vci->label == atom(QXcbAtom::RelY)) {
-                            ny = valuatorNormalized(value, vci);
-                        } else if (vci->label == atom(QXcbAtom::AbsX)) {
-                            nx = valuatorNormalized(value, vci);
-                        } else if (vci->label == atom(QXcbAtom::AbsY)) {
-                            ny = valuatorNormalized(value, vci);
-                        } else if (vci->label == atom(QXcbAtom::AbsMTPositionX)) {
-                            nx = valuatorNormalized(value, vci);
-                        } else if (vci->label == atom(QXcbAtom::AbsMTPositionY)) {
-                            ny = valuatorNormalized(value, vci);
-                        } else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) {
-                            d = valuatorNormalized(value, vci) * screen->geometry().width();
-                        } else if (vci->label == atom(QXcbAtom::AbsMTPressure) ||
-                                   vci->label == atom(QXcbAtom::AbsPressure)) {
-                            touchPoint.pressure = valuatorNormalized(value, vci);
-                        }
-                    }
-                }
-                // If any value was not updated, use the last-known value.
-                if (nx == -1.0) {
-                    x = touchPoint.area.center().x();
-                    nx = x / screen->geometry().width();
-                }
-                if (ny == -1.0) {
-                    y = touchPoint.area.center().y();
-                    ny = y / screen->geometry().height();
-                }
-                if (xiEvent->evtype != XI_TouchEnd) {
-                    if (d == 0.0)
-                        d = touchPoint.area.width();
-                }
-
-                switch (xiEvent->evtype) {
-                case XI_TouchBegin:
-                    if (firstTouch) {
-                        dev->firstPressedPosition = QPointF(x, y);
-                        dev->firstPressedNormalPosition = QPointF(nx, ny);
-                    }
-                    dev->pointPressedPosition.insert(touchPoint.id, QPointF(x, y));
-                    break;
-                case XI_TouchUpdate:
-                    if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
-                        qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
-                            dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
-                        qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
-                            dev->size.height() * screen->geometry().height() / screen->physicalSize().height();
-                        x = dev->firstPressedPosition.x() + dx;
-                        y = dev->firstPressedPosition.y() + dy;
-                        touchPoint.state = Qt::TouchPointMoved;
-                    } else if (touchPoint.area.center() != QPoint(x, y)) {
-                        touchPoint.state = Qt::TouchPointMoved;
-                        dev->pointPressedPosition[touchPoint.id] = QPointF(x, y);
-                    }
-                    break;
-                case XI_TouchEnd:
-                    touchPoint.state = Qt::TouchPointReleased;
-                    if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
-                        qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
-                            dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
-                        qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
-                            dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
-                        x = dev->firstPressedPosition.x() + dx;
-                        y = dev->firstPressedPosition.y() + dy;
-                    }
-                    dev->pointPressedPosition.remove(touchPoint.id);
-                }
-                touchPoint.area = QRectF(x - d/2, y - d/2, d, d);
-                touchPoint.normalPosition = QPointF(nx, ny);
+        if (xiDeviceEvent) {
+            switch (xiDeviceEvent->evtype) {
+            case XI_ButtonPress:
+            case XI_ButtonRelease:
+            case XI_Motion:
+                if (xi2MouseEvents() && eventListener)
+                    eventListener->handleXIMouseEvent(event);
+                break;
 
+            case XI_TouchBegin:
+            case XI_TouchUpdate:
+            case XI_TouchEnd:
                 if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
-                    qCDebug(lcQpaXInput) << "   touchpoint "  << touchPoint.id << " state " << touchPoint.state << " pos norm " << touchPoint.normalPosition <<
-                            " area " << touchPoint.area << " pressure " << touchPoint.pressure;
-                QWindowSystemInterface::handleTouchEvent(platformWindow->window(), xiEvent->time, dev->qtTouchDevice, m_touchPoints.values());
-                if (touchPoint.state == Qt::TouchPointReleased)
-                    // If a touchpoint was released, we can forget it, because the ID won't be reused.
-                    m_touchPoints.remove(touchPoint.id);
-                else
-                    // Make sure that we don't send TouchPointPressed/Moved in more than one QTouchEvent
-                    // with this touch point if the next XI2 event is about a different touch point.
-                    touchPoint.state = Qt::TouchPointStationary;
+                    qCDebug(lcQpaXInput, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x",
+                            event->event_type, xiDeviceEvent->sequenceNumber, xiDeviceEvent->detail,
+                            fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y),
+                            fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y),xiDeviceEvent->event);
+                if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event))
+                    xi2ProcessTouch(xiDeviceEvent, platformWindow);
+                break;
             }
         }
 #endif // XCB_USE_XINPUT22
     }
 }
 
+#ifdef XCB_USE_XINPUT22
+void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow)
+{
+    xXIDeviceEvent *xiDeviceEvent = static_cast<xXIDeviceEvent *>(xiDevEvent);
+    XInput2TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
+    Q_ASSERT(dev);
+    const bool firstTouch = m_touchPoints.isEmpty();
+    if (xiDeviceEvent->evtype == XI_TouchBegin) {
+        QWindowSystemInterface::TouchPoint tp;
+        tp.id = xiDeviceEvent->detail % INT_MAX;
+        tp.state = Qt::TouchPointPressed;
+        tp.pressure = -1.0;
+        m_touchPoints[tp.id] = tp;
+    }
+    QWindowSystemInterface::TouchPoint &touchPoint = m_touchPoints[xiDeviceEvent->detail];
+    qreal x = fixed1616ToReal(xiDeviceEvent->root_x);
+    qreal y = fixed1616ToReal(xiDeviceEvent->root_y);
+    qreal nx = -1.0, ny = -1.0, d = 0.0;
+    QXcbScreen* screen = m_screens.at(0);
+    for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
+        XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
+        if (classinfo->type == XIValuatorClass) {
+            XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
+            int n = vci->number;
+            double value;
+            if (!xi2GetValuatorValueIfSet(xiDeviceEvent, n, &value))
+                continue;
+            if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
+                qCDebug(lcQpaXInput, "   valuator %20s value %lf from range %lf -> %lf",
+                        atomName(vci->label).constData(), value, vci->min, vci->max );
+            if (vci->label == atom(QXcbAtom::RelX)) {
+                nx = valuatorNormalized(value, vci);
+            } else if (vci->label == atom(QXcbAtom::RelY)) {
+                ny = valuatorNormalized(value, vci);
+            } else if (vci->label == atom(QXcbAtom::AbsX)) {
+                nx = valuatorNormalized(value, vci);
+            } else if (vci->label == atom(QXcbAtom::AbsY)) {
+                ny = valuatorNormalized(value, vci);
+            } else if (vci->label == atom(QXcbAtom::AbsMTPositionX)) {
+                nx = valuatorNormalized(value, vci);
+            } else if (vci->label == atom(QXcbAtom::AbsMTPositionY)) {
+                ny = valuatorNormalized(value, vci);
+            } else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) {
+                d = valuatorNormalized(value, vci) * screen->geometry().width();
+            } else if (vci->label == atom(QXcbAtom::AbsMTPressure) ||
+                       vci->label == atom(QXcbAtom::AbsPressure)) {
+                touchPoint.pressure = valuatorNormalized(value, vci);
+            }
+        }
+    }
+    // If any value was not updated, use the last-known value.
+    if (nx == -1.0) {
+        x = touchPoint.area.center().x();
+        nx = x / screen->geometry().width();
+    }
+    if (ny == -1.0) {
+        y = touchPoint.area.center().y();
+        ny = y / screen->geometry().height();
+    }
+    if (xiDeviceEvent->evtype != XI_TouchEnd) {
+        if (d == 0.0)
+            d = touchPoint.area.width();
+    }
+
+    switch (xiDeviceEvent->evtype) {
+    case XI_TouchBegin:
+        if (firstTouch) {
+            dev->firstPressedPosition = QPointF(x, y);
+            dev->firstPressedNormalPosition = QPointF(nx, ny);
+        }
+        dev->pointPressedPosition.insert(touchPoint.id, QPointF(x, y));
+
+        // Touches must be accepted when we are grabbing touch events. Otherwise the entire sequence
+        // will get replayed when the grab ends.
+        if (m_xiGrab) {
+            // XIAllowTouchEvents deadlocks with libXi < 1.7.4 (this has nothing to do with the XI2 versions like 2.2)
+            // http://lists.x.org/archives/xorg-devel/2014-July/043059.html
+#ifndef LIBXI_MAJOR
+            static bool allowTouchWarningShown = false;
+            if (!allowTouchWarningShown) {
+                allowTouchWarningShown = true;
+                qWarning("Skipping XIAllowTouchEvents() because it was not possible to detect libXi version at build time."
+                         " Minimum libXi version required is 1.7.4."
+                         " Expect issues with touch behavior.");
+            }
+#elif LIBXI_MAJOR == 1 && (LIBXI_MINOR < 7 || (LIBXI_MINOR == 7 && LIBXI_PATCH < 4))
+            static bool allowTouchWarningShown = false;
+            if (!allowTouchWarningShown) {
+                allowTouchWarningShown = true;
+                qWarning("Skipping XIAllowTouchEvents() due to not having libXi >= 1.7.4."
+                         " libXi version at build time was %d.%d.%d."
+                         " Expect issues with touch behavior.", LIBXI_MAJOR, LIBXI_MINOR, LIBXI_PATCH);
+            }
+#else
+            XIAllowTouchEvents(static_cast<Display *>(m_xlib_display), xiDeviceEvent->deviceid,
+                               xiDeviceEvent->detail, xiDeviceEvent->event, XIAcceptTouch);
+#endif
+        }
+        break;
+    case XI_TouchUpdate:
+        if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
+            qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
+                dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
+            qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
+                dev->size.height() * screen->geometry().height() / screen->physicalSize().height();
+            x = dev->firstPressedPosition.x() + dx;
+            y = dev->firstPressedPosition.y() + dy;
+            touchPoint.state = Qt::TouchPointMoved;
+        } else if (touchPoint.area.center() != QPoint(x, y)) {
+            touchPoint.state = Qt::TouchPointMoved;
+            dev->pointPressedPosition[touchPoint.id] = QPointF(x, y);
+        }
+        break;
+    case XI_TouchEnd:
+        touchPoint.state = Qt::TouchPointReleased;
+        if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
+            qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
+                dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
+            qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
+                dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
+            x = dev->firstPressedPosition.x() + dx;
+            y = dev->firstPressedPosition.y() + dy;
+        }
+        dev->pointPressedPosition.remove(touchPoint.id);
+    }
+    touchPoint.area = QRectF(x - d/2, y - d/2, d, d);
+    touchPoint.normalPosition = QPointF(nx, ny);
+
+    if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
+        qCDebug(lcQpaXInput) << "   touchpoint "  << touchPoint.id << " state " << touchPoint.state << " pos norm " << touchPoint.normalPosition <<
+            " area " << touchPoint.area << " pressure " << touchPoint.pressure;
+    QWindowSystemInterface::handleTouchEvent(platformWindow->window(), xiDeviceEvent->time, dev->qtTouchDevice, m_touchPoints.values());
+    if (touchPoint.state == Qt::TouchPointReleased)
+        // If a touchpoint was released, we can forget it, because the ID won't be reused.
+        m_touchPoints.remove(touchPoint.id);
+    else
+        // Make sure that we don't send TouchPointPressed/Moved in more than one QTouchEvent
+        // with this touch point if the next XI2 event is about a different touch point.
+        touchPoint.state = Qt::TouchPointStationary;
+}
+
+bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
+{
+    if (grab && !canGrab())
+        return false;
+
+    int num_devices = 0;
+    Display *xDisplay = static_cast<Display *>(xlib_display());
+    XIDeviceInfo *info = XIQueryDevice(xDisplay, XIAllMasterDevices, &num_devices);
+    if (!info)
+        return false;
+
+    XIEventMask evmask;
+    unsigned char mask[XIMaskLen(XI_LASTEVENT)];
+    evmask.mask = mask;
+    evmask.mask_len = sizeof(mask);
+    memset(mask, 0, sizeof(mask));
+    evmask.deviceid = XIAllMasterDevices;
+
+    XISetMask(mask, XI_ButtonPress);
+    XISetMask(mask, XI_ButtonRelease);
+    XISetMask(mask, XI_Motion);
+    XISetMask(mask, XI_TouchBegin);
+    XISetMask(mask, XI_TouchUpdate);
+    XISetMask(mask, XI_TouchEnd);
+
+    bool grabbed = true;
+    for (int i = 0; i < num_devices; i++) {
+        int id = info[i].deviceid, n = 0;
+        XIDeviceInfo *deviceInfo = XIQueryDevice(xDisplay, id, &n);
+        if (deviceInfo) {
+            const bool grabbable = deviceInfo->use != XIMasterKeyboard;
+            XIFreeDeviceInfo(deviceInfo);
+            if (!grabbable)
+                continue;
+        }
+        if (!grab) {
+            Status result = XIUngrabDevice(xDisplay, id, CurrentTime);
+            if (result != Success) {
+                grabbed = false;
+                qCDebug(lcQpaXInput, "XInput 2.2: failed to ungrab events for device %d (result %d)", id, result);
+            }
+        } else {
+            Status result = XIGrabDevice(xDisplay, id, w, CurrentTime, None, XIGrabModeAsync,
+                                         XIGrabModeAsync, False, &evmask);
+            if (result != Success) {
+                grabbed = false;
+                qCDebug(lcQpaXInput, "XInput 2.2: failed to grab events for device %d on window %x (result %d)", id, w, result);
+            }
+        }
+    }
+
+    XIFreeDeviceInfo(info);
+
+    m_xiGrab = grabbed;
+
+    return grabbed;
+}
+#endif // XCB_USE_XINPUT22
+
 void QXcbConnection::xi2HandleHierachyEvent(void *event)
 {
     xXIHierarchyEvent *xiEvent = reinterpret_cast<xXIHierarchyEvent *>(event);
@@ -785,7 +914,8 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin
 #endif // XCB_USE_XINPUT21
 }
 
-static Qt::MouseButton xiToQtMouseButton(uint32_t b) {
+Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b)
+{
     switch (b) {
     case 1: return Qt::LeftButton;
     case 2: return Qt::MiddleButton;
@@ -832,20 +962,29 @@ static QTabletEvent::TabletDevice toolIdToTabletDevice(quint32 toolId) {
 }
 
 #ifndef QT_NO_TABLETEVENT
-bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData)
+bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData, QXcbWindowEventListener *eventListener)
 {
     bool handled = true;
     Display *xDisplay = static_cast<Display *>(m_xlib_display);
     xXIGenericDeviceEvent *xiEvent = static_cast<xXIGenericDeviceEvent *>(event);
+    xXIDeviceEvent *xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(xiEvent);
+
+#ifdef XCB_USE_XINPUT22
+    // Synthesize mouse events since otherwise there are no mouse events from
+    // the pen on the XI 2.2+ path.
+    if (xi2MouseEvents() && eventListener)
+        eventListener->handleXIMouseEvent(reinterpret_cast<xcb_ge_event_t *>(event));
+#endif
+
     switch (xiEvent->evtype) {
     case XI_ButtonPress: {
-        Qt::MouseButton b = xiToQtMouseButton(reinterpret_cast<xXIDeviceEvent *>(event)->detail);
+        Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail);
         tabletData->buttons |= b;
         xi2ReportTabletEvent(*tabletData, xiEvent);
         break;
     }
     case XI_ButtonRelease: {
-        Qt::MouseButton b = xiToQtMouseButton(reinterpret_cast<xXIDeviceEvent *>(event)->detail);
+        Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail);
         tabletData->buttons ^= b;
         xi2ReportTabletEvent(*tabletData, xiEvent);
         break;
@@ -908,7 +1047,7 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData)
                         // TODO maybe have a hash of tabletData->deviceId to device data so we can
                         // look up the tablet name here, and distinguish multiple tablets
                         qCDebug(lcQpaXInput, "XI2 proximity change on tablet %d (USB %x): last tool: %x id %x current tool: %x id %x TabletDevice %d",
-                            ev->deviceid, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID],
+                            tabletData->deviceId, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID],
                             ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], tabletData->tool);
                     }
                     XFree(data);
@@ -972,7 +1111,7 @@ void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event)
 
     if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
         qCDebug(lcQpaXInput, "XI2 event on tablet %d with tool %d type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf",
-            ev->deviceid, tabletData.tool, ev->evtype, ev->sequenceNumber, ev->detail,
+            tabletData.deviceId, tabletData.tool, ev->evtype, ev->sequenceNumber, ev->detail,
             fixed1616ToReal(ev->event_x), fixed1616ToReal(ev->event_y),
             fixed1616ToReal(ev->root_x), fixed1616ToReal(ev->root_y),
             (int)tabletData.buttons, pressure, xTilt, yTilt, rotation);
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index 6a9ef5869e8cb58be17079a1b9a0c84799969b32..2d96ed1c21c7fe0a3e5fd00f484e14518e397e25 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -47,6 +47,12 @@
 #include <stdio.h>
 #include <X11/keysym.h>
 
+#ifdef XCB_USE_XINPUT22
+#include <X11/extensions/XI2proto.h>
+#undef KeyPress
+#undef KeyRelease
+#endif
+
 #ifndef XK_ISO_Left_Tab
 #define XK_ISO_Left_Tab         0xFE20
 #endif
@@ -791,6 +797,31 @@ void QXcbKeyboard::updateXKBStateFromCore(quint16 state)
     }
 }
 
+void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo)
+{
+#ifdef XCB_USE_XINPUT22
+    if (m_config && !connection()->hasXKB()) {
+        xXIModifierInfo *mods = static_cast<xXIModifierInfo *>(modInfo);
+        xXIGroupInfo *group = static_cast<xXIGroupInfo *>(groupInfo);
+        const xkb_state_component newState = xkb_state_update_mask(xkb_state,
+                                                                   mods->base_mods,
+                                                                   mods->latched_mods,
+                                                                   mods->locked_mods,
+                                                                   group->base_group,
+                                                                   group->latched_group,
+                                                                   group->locked_group);
+
+        if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) {
+            //qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)");
+        }
+    }
+#else
+    Q_UNUSED(modInfo);
+    Q_UNUSED(groupInfo);
+    Q_ASSERT(false); // this can't be
+#endif
+}
+
 quint32 QXcbKeyboard::xkbModMask(quint16 state)
 {
     quint32 xkb_mask = 0;
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h
index 2281674e2f6e4e54ab564b9ee4f355c4c5fd5f6c..d2e37d624cfe5960bf50d8f18f2a82756824fa4d 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.h
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.h
@@ -68,6 +68,7 @@ public:
     void updateXKBMods();
     quint32 xkbModMask(quint16 state);
     void updateXKBStateFromCore(quint16 state);
+    void updateXKBStateFromXI(void *modInfo, void *groupInfo);
 #ifndef QT_NO_XKB
     // when XKEYBOARD is present on the X server
     int coreDeviceId() const { return core_device_id; }
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 3188a7f19de2e7e71cf545c03017ca8338b2f3fb..d1b688857d6cbe670b21a4f6d1d9c7aaa40f95e9 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -96,6 +96,7 @@
 
 #if defined(XCB_USE_XINPUT2)
 #include <X11/extensions/XInput2.h>
+#include <X11/extensions/XI2proto.h>
 #endif
 
 #define XCOORD_MAX 16383
@@ -2106,16 +2107,17 @@ void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event)
     }
 }
 
-void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
+void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
+                                        int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp)
 {
-    const bool isWheel = event->detail >= 4 && event->detail <= 7;
+    const bool isWheel = detail >= 4 && detail <= 7;
     if (!isWheel && window() != QGuiApplication::focusWindow()) {
         QWindow *w = static_cast<QWindowPrivate *>(QObjectPrivate::get(window()))->eventReceiver();
         if (!(w->flags() & Qt::WindowDoesNotAcceptFocus))
             w->requestActivate();
     }
 
-    updateNetWmUserTime(event->time);
+    updateNetWmUserTime(timestamp);
 
     if (m_embedded) {
         if (window() != QGuiApplication::focusWindow()) {
@@ -2126,53 +2128,125 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
         }
     }
     const int dpr = int(devicePixelRatio());
-    QPoint local(event->event_x/dpr, event->event_y/dpr);
-    QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y));
-
-    Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
+    QPoint local(event_x / dpr, event_y / dpr);
+    QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y));
 
     if (isWheel) {
-        if (!connection()->isUsingXInput21()) {
+        if (!connection()->isAtLeastXI21()) {
             // Logic borrowed from qapplication_x11.cpp
-            int delta = 120 * ((event->detail == 4 || event->detail == 6) ? 1 : -1);
-            bool hor = (((event->detail == 4 || event->detail == 5)
+            int delta = 120 * ((detail == 4 || detail == 6) ? 1 : -1);
+            bool hor = (((detail == 4 || detail == 5)
                          && (modifiers & Qt::AltModifier))
-                        || (event->detail == 6 || event->detail == 7));
+                        || (detail == 6 || detail == 7));
 
-            QWindowSystemInterface::handleWheelEvent(window(), event->time,
+            QWindowSystemInterface::handleWheelEvent(window(), timestamp,
                                                      local, global, delta, hor ? Qt::Horizontal : Qt::Vertical, modifiers);
         }
         return;
     }
 
-    handleMouseEvent(event->time, local, global, modifiers);
+    handleMouseEvent(timestamp, local, global, modifiers);
 }
 
-void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event)
+void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y,
+                                          int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp)
 {
     const int dpr = int(devicePixelRatio());
-    QPoint local(event->event_x/dpr, event->event_y/dpr);
-    QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y));
-    Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
+    QPoint local(event_x / dpr, event_y / dpr);
+    QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y));
 
-    if (event->detail >= 4 && event->detail <= 7) {
+    if (detail >= 4 && detail <= 7) {
         // mouse wheel, handled in handleButtonPressEvent()
         return;
     }
 
-    handleMouseEvent(event->time, local, global, modifiers);
+    handleMouseEvent(timestamp, local, global, modifiers);
 }
 
-void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
+void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
+                                         Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp)
 {
-    const int dpr = int(devicePixelRatio());
-    QPoint local(event->event_x/dpr, event->event_y/dpr);
     if (!xcbScreen())
         return;
-    QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y));
+    const int dpr = int(devicePixelRatio());
+    QPoint local(event_x / dpr, event_y / dpr);
+    QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y));
+    handleMouseEvent(timestamp, local, global, modifiers);
+}
+
+// Handlers for plain xcb events. Used only when XI 2.2 or newer is not available.
+void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
+{
     Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
+    handleButtonPressEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail,
+                           modifiers, event->time);
+}
 
-    handleMouseEvent(event->time, local, global, modifiers);
+void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event)
+{
+    Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
+    handleButtonReleaseEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail,
+                             modifiers, event->time);
+}
+
+void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
+{
+    Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
+    handleMotionNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, modifiers, event->time);
+}
+
+#ifdef XCB_USE_XINPUT22
+static inline int fixed1616ToInt(FP1616 val)
+{
+    return int((qreal(val >> 16)) + (val & 0xFFFF) / (qreal)0xFFFF);
+}
+#endif
+
+// With XI 2.2+ press/release/motion comes here instead of the above handlers.
+void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event)
+{
+#ifdef XCB_USE_XINPUT22
+    QXcbConnection *conn = connection();
+    xXIDeviceEvent *ev = reinterpret_cast<xXIDeviceEvent *>(event);
+    const Qt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(ev->mods.effective_mods);
+    const int event_x = fixed1616ToInt(ev->event_x);
+    const int event_y = fixed1616ToInt(ev->event_y);
+    const int root_x = fixed1616ToInt(ev->root_x);
+    const int root_y = fixed1616ToInt(ev->root_y);
+
+    conn->keyboard()->updateXKBStateFromXI(&ev->mods, &ev->group);
+
+    const Qt::MouseButton button = conn->xiToQtMouseButton(ev->detail);
+
+    if (ev->buttons_len > 0) {
+        unsigned char *buttonMask = (unsigned char *) &ev[1];
+        for (int i = 1; i <= 15; ++i)
+            conn->setButton(conn->translateMouseButton(i), XIMaskIsSet(buttonMask, i));
+    }
+
+    switch (ev->evtype) {
+    case XI_ButtonPress:
+        qCDebug(lcQpaXInput, "XI2 mouse press, button %d", button);
+        conn->setButton(button, true);
+        handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time);
+        break;
+    case XI_ButtonRelease:
+        qCDebug(lcQpaXInput, "XI2 mouse release, button %d", button);
+        conn->setButton(button, false);
+        handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time);
+        break;
+    case XI_Motion:
+        qCDebug(lcQpaXInput, "XI2 mouse motion %d,%d", event_x, event_y);
+        handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time);
+        break;
+    default:
+        qWarning() << "Unrecognized XI2 mouse event" << ev->evtype;
+        break;
+    }
+#else
+    Q_UNUSED(event);
+    Q_ASSERT(false); // this can't be
+#endif
 }
 
 QXcbWindow *QXcbWindow::toWindow() { return this; }
@@ -2356,6 +2430,10 @@ bool QXcbWindow::setKeyboardGrabEnabled(bool grab)
 
 bool QXcbWindow::setMouseGrabEnabled(bool grab)
 {
+#ifdef XCB_USE_XINPUT22
+    if (connection()->xi2MouseEvents())
+        return connection()->xi2SetMouseGrabEnabled(m_window, grab);
+#endif
     if (grab && !connection()->canGrab())
         return false;
 
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index 512bc54255de1d487e46083c6f16be2df6ada53a..e62bfcba64d0252b811fdd8f29473f3324f51b81 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -131,6 +131,7 @@ public:
     void handleFocusInEvent(const xcb_focus_in_event_t *event) Q_DECL_OVERRIDE;
     void handleFocusOutEvent(const xcb_focus_out_event_t *event) Q_DECL_OVERRIDE;
     void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) Q_DECL_OVERRIDE;
+    void handleXIMouseEvent(xcb_ge_event_t *) Q_DECL_OVERRIDE;
 
     QXcbWindow *toWindow() Q_DECL_OVERRIDE;
 
@@ -199,6 +200,15 @@ protected:
     void doFocusIn();
     void doFocusOut();
 
+    void handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
+                                int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp);
+
+    void handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y,
+                                  int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp);
+
+    void handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
+                                 Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp);
+
     xcb_window_t m_window;
 
     QXcbScreen *m_xcbScreen;
diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
index fd704dd9041a2829862b9398cebe07b032291b88..12987567ff8d4e064695e9a7ef48d194fcb6bc19 100644
--- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro
+++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
@@ -59,6 +59,11 @@ contains(QT_CONFIG, xcb-xlib) {
         DEFINES += XCB_USE_XINPUT2
         SOURCES += qxcbconnection_xi2.cpp
         LIBS += -lXi
+        !isEmpty(QMAKE_LIBXI_VERSION_MAJOR) {
+            DEFINES += LIBXI_MAJOR=$$QMAKE_LIBXI_VERSION_MAJOR \
+                       LIBXI_MINOR=$$QMAKE_LIBXI_VERSION_MINOR \
+                       LIBXI_PATCH=$$QMAKE_LIBXI_VERSION_PATCH
+        }
     }
 }