From 4521e2ec61c6eddd6d4a8454f84a05d9a3195f4b Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Fri, 14 Oct 2011 15:55:35 +0200 Subject: Add multipoint touch support for Harmattan to the xcb platform plugin. 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 --- config.tests/x11/xinput2/xinput2.cpp | 75 ++++++ config.tests/x11/xinput2/xinput2.pro | 4 + configure | 55 +++- src/plugins/platforms/xcb/qxcbconnection.cpp | 31 +++ src/plugins/platforms/xcb/qxcbconnection.h | 31 +++ src/plugins/platforms/xcb/qxcbconnection_maemo.cpp | 294 +++++++++++++++++++++ src/plugins/platforms/xcb/qxcbwindow.cpp | 21 ++ src/plugins/platforms/xcb/xcb.pro | 7 + 8 files changed, 517 insertions(+), 1 deletion(-) create mode 100644 config.tests/x11/xinput2/xinput2.cpp create mode 100644 config.tests/x11/xinput2/xinput2.pro create mode 100644 src/plugins/platforms/xcb/qxcbconnection_maemo.cpp diff --git a/config.tests/x11/xinput2/xinput2.cpp b/config.tests/x11/xinput2/xinput2.cpp new file mode 100644 index 0000000000..14a51f4d28 --- /dev/null +++ b/config.tests/x11/xinput2/xinput2.cpp @@ -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 +#include +#include + +#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 index 0000000000..ae8819b3d1 --- /dev/null +++ b/config.tests/x11/xinput2/xinput2.pro @@ -0,0 +1,4 @@ +CONFIG += x11 +CONFIG -= qt +LIBS += -lXi +SOURCES = xinput2.cpp diff --git a/configure b/configure index 708d7404e9..e985f52f93 100755 --- 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 <] [-prefix-install] [-bindir ] [-libdir ] @@ -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" diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index af5822f7f7..bdf308adb3 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -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) diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 769b2e79f8..5887e57968 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -51,6 +51,10 @@ #include #include +#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); } @@ -334,6 +357,11 @@ private: void initializeXRender(); #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); @@ -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 index 0000000000..d0529202b9 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbconnection_maemo.cpp @@ -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 +#include +#include +#include + +// 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 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 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(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 + + diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 8c2b2d53fd..3b379c3caa 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -93,6 +93,10 @@ #include #endif +#ifdef XCB_USE_XINPUT2_MAEMO +#include +#endif + #if defined(XCB_USE_GLX) #include "qglxintegration.h" #include @@ -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()); diff --git a/src/plugins/platforms/xcb/xcb.pro b/src/plugins/platforms/xcb/xcb.pro index 13fe5b900c..2498581eb7 100644 --- a/src/plugins/platforms/xcb/xcb.pro +++ b/src/plugins/platforms/xcb/xcb.pro @@ -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 -- cgit v1.2.3