Add multipoint touch support for Harmattan to the xcb platform plugin.
authorJocelyn Turcotte <jocelyn.turcotte@nokia.com>
Fri, 14 Oct 2011 13:55:35 +0000 (15:55 +0200)
committerQt by Nokia <qt-info@nokia.com>
Mon, 17 Oct 2011 16:03:13 +0000 (18:03 +0200)
Proper multipoint touch support was only introduced in XInput2.1, but Harmattan uses
a tweaked version of XInput2.0 that transfers touch data through mouse events.

This patch applies on the xcb plugin a subset of the changes that were applied on the
Qt 4.7 that was shipped to Harmattan to get similar multipoint touch support.

Change-Id: Ifda7ad40de29d7ded1443d4f78b3ec3807303a9f
Reviewed-by: Simon Hausmann <simon.hausmann@nokia.com>
config.tests/x11/xinput2/xinput2.cpp [new file with mode: 0644]
config.tests/x11/xinput2/xinput2.pro [new file with mode: 0644]
configure
src/plugins/platforms/xcb/qxcbconnection.cpp
src/plugins/platforms/xcb/qxcbconnection.h
src/plugins/platforms/xcb/qxcbconnection_maemo.cpp [new file with mode: 0644]
src/plugins/platforms/xcb/qxcbwindow.cpp
src/plugins/platforms/xcb/xcb.pro

diff --git a/config.tests/x11/xinput2/xinput2.cpp b/config.tests/x11/xinput2/xinput2.cpp
new file mode 100644 (file)
index 0000000..14a51f4
--- /dev/null
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the config.tests of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <X11/Xlib.h>
+#include <X11/extensions/XInput2.h>
+#include <X11/extensions/Xge.h>
+
+#ifndef XInput_2_0
+#  error "Missing XInput_2_0 #define"
+#endif
+
+int main(int, char **)
+{
+    // need XGenericEventCookie for XInput2 to work
+    Display *dpy = 0;
+    XEvent xevent;
+    if (XGetEventData(dpy, &xevent.xcookie)) {
+        XFreeEventData(dpy, &xevent.xcookie);
+    }
+
+    XIEvent *xievent;
+    xievent = 0;
+
+    XIDeviceEvent *xideviceevent;
+    xideviceevent = 0;
+
+    XIHierarchyEvent *xihierarchyevent;
+    xihierarchyevent = 0;
+
+    int deviceid = 0;
+    int len = 0;
+    Atom *atoms = XIListProperties(dpy, deviceid, &len);
+    if (atoms)
+        XFree(atoms);
+
+    return 0;
+}
diff --git a/config.tests/x11/xinput2/xinput2.pro b/config.tests/x11/xinput2/xinput2.pro
new file mode 100644 (file)
index 0000000..ae8819b
--- /dev/null
@@ -0,0 +1,4 @@
+CONFIG += x11
+CONFIG -= qt
+LIBS += -lXi
+SOURCES = xinput2.cpp
index 708d740..e985f52 100755 (executable)
--- a/configure
+++ b/configure
@@ -748,6 +748,7 @@ CFG_DECORATION_AVAILABLE="styled windows default"
 CFG_DECORATION_ON="${CFG_DECORATION_AVAILABLE}" # all on by default
 CFG_DECORATION_PLUGIN_AVAILABLE=
 CFG_DECORATION_PLUGIN=
+CFG_XINPUT2=auto
 CFG_XINPUT=runtime
 CFG_XKB=auto
 CFG_XCB=auto
@@ -819,6 +820,7 @@ XPLATFORM=              # This seems to be the QMAKESPEC, like "linux-g++" or "s
 XPLATFORM_MINGW=no      # Whether target platform is MinGW (win32-g++*)
 XPLATFORM_SYMBIAN=no    # Whether target platform is SYMBIAN (*symbian*)
 XPLATFORM_SYMBIAN_SBSV2=no # Whether target platform is SYMBIAN_SBSV2 (symbian-sbsv2)
+XPLATFORM_MAEMO=no
 PLATFORM=$QMAKESPEC
 QT_CROSS_COMPILE=no
 OPT_CONFIRM_LICENSE=no
@@ -1042,7 +1044,7 @@ while [ "$#" -gt 0 ]; do
         VAL=no
         ;;
     #Qt style yes options
-    -incremental|-qvfb|-profile|-shared|-static|-sm|-xinerama|-xshape|-xsync|-xinput|-egl|-reduce-exports|-pch|-separate-debug-info|-stl|-freetype|-xcursor|-xfixes|-xrandr|-xrender|-mitshm|-fontconfig|-xkb|-xcb|-wayland|-nis|-qdbus|-dbus|-dbus-linked|-glib|-gstreamer|-gtkstyle|-cups|-iconv|-largefile|-h|-help|-v|-verbose|-debug|-release|-fast|-accessibility|-confirm-license|-gnumake|-framework|-qt3support|-debug-and-release|-exceptions|-cocoa|-carbon|-universal|-harfbuzz|-prefix-install|-silent|-armfpa|-optimized-qmake|-dwarf2|-reduce-relocations|-sse|-openssl|-openssl-linked|-ptmalloc|-xmlpatterns|-phonon|-phonon-backend|-multimedia|-audio-backend|-svg|-v8|-declarative|-declarative-debug|-javascript-jit|-script|-scripttools|-rpath|-force-pkg-config|-s60|-usedeffiles|-icu)
+    -incremental|-qvfb|-profile|-shared|-static|-sm|-xinerama|-xshape|-xsync|-xinput|-xinput2|-egl|-reduce-exports|-pch|-separate-debug-info|-stl|-freetype|-xcursor|-xfixes|-xrandr|-xrender|-mitshm|-fontconfig|-xkb|-xcb|-wayland|-nis|-qdbus|-dbus|-dbus-linked|-glib|-gstreamer|-gtkstyle|-cups|-iconv|-largefile|-h|-help|-v|-verbose|-debug|-release|-fast|-accessibility|-confirm-license|-gnumake|-framework|-qt3support|-debug-and-release|-exceptions|-cocoa|-carbon|-universal|-harfbuzz|-prefix-install|-silent|-armfpa|-optimized-qmake|-dwarf2|-reduce-relocations|-sse|-openssl|-openssl-linked|-ptmalloc|-xmlpatterns|-phonon|-phonon-backend|-multimedia|-audio-backend|-svg|-v8|-declarative|-declarative-debug|-javascript-jit|-script|-scripttools|-rpath|-force-pkg-config|-s60|-usedeffiles|-icu)
         VAR=`echo $1 | sed "s,^-\(.*\),\1,"`
         VAL=yes
         ;;
@@ -1592,6 +1594,7 @@ while [ "$#" -gt 0 ]; do
         case `basename "$XPLATFORM"` in win32-g++*) XPLATFORM_MINGW=yes;; esac
         case "$XPLATFORM" in *symbian*) XPLATFORM_SYMBIAN=yes;; esac
         case "$XPLATFORM" in symbian-sbsv2) XPLATFORM_SYMBIAN_SBSV2=yes;; esac
+        case "$XPLATFORM" in linux-g++-maemo) XPLATFORM_MAEMO=yes;; esac
         ;;
     debug-and-release)
         if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then
@@ -1712,6 +1715,13 @@ while [ "$#" -gt 0 ]; do
             UNKNOWN_OPT=yes
         fi
         ;;
+     xinput2)
+        if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then
+            CFG_XINPUT2="$VAL"
+        else
+            UNKNOWN_OPT=yes
+        fi
+        ;;
     xinput)
         if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ] || [ "$VAL" = "runtime" ]; then
             CFG_XINPUT="$VAL"
@@ -2913,6 +2923,7 @@ fi
 case `basename "$XPLATFORM"` in win32-g++*) XPLATFORM_MINGW=yes;; esac
 case "$XPLATFORM" in *symbian*) XPLATFORM_SYMBIAN=yes;; esac
 case "$XPLATFORM" in symbian-sbsv2) XPLATFORM_SYMBIAN_SBSV2=yes;; esac
+case "$XPLATFORM" in linux-g++-maemo) XPLATFORM_MAEMO=yes;; esac
 
 if [ -d "$PLATFORM" ]; then
   QMAKESPEC="$PLATFORM"
@@ -3732,6 +3743,13 @@ if [ "$OPT_HELP" = "yes" ]; then
         XWY="*"
         XWN=" "
     fi
+    if [ "$CFG_XINPUT2" = "no" ]; then
+        X2Y=" "
+        X2N="*"
+    else
+        X2Y="*"
+        X2N=" "
+    fi
 
     cat <<EOF
 Usage:  $relconf [-h] [-prefix <dir>] [-prefix-install] [-bindir <dir>] [-libdir <dir>]
@@ -4088,6 +4106,17 @@ EOF
 
 fi # X11/QWS
 
+if [ "$XPLATFORM_MAEMO" = "yes" ]; then
+
+    cat << EOF
+
+ $X2N  -no-xinput2......... Do not compile XInput2 support.
+ $X2Y  -xinput2............ Compile XInput2 support.
+
+EOF
+
+fi
+
 if [ "$PLATFORM_X11" = "yes" ]; then
     if [ "$CFG_SM" = "no" ]; then
         SMY=" "
@@ -6301,6 +6330,26 @@ if [ "$PLATFORM_QPA" = "yes" ]; then
             if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/qpa/xcb-xlib "xcb-xlib" $L_FLAGS $I_FLAGS $l_FLAGS; then
                 QT_CONFIG="$QT_CONFIG xcb-xlib"
             fi
+
+            if [ "$XPLATFORM_MAEMO" = "yes" ]; then
+                # auto-detect XInput2/Xinput support
+                if [ "$CFG_XINPUT2" != "no" ]; then
+                    if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/x11/xinput2 "XInput2" $L_FLAGS $I_FLAGS $l_FLAGS $X11TESTS_FLAGS; then
+                    CFG_XINPUT2=yes
+                    CFG_XINPUT=no
+                    else
+                        if [ "$CFG_XINPUT2" = "yes" ] && [ "$CFG_CONFIGURE_EXIT_ON_ERROR" = "yes" ]; then
+                            echo "XInput2 support cannot be enabled due to functionality tests!"
+                            echo " Turn on verbose messaging (-v) to $0 to see the final report."
+                            echo " If you believe this message is in error you may use the continue"
+                            echo " switch (-continue) to $0 to continue."
+                            exit 101
+                        else
+                            CFG_XINPUT2=no
+                        fi
+                    fi
+                fi
+            fi
         else
             if [ "$CFG_XCB" = "yes" ]; then
                 echo "The XCB test failed!"
@@ -7271,6 +7320,7 @@ fi
 [ "$CFG_OPENSSL" = "linked" ] && QT_CONFIG="$QT_CONFIG openssl-linked"
 [ "$CFG_MAC_HARFBUZZ" = "yes" ] && QT_CONFIG="$QT_CONFIG harfbuzz"
 [ "$CFG_XCB" = "yes" ] && QT_CONFIG="$QT_CONFIG xcb"
+[ "$CFG_XINPUT2" = "yes" ] && QT_CONFIG="$QT_CONFIG xinput2"
 
 if [ "$PLATFORM_X11" = "yes" ]; then
     [ "$CFG_SM" = "yes" ] && QT_CONFIG="$QT_CONFIG x11sm"
@@ -8633,6 +8683,9 @@ if [ "$CFG_XCB_LIMITED" = "yes" ] && [ "$CFG_XCB" = "yes" ]; then
 else
     echo "Xcb support ............ $CFG_XCB"
 fi
+if [ "$XPLATFORM_MAEMO" = "yes" ] && [ "$CFG_XCB" = "yes" ]; then
+    echo "XInput2 support ........ $CFG_XINPUT2"
+fi
 echo
 
 [ "$CFG_PTMALLOC" != "no" ] && echo "Use ptmalloc ........... $CFG_PTMALLOC"
index af5822f..bdf308a 100644 (file)
@@ -88,6 +88,9 @@ QT_BEGIN_NAMESPACE
 
 QXcbConnection::QXcbConnection(const char *displayName)
     : m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY"))
+#ifdef XCB_USE_XINPUT2_MAEMO
+    , m_xinputData(0)
+#endif
 #ifdef XCB_USE_DRI2
     , m_dri2_major(0)
     , m_dri2_minor(0)
@@ -155,6 +158,9 @@ QXcbConnection::QXcbConnection(const char *displayName)
 
     initializeXFixes();
     initializeXRender();
+#ifdef XCB_USE_XINPUT2_MAEMO
+    initializeXInput2();
+#endif
 
     m_wmSupport = new QXcbWMSupport(this);
     m_keyboard = new QXcbKeyboard(this);
@@ -173,6 +179,10 @@ QXcbConnection::~QXcbConnection()
 
     qDeleteAll(m_screens);
 
+#ifdef XCB_USE_XINPUT2_MAEMO
+    finalizeXInput2();
+#endif
+
 #ifdef XCB_POLL_FOR_QUEUED_EVENT
     sendConnectionEvent(QXcbAtom::_QT_CLOSE_CONNECTION);
     m_reader->wait();
@@ -530,6 +540,11 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
     case XCB_PROPERTY_NOTIFY:
         HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent);
         break;
+#ifdef XCB_USE_XINPUT2_MAEMO
+    case GenericEvent:
+        handleGenericEvent((xcb_ge_event_t*)event);
+        break;
+#endif
     default:
         handled = false;
         break;
@@ -862,6 +877,22 @@ static const char * xcb_atomnames = {
     // Tablet
     "STYLUS\0"
     "ERASER\0"
+
+    // XInput2
+    "Button Left\0"
+    "Button Middle\0"
+    "Button Right\0"
+    "Button Wheel Up\0"
+    "Button Wheel Down\0"
+    "Button Horiz Wheel Left\0"
+    "Button Horiz Wheel Right\0"
+    "Abs MT Position X\0"
+    "Abs MT Position Y\0"
+    "Abs MT Touch Major\0"
+    "Abs MT Touch Minor\0"
+    "Abs MT Pressure\0"
+    "Abs MT Tracking ID\0"
+    "Max Contacts\0"
 };
 
 xcb_atom_t QXcbConnection::atom(QXcbAtom::Atom atom)
index 769b2e7..5887e57 100644 (file)
 #include <QThread>
 #include <QVector>
 
+#ifdef XCB_USE_XINPUT2_MAEMO
+struct XInput2Data;
+#endif
+
 #define Q_XCB_DEBUG
 
 QT_BEGIN_NAMESPACE
@@ -222,6 +226,22 @@ namespace QXcbAtom {
         XTabletStylus,
         XTabletEraser,
 
+        // XInput2
+        ButtonLeft,
+        ButtonMiddle,
+        ButtonRight,
+        ButtonWheelUp,
+        ButtonWheelDown,
+        ButtonHorizWheelLeft,
+        ButtonHorizWheelRight,
+        AbsMTPositionX,
+        AbsMTPositionY,
+        AbsMTTouchMajor,
+        AbsMTTouchMinor,
+        AbsMTPressure,
+        AbsMTTrackingID,
+        MaxContacts,
+
         NPredefinedAtoms,
 
         _QT_SETTINGS_TIMESTAMP = NPredefinedAtoms,
@@ -301,6 +321,9 @@ public:
 #if defined(XCB_USE_EGL) || defined(XCB_USE_DRI2)
     void *egl_display() const { return m_egl_display; }
 #endif
+#ifdef XCB_USE_XINPUT2_MAEMO
+    bool isUsingXInput2();
+#endif
 
     void sync();
     void flush() { xcb_flush(m_connection); }
@@ -335,6 +358,11 @@ private:
 #ifdef XCB_USE_DRI2
     void initializeDri2();
 #endif
+#ifdef XCB_USE_XINPUT2_MAEMO
+    void initializeXInput2();
+    void finalizeXInput2();
+    void handleGenericEvent(xcb_ge_event_t *event);
+#endif
     void handleClientMessageEvent(const xcb_client_message_event_t *event);
 
     xcb_connection_t *m_connection;
@@ -360,6 +388,9 @@ private:
     void *m_xlib_display;
 #endif
     QXcbEventReader *m_reader;
+#ifdef XCB_USE_XINPUT2_MAEMO
+    XInput2Data *m_xinputData;
+#endif
 #ifdef XCB_USE_DRI2
     uint32_t m_dri2_major;
     uint32_t m_dri2_minor;
diff --git a/src/plugins/platforms/xcb/qxcbconnection_maemo.cpp b/src/plugins/platforms/xcb/qxcbconnection_maemo.cpp
new file mode 100644 (file)
index 0000000..d052920
--- /dev/null
@@ -0,0 +1,294 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxcbconnection.h"
+
+#ifdef XCB_USE_XINPUT2_MAEMO
+
+#include "qxcbwindow.h"
+#include <QtGui/QWindowSystemInterface>
+#include <X11/extensions/XInput2.h>
+#include <X11/extensions/XI2proto.h>
+#include <X11/Xatom.h>
+
+// Define it here to work around XLib defining Bool and stuff.
+// We can't declare those variables in the header without facing include order headaches.
+struct XInput2Data {
+    XInput2Data()
+    : use_xinput(false)
+    , xinput_opcode(0)
+    , xinput_eventbase(0)
+    , xinput_errorbase(0)
+    , xideviceinfo(0)
+    , xibuttonclassinfo(0)
+    , xiMaxContacts(0)
+    {
+    }
+    // true if Qt is compiled w/ XInput2 or Tablet support and we have a tablet.
+    bool use_xinput;
+    int xinput_opcode;
+    int xinput_eventbase;
+    int xinput_errorbase;
+    // device info for the master pointer Qt is using
+    XIDeviceInfo *xideviceinfo;
+    XIButtonClassInfo *xibuttonclassinfo;
+    int xiMaxContacts;
+    QList<QWindowSystemInterface::TouchPoint> allTouchPoints;
+};
+
+bool QXcbConnection::isUsingXInput2()
+{
+    return m_xinputData && m_xinputData->use_xinput && m_xinputData->xiMaxContacts != 0;
+}
+
+void QXcbConnection::initializeXInput2()
+{
+    Q_ASSERT(!m_xinputData);
+    m_xinputData = new XInput2Data;
+    m_xinputData->use_xinput = XQueryExtension((Display *)m_xlib_display, "XInputExtension", &m_xinputData->xinput_opcode,
+                                      &m_xinputData->xinput_eventbase, &m_xinputData->xinput_errorbase);
+    if (m_xinputData->use_xinput) {
+        // we want XInput2
+        int ximajor = 2, ximinor = 0;
+        if (XIQueryVersion((Display *)m_xlib_display, &ximajor, &ximinor) == BadRequest) {
+            // XInput2 not available
+            m_xinputData->use_xinput = false;
+        } else {
+            // find the first master pointer and use this throughout Qt
+            // when making XI2 calls that need a device id (rationale is that
+            // for the time being, most setups will only have one master
+            // pointer (despite having multiple slaves)
+            int deviceCount = 0;
+            XIDeviceInfo *devices = XIQueryDevice((Display *)m_xlib_display, XIAllMasterDevices, &deviceCount);
+            if (devices) {
+                for (int i = 0; i < deviceCount; ++i) {
+                    if (devices[i].use == XIMasterPointer) {
+                        int unused = 0;
+                        m_xinputData->xideviceinfo = XIQueryDevice((Display *)m_xlib_display, devices[i].deviceid, &unused);
+                        break;
+                    }
+                }
+                XIFreeDeviceInfo(devices);
+            }
+            if (!m_xinputData->xideviceinfo)
+                qFatal("Qt: Internal error, no XI2 master pointer found.");
+
+            // find the button info
+            m_xinputData->xibuttonclassinfo = 0;
+            for (int i = 0; i < m_xinputData->xideviceinfo->num_classes; ++i) {
+                if (m_xinputData->xideviceinfo->classes[i]->type == XIButtonClass) {
+                    m_xinputData->xibuttonclassinfo = (XIButtonClassInfo *) m_xinputData->xideviceinfo->classes[i];
+                    break;
+                }
+            }
+
+            // find the "Max Contacts" property on the device
+            Atom typeReturn;
+            int formatReturn;
+            ulong countReturn, bytesReturn;
+            uchar *data = 0;
+            if (XIGetProperty((Display *)m_xlib_display,
+                              m_xinputData->xibuttonclassinfo->sourceid,
+                              atom(QXcbAtom::MaxContacts),
+                              0, 1,
+                              False,
+                              XA_INTEGER,
+                              &typeReturn,
+                              &formatReturn,
+                              &countReturn,
+                              &bytesReturn,
+                              &data) == Success
+                && data != 0
+                && typeReturn == XA_INTEGER
+                && formatReturn == 8
+                && countReturn == 1) {
+                // touch driver reported the max number of touch-points
+                m_xinputData->xiMaxContacts = data[0];
+            } else {
+                m_xinputData->xiMaxContacts = 0;
+            }
+            if (data)
+                XFree(data);
+            XFlush((Display *)m_xlib_display);
+        }
+    }
+}
+
+void QXcbConnection::finalizeXInput2()
+{
+    if (m_xinputData && m_xinputData->xideviceinfo) {
+        XIFreeDeviceInfo(m_xinputData->xideviceinfo);
+    }
+    delete m_xinputData;
+}
+
+// Borrowed from libXi.
+static int count_bits(unsigned char* ptr, int len)
+{
+    int bits = 0;
+    int i;
+    unsigned char x;
+
+    for (i = 0; i < len; i++)
+    {
+        x = ptr[i];
+        while (x > 0)
+        {
+            bits += (x & 0x1);
+            x >>= 1;
+        }
+    }
+    return bits;
+}
+
+static bool getValuatorValueIfSet(xXIDeviceEvent *xideviceevent, int valuatorNum, double *value)
+{
+    unsigned char *buttonsMaskAddr = (unsigned char*)&xideviceevent[1];
+    unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4;
+    FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4);
+    int numValuatorValues = count_bits(valuatorsMaskAddr, xideviceevent->valuators_len * 4);
+    // This relies on all bit being set until a certain number i.e. it doesn't support only bit 0 and 5 being set in the mask.
+    // Just like the original code, works for now.
+    if (valuatorNum >= numValuatorValues)
+        return false;
+    *value = valuatorsValuesAddr[valuatorNum].integral;
+    *value += ((double)valuatorsValuesAddr[valuatorNum].frac / (1 << 16) / (1 << 16));
+    return true;
+}
+
+void QXcbConnection::handleGenericEvent(xcb_ge_event_t *event)
+{
+    // xGenericEvent has "extension" on the second byte, xcb_ge_event_t has "pad0".
+    if (m_xinputData->use_xinput && event->pad0 == m_xinputData->xinput_opcode) {
+        // xcb event structs contain stuff that wasn't on the wire, the full_sequence field
+        // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
+        // Move this data back to have the same layout in memory as it was on the wire
+        // and allow casting, overwritting the full_sequence field.
+        memmove((char*)event + 32, (char*)event + 36, event->length * 4);
+        xXIGenericDeviceEvent* xievent = (xXIGenericDeviceEvent*)event;
+
+        // On Harmattan XInput2 is hacked to give touch points updates into standard mouse button press/motion events.
+        if (m_xinputData->xiMaxContacts != 0
+            && (xievent->evtype == XI_ButtonPress
+                || xievent->evtype == XI_ButtonRelease
+                || xievent->evtype == XI_Motion)) {
+            xXIDeviceEvent *xideviceevent = (xXIDeviceEvent *)xievent;
+            QList<QWindowSystemInterface::TouchPoint> touchPoints = m_xinputData->allTouchPoints;
+            if (touchPoints.count() != m_xinputData->xiMaxContacts) {
+                // initial event, allocate space for all (potential) touch points
+                touchPoints.reserve(m_xinputData->xiMaxContacts);
+                for (int i = 0; i < m_xinputData->xiMaxContacts; ++i) {
+                    QWindowSystemInterface::TouchPoint tp;
+                    tp.id = i;
+                    tp.isPrimary = (i == 0);
+                    tp.state = Qt::TouchPointReleased;
+                    touchPoints << tp;
+                }
+            }
+            qreal x, y, nx, ny, w = 0.0, h = 0.0, p = -1.0;
+            int id;
+            uint active = 0;
+            for (int i = 0; i < m_xinputData->xideviceinfo->num_classes; ++i) {
+                XIAnyClassInfo *classinfo = m_xinputData->xideviceinfo->classes[i];
+                if (classinfo->type == XIValuatorClass) {
+                    XIValuatorClassInfo *valuatorclassinfo = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
+                    int n = valuatorclassinfo->number;
+                    double value;
+                    if (!getValuatorValueIfSet(xideviceevent, n, &value))
+                        continue;
+
+                    if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTPositionX)) {
+                        x = value;
+                        nx = (x - valuatorclassinfo->min) / (valuatorclassinfo->max - valuatorclassinfo->min);
+                    } else if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTPositionY)) {
+                        y = value;
+                        ny = (y - valuatorclassinfo->min) / (valuatorclassinfo->max - valuatorclassinfo->min);
+                    } else if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTTouchMajor)) {
+                        w = value;
+                    } else if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTTouchMinor)) {
+                        h = value;
+                    } else if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTPressure)) {
+                        p = (value - valuatorclassinfo->min) / (valuatorclassinfo->max - valuatorclassinfo->min);
+                    } else if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTTrackingID)) {
+                        id = value;
+                        active |= 1 << id;
+                        QWindowSystemInterface::TouchPoint &touchPoint = touchPoints[id];
+
+                        Qt::TouchPointState newstate;
+                        if (touchPoint.state == Qt::TouchPointReleased) {
+                            newstate = Qt::TouchPointPressed;
+                        } else {
+                            if (touchPoint.area.center() != QPoint(x, y))
+                                newstate = Qt::TouchPointMoved;
+                            else
+                                newstate = Qt::TouchPointStationary;
+                        }
+
+                        touchPoint.state = newstate;
+                        touchPoint.area = QRectF(x - w/2, y - h/2, w, h);
+                        touchPoint.normalPosition = QPointF(nx, ny);
+                        touchPoint.pressure = p;
+                    }
+                }
+            }
+
+            // mark previously-active-but-now-inactive touch points as released
+            for (int i = 0; i < touchPoints.count(); ++i)
+                if (!(active & (1 << i)) && touchPoints.at(i).state != Qt::TouchPointReleased)
+                    touchPoints[i].state = Qt::TouchPointReleased;
+
+            if (xideviceevent->evtype == XI_ButtonRelease) {
+                // final event, forget touch state
+                m_xinputData->allTouchPoints.clear();
+            } else {
+                // save current state so that we have something to reuse later
+                m_xinputData->allTouchPoints = touchPoints;
+            }
+
+            if (QXcbWindow *platformWindow = platformWindowFromId(xideviceevent->event))
+                QWindowSystemInterface::handleTouchEvent(platformWindow->window(), xideviceevent->time, (QEvent::Type)0 /*None*/, QTouchEvent::TouchScreen, m_xinputData->allTouchPoints);
+        }
+    }
+}
+
+#endif // XCB_USE_XINPUT2_MAEMO
+
+
index 8c2b2d5..3b379c3 100644 (file)
 #include <X11/Xutil.h>
 #endif
 
+#ifdef XCB_USE_XINPUT2_MAEMO
+#include <X11/extensions/XInput2.h>
+#endif
+
 #if defined(XCB_USE_GLX)
 #include "qglxintegration.h"
 #include <QtPlatformSupport/private/qglxconvenience_p.h>
@@ -322,6 +326,23 @@ void QXcbWindow::create()
                                    atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32,
                                    1, &leader));
 
+#ifdef XCB_USE_XINPUT2_MAEMO
+    if (connection()->isUsingXInput2()) {
+        XIEventMask xieventmask;
+        uchar bitmask[2] = { 0, 0 };
+
+        xieventmask.deviceid = XIAllMasterDevices;
+        xieventmask.mask = bitmask;
+        xieventmask.mask_len = sizeof(bitmask);
+
+        XISetMask(bitmask, XI_ButtonPress);
+        XISetMask(bitmask, XI_ButtonRelease);
+        XISetMask(bitmask, XI_Motion);
+
+        XISelectEvents(DISPLAY_FROM_XCB(this), m_window, &xieventmask, 1);
+    }
+#endif
+
     setWindowFlags(window()->windowFlags());
     setWindowTitle(window()->windowTitle());
     setWindowState(window()->windowState());
index 13fe5b9..2498581 100644 (file)
@@ -45,6 +45,13 @@ contains(QT_CONFIG, xcb-poll-for-queued-event) {
 contains(QT_CONFIG, xcb-xlib) {
     DEFINES += XCB_USE_XLIB
     LIBS += -lX11 -lX11-xcb
+
+    linux-g++-maemo:contains(QT_CONFIG, xinput2) {
+        # XInput2 support for Harmattan.
+        DEFINES += XCB_USE_XINPUT2_MAEMO
+        SOURCES += qxcbconnection_maemo.cpp
+        LIBS += -lXi
+    }
 }
 
 # to support custom cursors with depth > 1