Search
SailfishOS Open Build Service
>
Projects
>
home:kaltsi
:
tnhlbug
>
qt
> 0007-xinput2-support.patch
Log In
Username
Password
Cancel
Overview
Repositories
Revisions
Requests
Users
Advanced
Attributes
Meta
File 0007-xinput2-support.patch of Package qt
From 60f4497845a6e034395444d620409b574349a316 Mon Sep 17 00:00:00 2001 From: Robin Burchell <robin.burchell@collabora.com> Date: Sun, 13 Nov 2011 12:36:01 +0100 Subject: [PATCH 07/29] xinput2 support --- config.tests/x11/xinput2/xinput2.cpp | 75 ++ config.tests/x11/xinput2/xinput2.pro | 4 + configure | 48 +- src/gui/kernel/kernel.pri | 3 +- src/gui/kernel/qapplication_p.h | 4 + src/gui/kernel/qapplication_x11.cpp | 1386 ++++++++++++++++++++++++++++------ src/gui/kernel/qcursor_x11.cpp | 145 +++- src/gui/kernel/qt_x11_p.h | 49 +- src/gui/kernel/qwidget.cpp | 2 +- src/gui/kernel/qwidget_p.h | 9 +- src/gui/kernel/qwidget_x11.cpp | 178 +++-- src/gui/kernel/qx11embed_x11.cpp | 54 +- 12 files changed, 1620 insertions(+), 337 deletions(-) create mode 100644 config.tests/x11/xinput2/xinput2.cpp create mode 100644 config.tests/x11/xinput2/xinput2.pro diff --git a/config.tests/x11/xinput2/xinput2.cpp b/config.tests/x11/xinput2/xinput2.cpp new file mode 100644 index 0000000..583aa3b --- /dev/null +++ b/config.tests/x11/xinput2/xinput2.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $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 index 0000000..ae8819b --- /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 25be8c6..9e47eeb 100755 --- a/configure +++ b/configure @@ -827,6 +827,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_NIS=auto @@ -1125,7 +1126,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|-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|-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|-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|-declarative|-declarative-debug|-javascript-jit|-script|-scripttools|-rpath|-force-pkg-config|-s60|-usedeffiles|-icu) VAR=`echo $1 | sed "s,^-\(.*\),\1,"` VAL=yes ;; @@ -1816,6 +1817,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" @@ -4252,6 +4260,13 @@ if [ "$PLATFORM_X11" = "yes" ]; then XMY="*" XMN=" " fi + if [ "$CFG_XINPUT2" = "no" ]; then + X2Y=" " + X2N="*" + else + X2Y="*" + X2N=" " + fi if [ "$CFG_XINPUT" = "no" ]; then XIY=" " XIN="*" @@ -4356,7 +4371,10 @@ Qt/X11 only: Requires fontconfig/fontconfig.h, libfontconfig, freetype.h and libfreetype. - $XIN -no-xinput ......... Do not compile Xinput support. + $X2N -no-xinput2......... Do not compile XInput2 support. + $X2Y -xinput2............ Compile XInput2 support. + + $XIN -no-xinput.......... Do not compile Xinput support. $XIY -xinput ............ Compile Xinput support. This also enabled tablet support which requires IRIX with wacom.h and libXi or XFree86 with X11/extensions/XInput.h and libXi. @@ -6238,7 +6256,23 @@ if [ "$PLATFORM_X11" = "yes" ]; then fi fi - # auto-detect Xinput support + # 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 if [ "$CFG_XINPUT" != "no" ]; then if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/x11/xinput "XInput" $L_FLAGS $I_FLAGS $l_FLAGS $X11TESTS_FLAGS; then if [ "$CFG_XINPUT" != "runtime" ]; then @@ -7462,9 +7496,12 @@ if [ "$PLATFORM_X11" = "yes" ]; then if [ "$CFG_FONTCONFIG" = "yes" ]; then QT_CONFIG="$QT_CONFIG fontconfig" fi - if [ "$CFG_XINPUT" = "yes" ]; then + if [ "$CFG_XINPUT2" = "yes" -o "$CFG_XINPUT" = "yes" ]; then QMakeVar set QMAKE_LIBS_X11 '-lXi $$QMAKE_LIBS_X11' fi + if [ "$CFG_XINPUT2" = "yes" ]; then + QT_CONFIG="$QT_CONFIG xinput2" + fi if [ "$CFG_XINPUT" = "yes" ]; then QT_CONFIG="$QT_CONFIG xinput tablet" fi @@ -8349,6 +8386,7 @@ fi [ "$CFG_ICONV" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_ICONV" [ "$CFG_GLIB" != "yes" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_GLIB" [ "$CFG_QGTKSTYLE" != "yes" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_STYLE_GTK" + if [ "$XPLATFORM_SYMBIAN" = "no" ]; then # Do not apply following negative X11/Unix/Mac only flags on Symbian, so that # configuration matches with the one generated by configure executable tool @@ -8378,6 +8416,7 @@ if [ "$XPLATFORM_SYMBIAN" = "no" ]; then [ "$CFG_XVIDEO" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_XVIDEO" [ "$CFG_XSYNC" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_XSYNC" [ "$CFG_XINPUT" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_XINPUT QT_NO_TABLET" + [ "$CFG_XINPUT2" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_XINPUT2" [ "$CFG_XCURSOR" = "runtime" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_RUNTIME_XCURSOR" [ "$CFG_XINERAMA" = "runtime" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_RUNTIME_XINERAMA" @@ -8947,6 +8986,7 @@ if [ "$PLATFORM_X11" = "yes" ]; then echo "Xfixes support ......... $CFG_XFIXES" echo "Xrandr support ......... $CFG_XRANDR" echo "Xrender support ........ $CFG_XRENDER" + echo "XInput2 support ........ $CFG_XINPUT2" echo "Xi support ............. $CFG_XINPUT" echo "MIT-SHM support ........ $CFG_MITSHM" echo "FontConfig support ..... $CFG_FONTCONFIG" diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index d9d67e7..4aef157 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -140,9 +140,10 @@ symbian { unix:x11 { INCLUDEPATH += ../3rdparty/xorg HEADERS += \ + kernel/qt_x11_p.h \ kernel/qx11embed_x11.h \ kernel/qx11info_x11.h \ - kernel/qkde_p.h + kernel/qkde_p.h SOURCES += \ kernel/qapplication_x11.cpp \ diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index cfaae50..e0047ff 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -636,6 +636,10 @@ public: bool useTranslucentEGLSurfaces; #endif +#if defined(Q_WS_X11) && !defined(QT_NO_XINPUT2) + QList<QTouchEvent::TouchPoint> appAllTouchPoints; +#endif + private: #ifdef Q_WS_QWS QMap<const QScreen*, QRect> maxWindowRects; diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp index 22f7e19..90aad3f 100644 --- a/src/gui/kernel/qapplication_x11.cpp +++ b/src/gui/kernel/qapplication_x11.cpp @@ -3,6 +3,9 @@ ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** +** Code contributions to adapt XInput2.0 portions to support multiple +** connected input devices are Copyright (C) 2011 Intel Corporation +** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ @@ -125,9 +128,6 @@ extern "C" { #define XK_MISCELLANY #include <X11/keysymdef.h> -#if !defined(QT_NO_XINPUT) -#include <X11/extensions/XI.h> -#endif #include <stdlib.h> #include <string.h> @@ -326,6 +326,23 @@ static const char * x11_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" }; Q_GUI_EXPORT QX11Data *qt_x11Data = 0; @@ -373,7 +390,6 @@ static Window mouseActWindow = 0; // window where mous static Qt::MouseButton mouseButtonPressed = Qt::NoButton; // last mouse button pressed static Qt::MouseButtons mouseButtonState = Qt::NoButton; // mouse button state static Time mouseButtonPressTime = 0; // when was a button pressed -static short mouseXPos, mouseYPos; // mouse pres position in act window static short mouseGlobalXPos, mouseGlobalYPos; // global mouse press position extern QWidgetList *qt_modal_stack; // stack of modal widgets @@ -565,6 +581,15 @@ class QETWidget : public QWidget // event translator widget public: QWidgetPrivate* d_func() { return QWidget::d_func(); } bool translateMouseEvent(const XEvent *); + bool sendMouseEvent(int xtype, + Window window, + QEvent::Type type, + QPoint pos, + QPoint globalPos, + Qt::MouseButton button, + Qt::MouseButtons buttons, + Qt::KeyboardModifiers modifiers); + void translatePaintEvent(const XEvent *); bool translateConfigEvent(const XEvent *); bool translateCloseEvent(const XEvent *); @@ -576,6 +601,10 @@ public: #endif bool translatePropertyEvent(const XEvent *); +#if !defined(QT_NO_XINPUT2) + bool translateXI2Event(const XIEvent *); +#endif + void doDeferredMap() { Q_ASSERT(testAttribute(Qt::WA_WState_Created)); @@ -697,7 +726,7 @@ static int qt_x_errhandler(Display *dpy, XErrorEvent *err) default: #if !defined(QT_NO_XINPUT) - if (err->request_code == X11->xinput_major + if (err->request_code == X11->xinput_opcode && err->error_code == (X11->xinput_errorbase + XI_BadDevice) && err->minor_code == 3 /* X_OpenDevice */) { return 0; @@ -728,7 +757,7 @@ static int qt_x_errhandler(Display *dpy, XErrorEvent *err) extensionName = "RENDER"; else if (err->request_code == X11->xrandr_major) extensionName = "RANDR"; - else if (err->request_code == X11->xinput_major) + else if (err->request_code == X11->xinput_opcode) extensionName = "XInputExtension"; else if (err->request_code == X11->mitshm_major) extensionName = "MIT-SHM"; @@ -1629,6 +1658,116 @@ static void getXDefault(const char *group, const char *key, bool *val) } #endif +#if !defined(QT_NO_XINPUT2) +// xiFindActiveDevice +// Used to determine if an input event's reported device Id is for +// either the Master core pointer or one of the attached Touch +// devices +int xiFindActiveDevice(int deviceId) +{ + if (!X11->xiDeviceInfo) + return -1; + + foreach (int id, X11->xiActiveDevices) { + if (id == deviceId) { + for (int i = 0; i < X11->xiDeviceCount; i++) + if (X11->xiDeviceInfo[i].deviceid == deviceId) + return i; + } + } + + return -1; +} + +// xiFreeDevices +// Helper function to clean up a few duplications of code +void xiFreeDevices() +{ + if (X11->xiDeviceInfo) { + X11->xiIsTouch.clear(); + X11->xibuttonclassinfo = 0; + XIFreeDeviceInfo(X11->xiDeviceInfo); + } +} + +// xiEnumerateDevices +// Query X for the full set of all input devices and attach to +// the first Virtual Core Pointer found, recording it as the +// xiMasterDeviceId Additionally, attach to any Touch enabled device +// that is either a Slave of that Core Pointer or is Floating +void xiEnumerateDevices() +{ + xiFreeDevices(); + + XIDeviceInfo *info; + X11->xiDeviceInfo = XIQueryDevice(QX11Info::display(), XIAllDevices, &X11->xiDeviceCount); + X11->xiIsTouch.reserve(X11->xiDeviceCount); + + // We connect to only the first Master Pointer for Touch and Pointer events. + // We connect to any Floating Slave pointers for Touch + X11->xiMasterIndex = -1; + X11->xiMasterDeviceId = -1; + bool xiDebug = false; + if (qgetenv("QT_XINPUT_DEBUG") == "1") + xiDebug = true; + + for (int i = 0; i < X11->xiDeviceCount; i++) { + info = &X11->xiDeviceInfo[i]; + X11->xiIsTouch << false; + +#define XI_DEBUG if (xiDebug) qDebug() + + // We will only take the first slave pointers from the first master pointer we find; we do not + if (info->use == XIMasterPointer && X11->xiMasterIndex != -1) { + XI_DEBUG << "Skipping additional Master Pointer:" << info->name << "(" << info->deviceid << ")"; + continue; + } + + if (info->use == XIMasterPointer) { + X11->xiMasterIndex = i; + X11->xiMasterDeviceId = info->deviceid; + X11->xiIsTouch[i] = false; + XI_DEBUG << "Adding Master Pointer:" << info->name << "(" << info->deviceid << ")"; + X11->xiActiveDevices.append(info->deviceid); + for (int j = 0; j < info->num_classes; ++j) { + if (info->classes[j]->type == XIButtonClass) { + X11->xibuttonclassinfo = (XIButtonClassInfo *) info->classes[j]; + break; + } + } + continue; + } + + if (info->use == XIFloatingSlave || info->use == XISlavePointer) { + if (info->use == XISlavePointer && info->attachment != X11->xiMasterDeviceId) { + XI_DEBUG << "Skipping Slave on non-Master:" << info->name << "(" << info->deviceid << ")"; + continue; + } + + for (int j = 0; j < info->num_classes; j++) { + if (info->classes[j]->type == XIValuatorClass) { + XIValuatorClassInfo *valuator = (XIValuatorClassInfo *)(info->classes[j]); + // If this device doesn't support the AbsMTTrackingID then we can't use it as a touch device + if (valuator->label == ATOM(AbsMTTrackingID)) { + X11->xiIsTouch[i] = true; + break; + } + } + } + + if (X11->xiIsTouch[i]) { + XI_DEBUG << "Adding" << (info->use == XIFloatingSlave ? "FLOATING" : "ATTACHED") << "touch device:" << info->name << "(" << info->deviceid << ")"; + X11->xiActiveDevices.append(info->deviceid); + } else { + XI_DEBUG << "Skipping non-Touch device:" << info->name << "(" << info->deviceid << ")"; + } + } + } +} + +#endif + + // ### This should be static but it isn't because of the friend declaration // ### in qpaintdevice.h which then should have a static too but can't have // ### it because "storage class specifiers invalid in friend function @@ -1661,9 +1800,14 @@ void qt_init(QApplicationPrivate *priv, int, // XInputExtension X11->use_xinput = false; - X11->xinput_major = 0; + X11->xinput_opcode = 0; X11->xinput_eventbase = 0; X11->xinput_errorbase = 0; +#if !defined(QT_NO_XINPUT2) + X11->xiDeviceInfo = 0; + X11->xiDeviceCount = 0; + X11->xiMasterIndex = 0; +#endif X11->use_xkb = false; X11->xkb_major = 0; @@ -2142,14 +2286,40 @@ void qt_init(QApplicationPrivate *priv, int, #endif // QT_RUNTIME_XINERAMA #endif // QT_NO_XINERAMA -#ifndef QT_NO_XINPUT +#if !defined(QT_NO_XINPUT2) + X11->use_xinput = XQueryExtension(X11->display, "XInputExtension", &X11->xinput_opcode, + &X11->xinput_eventbase, &X11->xinput_errorbase); + if (X11->use_xinput) { + // we want XInput2 + int ximajor = 2, ximinor = 0; + if (XIQueryVersion(X11->display, &ximajor, &ximinor) == BadRequest) { + // XInput2 not available + X11->use_xinput = false; + } else { + // XInput2 available, select for XI_HierarchyChanged events. we use these + // events to make sure that we follow device capability changes + XIEventMask xieventmask; + uchar bitmask[2] = { 0, 0 }; + XISetMask(bitmask, XI_DeviceChanged); + XISetMask(bitmask, XI_HierarchyChanged); + + xieventmask.deviceid = XIAllDevices; + xieventmask.mask = bitmask; + xieventmask.mask_len = sizeof(bitmask); + + XISelectEvents(X11->display, DefaultRootWindow(X11->display), &xieventmask, 1); + + xiEnumerateDevices(); + } + } +#elif !defined(QT_NO_XINPUT) // See if Xinput is supported on the connected display X11->ptrXCloseDevice = 0; X11->ptrXListInputDevices = 0; X11->ptrXOpenDevice = 0; X11->ptrXFreeDeviceList = 0; X11->ptrXSelectExtensionEvent = 0; - X11->use_xinput = XQueryExtension(X11->display, "XInputExtension", &X11->xinput_major, + X11->use_xinput = XQueryExtension(X11->display, "XInputExtension", &X11->xinput_opcode, &X11->xinput_eventbase, &X11->xinput_errorbase); if (X11->use_xinput) { X11->ptrXCloseDevice = XINPUT_LOAD(XCloseDevice); @@ -2668,6 +2838,10 @@ void qt_cleanup() qt_save_rootinfo(); if (qt_is_gui_used) { +#if !defined(QT_NO_XINPUT2) + xiFreeDevices(); +#endif + QPixmapCache::clear(); QCursorData::cleanup(); QFont::cleanup(); @@ -3131,6 +3305,39 @@ static QETWidget *qPRFindWidget(Window oldwin) return wPRmapper ? (QETWidget*)wPRmapper->value((int)oldwin, 0) : 0; } +// XEvent processing + +#if !defined(QT_NO_XINPUT2) + +extern "C" { + + static Bool qt_XI_Wheel_scanner(Display *display, XEvent *event, XPointer arg) + { + XIDeviceEvent *originalevent = (XIDeviceEvent *) arg; + bool returnValue = false; + if (event->type == GenericEvent + && event->xcookie.extension == X11->xinput_opcode + && event->xcookie.evtype == XI_ButtonPress) { + XGetEventData(display, &event->xcookie); + XIDeviceEvent *deviceevent = (XIDeviceEvent *) event->xcookie.data; + // compress this event if it's the same button on the same window + if ((returnValue = (deviceevent->event == originalevent->event + && deviceevent->detail == originalevent->detail))) { + // do *NOT* use XFreeEventData() when we have not compressed + // the event, as it will remove the cookie data completely from + // the event. Instead we let x11ProcessEvents() call + // XFreeEventData() once this event has been pulled of the + // queue for normal processing. + XFreeEventData(display, &event->xcookie); + } + } + return returnValue; + } + +} + +#endif + int QApplication::x11ClientMessage(QWidget* w, XEvent* event, bool passive_only) { if (w && !w->internalWinId()) @@ -3256,7 +3463,74 @@ int QApplication::x11ProcessEvent(XEvent* event) } #endif - QETWidget *widget = (QETWidget*)QWidget::find((WId)event->xany.window); + QETWidget *widget = 0; +#if !defined(QT_NO_XINPUT2) + bool isXI2Event = false; + if (event->type == GenericEvent) { + // event->xany.window is not usable for these events + if (X11->use_xinput + && XGetEventData(X11->display, &event->xcookie) + && event->xcookie.extension == X11->xinput_opcode) { + // remember for later + isXI2Event = true; + + // look up widget based on the type of event we received + switch (event->xcookie.evtype) { + case XI_DeviceChanged: + { + XIDeviceChangedEvent *xidevicechangedevent = (XIDeviceChangedEvent *) event->xcookie.data; + if (xidevicechangedevent->reason == XISlaveSwitch) { + qDebug("XISlaveSwitch"); + xiEnumerateDevices(); + } else { + // ### TODO: handle this type of event + qDebug("XI_DeviceChanged, id %d, source %d, reason %d", + xidevicechangedevent->deviceid, + xidevicechangedevent->sourceid, + xidevicechangedevent->reason); + } + break; + } + case XI_HierarchyChanged: + // ### TODO: handle this type of event + qDebug("XI_HierarchyChanged"); + xiEnumerateDevices(); + break; + case XI_ButtonPress: + case XI_ButtonRelease: + case XI_Motion: + // all of these events send XIDeviceEvents + widget = (QETWidget *) QWidget::find(((XIDeviceEvent *) event->xcookie.data)->event); + break; + case XI_Enter: + case XI_Leave: + // all of these events send XIEnterEvents + widget = (QETWidget *) QWidget::find(((XIEnterEvent *) event->xcookie.data)->event); + break; + } + } + } else +#endif + { + widget = (QETWidget*)QWidget::find((WId)event->xany.window); + } + +#if !defined(QT_NO_XINPUT2) + // make sure XFreeEventData() is called at every return point + class CallXFreeEventData + { + Display *display; + XGenericEventCookie *cookie; + public: + CallXFreeEventData(Display *display, XGenericEventCookie *cookie) + : display(display), cookie(cookie) + { } + ~CallXFreeEventData() + { + XFreeEventData(display, cookie); + } + } instance(X11->display, &event->xcookie); +#endif if (wPRmapper) { // just did a widget reparent? if (widget == 0) { // not in std widget mapper @@ -3268,6 +3542,17 @@ int QApplication::x11ProcessEvent(XEvent* event) case XKeyRelease: widget = qPRFindWidget(event->xany.window); break; +#if !defined(QT_NO_XINPUT2) + case GenericEvent: + // as above, event->xany.window is unusable for these events + if (X11->use_xinput) { + if (event->xcookie.extension != X11->xinput_opcode) + break; + widget = qPRFindWidget(((XIDeviceEvent *) event->xcookie.data)->event); + break; + } + break; +#endif } } else if (widget->testAttribute(Qt::WA_WState_Reparented)) @@ -3387,6 +3672,19 @@ int QApplication::x11ProcessEvent(XEvent* event) case ButtonRelease: case XKeyPress: case XKeyRelease: +#if !defined(QT_NO_XINPUT2) + case GenericEvent: + if (X11->use_xinput) { + if (event->xcookie.extension != X11->xinput_opcode + || (event->xcookie.evtype != XI_ButtonPress + && event->xcookie.evtype != XI_ButtonRelease)) + break; + // Button press/release from XI2, allow event to fallthrough + } else { + break; + } + // fallthrough intended +#endif do { popup->close(); } while ((popup = qApp->activePopupWidget())); @@ -3472,21 +3770,64 @@ int QApplication::x11ProcessEvent(XEvent* event) } #endif // QT_NO_XFIXES - switch (event->type) { + int inputEventType = event->type; +#if !defined(QT_NO_XINPUT2) + XIDeviceEvent *xievent = 0; + if (isXI2Event) { + xievent = (XIDeviceEvent *) event->xcookie.data; + X11->time = xievent->time; + + switch (xievent->evtype) { + break; + case XI_ButtonPress: + pressed_window = xievent->event; + inputEventType = ButtonPress; + break; + case XI_ButtonRelease: + inputEventType = ButtonRelease; + break; + case XI_Motion: + inputEventType = MotionNotify; + break; + case XI_Enter: + inputEventType = EnterNotify; + break; + case XI_Leave: + inputEventType = LeaveNotify; + break; + case XI_DeviceChanged: + // unreachable, should already be handled above (allow fallthrough) + default: + qWarning("Qt: Unknown XI2 event %d", xievent->evtype); + // must return, don't know that this event can be cast to XIDeviceEvent + return 0; + } + } +#endif + bool handled = true; + switch (inputEventType) { case ButtonRelease: // mouse event - if (!d->inPopupMode() && !QWidget::mouseGrabber() && pressed_window != widget->internalWinId() + if (!d->inPopupMode() + && !QWidget::mouseGrabber() + && pressed_window != widget->internalWinId() && (widget = (QETWidget*) QWidget::find((WId)pressed_window)) == 0) break; // fall through intended case ButtonPress: - if (event->xbutton.root != RootWindow(X11->display, widget->x11Info().screen()) + if (( +#if !defined(QT_NO_XINPUT2) + isXI2Event + ? xievent->root != RootWindow(X11->display, widget->x11Info().screen()) + : +#endif + event->xbutton.root != RootWindow(X11->display, widget->x11Info().screen())) && ! qt_xdnd_dragging) { while (activePopupWidget()) activePopupWidget()->close(); return 1; } - if (event->type == ButtonPress) + if (inputEventType == ButtonPress) qt_net_update_user_time(widget->window(), X11->userTime); // fall through intended case MotionNotify: @@ -3494,15 +3835,29 @@ int QApplication::x11ProcessEvent(XEvent* event) if (!qt_tabletChokeMouse) { #endif if (widget->testAttribute(Qt::WA_TransparentForMouseEvents)) { - QPoint pos(event->xbutton.x, event->xbutton.y); + QPoint pos = +#if !defined(QT_NO_XINPUT2) + isXI2Event + ? QPoint(xievent->event_x, xievent->event_y) + : +#endif + QPoint(event->xbutton.x, event->xbutton.y); pos = widget->d_func()->mapFromWS(pos); QWidget *window = widget->window(); pos = widget->mapTo(window, pos); if (QWidget *child = window->childAt(pos)) { widget = static_cast<QETWidget *>(child); pos = child->mapFrom(window, pos); - event->xbutton.x = pos.x(); - event->xbutton.y = pos.y(); +#if !defined(QT_NO_XINPUT2) + if (isXI2Event) { + xievent->event_y = pos.y(); + xievent->event_x = pos.x(); + } else +#endif + { + event->xbutton.x = pos.x(); + event->xbutton.y = pos.y(); + } } } widget->translateMouseEvent(event); @@ -3512,29 +3867,197 @@ int QApplication::x11ProcessEvent(XEvent* event) } #endif break; + case EnterNotify: + { // enter window + if (QWidget::mouseGrabber() && (!d->inPopupMode() || widget->window() != activePopupWidget())) + break; + int mode; + int detail; + bool focus; + int x, y; +#if !defined(QT_NO_XINPUT2) + if (isXI2Event) { + XIEnterEvent *xienterevent = (XIEnterEvent *) event->xcookie.data; - case XKeyPress: // keyboard event - qt_net_update_user_time(widget->window(), X11->userTime); - // fallthrough intended - case XKeyRelease: - { - if (keywidget && keywidget->isEnabled()) { // should always exist - // qDebug("sending key event"); - qt_keymapper_private()->translateKeyEvent(keywidget, event, grabbed); + if (X11->xiMasterIndex != -1 && + X11->xiMasterDeviceId == xienterevent->deviceid) + break; + + mode = xienterevent->mode; + detail = xienterevent->detail; + focus = xienterevent->focus; + x = xienterevent->event_x; + y = xienterevent->event_y; + } else +#endif + { + mode = event->xcrossing.mode; + detail = event->xcrossing.detail; + focus = event->xcrossing.focus; + x = event->xcrossing.x; + y = event->xcrossing.y; + } + + if ((mode != NotifyNormal + && mode != NotifyUngrab) + || detail == NotifyVirtual + || detail == NotifyNonlinearVirtual) + break; + if (focus && + !(widget->windowType() == Qt::Desktop) && !widget->isActiveWindow()) { + if (X11->focus_model == QX11Data::FM_Unknown) // check focus model + qt_check_focus_model(); + if (X11->focus_model == QX11Data::FM_PointerRoot) // PointerRoot mode + setActiveWindow(widget); } + + if (qt_button_down && !d->inPopupMode()) + break; + + QWidget *alien = widget->childAt(widget->d_func()->mapFromWS(QPoint(x, y))); + QWidget *enter = alien ? alien : widget; + QWidget *leave = 0; + if (qt_last_mouse_receiver && !qt_last_mouse_receiver->internalWinId()) + leave = qt_last_mouse_receiver; + else + leave = QWidget::find(curWin); + + // ### Alien: enter/leave might be wrong here with overlapping siblings + // if the enter widget is native and stacked under a non-native widget. + QApplicationPrivate::dispatchEnterLeave(enter, leave); + curWin = widget->internalWinId(); + qt_last_mouse_receiver = enter; + if (!d->inPopupMode() || widget->window() == activePopupWidget()) + widget->translateMouseEvent(event); //we don't get MotionNotify, emulate it break; } + case LeaveNotify: + { // leave window + QWidget *mouseGrabber = QWidget::mouseGrabber(); + if (mouseGrabber && !d->inPopupMode()) + break; + if (curWin && widget->internalWinId() != curWin) + break; + int mode; + int detail; + bool focus; + int x, y; +#if !defined(QT_NO_XINPUT2) + if (isXI2Event) { + XIEnterEvent *xienterevent = (XIEnterEvent *) event->xcookie.data; - case GraphicsExpose: - case Expose: // paint event - widget->translatePaintEvent(event); - break; + if (X11->xiMasterIndex != -1 && + X11->xiMasterDeviceId == xienterevent->deviceid) + break; - case ConfigureNotify: // window move/resize event - if (event->xconfigure.event == event->xconfigure.window) - widget->translateConfigEvent(event); + mode = xienterevent->mode; + detail = xienterevent->detail; + focus = xienterevent->focus; + x = xienterevent->event_x; + y = xienterevent->event_y; + } else +#endif + { + mode = event->xcrossing.mode; + detail = event->xcrossing.detail; + focus = event->xcrossing.focus; + x = event->xcrossing.x; + y = event->xcrossing.y; + } + + if ((mode != NotifyNormal + && mode != NotifyUngrab) + || detail == NotifyInferior) + break; + if (!(widget->windowType() == Qt::Desktop)) + widget->translateMouseEvent(event); //we don't get MotionNotify, emulate it + + // look for an EnterNotify to match this LeaveNotify + QWidget* enter = 0; + QPoint enterPoint; + XEvent ev; +#if !defined(QT_NO_XINPUT2) + if (isXI2Event) { + // ### TODO: port code below (Leave->Enter matching) to XI2 + } else +#endif + { + while (XCheckMaskEvent(X11->display, EnterWindowMask | LeaveWindowMask , &ev) + && !qt_x11EventFilter(&ev)) { + QWidget* event_widget = QWidget::find(ev.xcrossing.window); + if(event_widget && event_widget->x11Event(&ev)) + break; + if (ev.type == LeaveNotify + || (ev.xcrossing.mode != NotifyNormal + && ev.xcrossing.mode != NotifyUngrab) + || ev.xcrossing.detail == NotifyVirtual + || ev.xcrossing.detail == NotifyNonlinearVirtual) + continue; + enter = event_widget; + if (enter) + enterPoint = enter->d_func()->mapFromWS(QPoint(ev.xcrossing.x, ev.xcrossing.y)); + if (ev.xcrossing.focus && + enter && !(enter->windowType() == Qt::Desktop) && !enter->isActiveWindow()) { + if (X11->focus_model == QX11Data::FM_Unknown) // check focus model + qt_check_focus_model(); + if (X11->focus_model == QX11Data::FM_PointerRoot) // PointerRoot mode + setActiveWindow(enter); + } + break; + } + } + + if ((! enter || (enter->windowType() == Qt::Desktop)) && + focus && widget == QApplicationPrivate::active_window && + X11->focus_model == QX11Data::FM_PointerRoot // PointerRoot mode + ) { + setActiveWindow(0); + } + + if (qt_button_down && !d->inPopupMode()) + break; + + if (!curWin) + QApplicationPrivate::dispatchEnterLeave(widget, 0); + + if (enter) { + QWidget *alienEnter = enter->childAt(enterPoint); + if (alienEnter) + enter = alienEnter; + } + + QWidget *leave = qt_last_mouse_receiver ? qt_last_mouse_receiver : widget; + QWidget *activePopupWidget = qApp->activePopupWidget(); + + if (mouseGrabber && activePopupWidget && leave == activePopupWidget) + enter = mouseGrabber; + else if (enter != widget && mouseGrabber) { + if (!widget->rect().contains(widget->d_func()->mapFromWS(QPoint(x, y)))) + break; + } + + QApplicationPrivate::dispatchEnterLeave(enter, leave); + qt_last_mouse_receiver = enter; + + if (enter && QApplicationPrivate::tryModalHelper(enter, 0)) { + QWidget *nativeEnter = enter->internalWinId() ? enter : enter->nativeParentWidget(); + curWin = nativeEnter->internalWinId(); + static_cast<QETWidget *>(nativeEnter)->translateMouseEvent(&ev); //we don't get MotionNotify, emulate it + } else { + curWin = 0; + qt_last_mouse_receiver = 0; + } + break; + } + + default: + handled = false; break; + } + if (handled) + return 0; + switch (event->type) { case XFocusIn: { // got focus if ((widget->windowType() == Qt::Desktop)) break; @@ -3542,10 +4065,12 @@ int QApplication::x11ProcessEvent(XEvent* event) break; if (!widget->isWindow()) break; + if (event->xfocus.detail != NotifyAncestor && event->xfocus.detail != NotifyInferior && event->xfocus.detail != NotifyNonlinear) break; + setActiveWindow(widget); if (X11->focus_model == QX11Data::FM_PointerRoot) { // We got real input focus from somewhere, but we were in PointerRoot @@ -3556,11 +4081,12 @@ int QApplication::x11ProcessEvent(XEvent* event) } break; - case XFocusOut: // lost focus + case XFocusOut: { // lost focus if ((widget->windowType() == Qt::Desktop)) break; if (!widget->isWindow()) break; + if (event->xfocus.mode == NotifyGrab) { qt_xfocusout_grab_counter++; break; @@ -3569,149 +4095,53 @@ int QApplication::x11ProcessEvent(XEvent* event) event->xfocus.detail != NotifyNonlinearVirtual && event->xfocus.detail != NotifyNonlinear) break; + if (!d->inPopupMode() && widget == QApplicationPrivate::active_window) { XEvent ev; + QWidget *focusInWidget = 0; bool focus_will_change = false; if (XCheckTypedEvent(X11->display, XFocusIn, &ev)) { // we're about to get an XFocusIn, if we know we will // get a new active window, we don't want to set the // active window to 0 now - QWidget *w2 = QWidget::find(ev.xany.window); - if (w2 - && w2->windowType() != Qt::Desktop - && !d->inPopupMode() // some delayed focus event to ignore - && w2->isWindow() - && (ev.xfocus.detail == NotifyAncestor - || ev.xfocus.detail == NotifyInferior - || ev.xfocus.detail == NotifyNonlinear)) - focus_will_change = true; - + focusInWidget = QWidget::find(ev.xany.window); XPutBackEvent(X11->display, &ev); } + + if (focusInWidget + && focusInWidget->windowType() != Qt::Desktop + && !d->inPopupMode() // some delayed focus event to ignore + && focusInWidget->isWindow() + && (ev.xfocus.detail == NotifyAncestor + || ev.xfocus.detail == NotifyInferior + || ev.xfocus.detail == NotifyNonlinear)) + focus_will_change = true; if (!focus_will_change) setActiveWindow(0); } - break; - - case EnterNotify: { // enter window - if (QWidget::mouseGrabber() && (!d->inPopupMode() || widget->window() != activePopupWidget())) - break; - if ((event->xcrossing.mode != NotifyNormal - && event->xcrossing.mode != NotifyUngrab) - || event->xcrossing.detail == NotifyVirtual - || event->xcrossing.detail == NotifyNonlinearVirtual) - break; - if (event->xcrossing.focus && - !(widget->windowType() == Qt::Desktop) && !widget->isActiveWindow()) { - if (X11->focus_model == QX11Data::FM_Unknown) // check focus model - qt_check_focus_model(); - if (X11->focus_model == QX11Data::FM_PointerRoot) // PointerRoot mode - setActiveWindow(widget); - } - - if (qt_button_down && !d->inPopupMode()) - break; - - QWidget *alien = widget->childAt(widget->d_func()->mapFromWS(QPoint(event->xcrossing.x, - event->xcrossing.y))); - QWidget *enter = alien ? alien : widget; - QWidget *leave = 0; - if (qt_last_mouse_receiver && !qt_last_mouse_receiver->internalWinId()) - leave = qt_last_mouse_receiver; - else - leave = QWidget::find(curWin); - - // ### Alien: enter/leave might be wrong here with overlapping siblings - // if the enter widget is native and stacked under a non-native widget. - QApplicationPrivate::dispatchEnterLeave(enter, leave); - curWin = widget->internalWinId(); - qt_last_mouse_receiver = enter; - if (!d->inPopupMode() || widget->window() == activePopupWidget()) - widget->translateMouseEvent(event); //we don't get MotionNotify, emulate it } break; - case LeaveNotify: { // leave window - QWidget *mouseGrabber = QWidget::mouseGrabber(); - if (mouseGrabber && !d->inPopupMode()) - break; - if (curWin && widget->internalWinId() != curWin) - break; - if ((event->xcrossing.mode != NotifyNormal - && event->xcrossing.mode != NotifyUngrab) - || event->xcrossing.detail == NotifyInferior) - break; - if (!(widget->windowType() == Qt::Desktop)) - widget->translateMouseEvent(event); //we don't get MotionNotify, emulate it - QWidget* enter = 0; - QPoint enterPoint; - XEvent ev; - while (XCheckMaskEvent(X11->display, EnterWindowMask | LeaveWindowMask , &ev) - && !qt_x11EventFilter(&ev)) { - QWidget* event_widget = QWidget::find(ev.xcrossing.window); - if(event_widget && event_widget->x11Event(&ev)) - break; - if (ev.type == LeaveNotify - || (ev.xcrossing.mode != NotifyNormal - && ev.xcrossing.mode != NotifyUngrab) - || ev.xcrossing.detail == NotifyVirtual - || ev.xcrossing.detail == NotifyNonlinearVirtual) - continue; - enter = event_widget; - if (enter) - enterPoint = enter->d_func()->mapFromWS(QPoint(ev.xcrossing.x, ev.xcrossing.y)); - if (ev.xcrossing.focus && - enter && !(enter->windowType() == Qt::Desktop) && !enter->isActiveWindow()) { - if (X11->focus_model == QX11Data::FM_Unknown) // check focus model - qt_check_focus_model(); - if (X11->focus_model == QX11Data::FM_PointerRoot) // PointerRoot mode - setActiveWindow(enter); + case XKeyPress: // keyboard event + qt_net_update_user_time(widget->window(), X11->userTime); + // fallthrough intended + case XKeyRelease: + { + if (keywidget && keywidget->isEnabled()) { // should always exist + // qDebug("sending key event"); + qt_keymapper_private()->translateKeyEvent(keywidget, event, grabbed); } break; } - if ((! enter || (enter->windowType() == Qt::Desktop)) && - event->xcrossing.focus && widget == QApplicationPrivate::active_window && - X11->focus_model == QX11Data::FM_PointerRoot // PointerRoot mode - ) { - setActiveWindow(0); - } - - if (qt_button_down && !d->inPopupMode()) - break; - - if (!curWin) - QApplicationPrivate::dispatchEnterLeave(widget, 0); - - if (enter) { - QWidget *alienEnter = enter->childAt(enterPoint); - if (alienEnter) - enter = alienEnter; - } - - QWidget *leave = qt_last_mouse_receiver ? qt_last_mouse_receiver : widget; - QWidget *activePopupWidget = qApp->activePopupWidget(); - - if (mouseGrabber && activePopupWidget && leave == activePopupWidget) - enter = mouseGrabber; - else if (enter != widget && mouseGrabber) { - if (!widget->rect().contains(widget->d_func()->mapFromWS(QPoint(event->xcrossing.x, - event->xcrossing.y)))) - break; - } - - QApplicationPrivate::dispatchEnterLeave(enter, leave); - qt_last_mouse_receiver = enter; + case GraphicsExpose: + case Expose: // paint event + widget->translatePaintEvent(event); + break; - if (enter && QApplicationPrivate::tryModalHelper(enter, 0)) { - QWidget *nativeEnter = enter->internalWinId() ? enter : enter->nativeParentWidget(); - curWin = nativeEnter->internalWinId(); - static_cast<QETWidget *>(nativeEnter)->translateMouseEvent(&ev); //we don't get MotionNotify, emulate it - } else { - curWin = 0; - qt_last_mouse_receiver = 0; - } - } + case ConfigureNotify: // window move/resize event + if (event->xconfigure.event == event->xconfigure.window) + widget->translateConfigEvent(event); break; case UnmapNotify: // window hidden @@ -4013,6 +4443,14 @@ bool qt_try_modal(QWidget *widget, XEvent *event) case LeaveNotify: case ClientMessage: return false; +#if !defined(QT_NO_XINPUT2) + case GenericEvent: + if (X11->use_xinput + && event->xcookie.extension == X11->xinput_opcode) + return false; + else + return true; +#endif default: break; } @@ -4051,10 +4489,38 @@ void QApplicationPrivate::openPopup(QWidget *popup) int r = XGrabKeyboard(dpy, popup->effectiveWinId(), false, GrabModeAsync, GrabModeAsync, X11->time); if ((popupGrabOk = (r == GrabSuccess))) { - r = XGrabPointer(dpy, popup->effectiveWinId(), true, - (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask - | EnterWindowMask | LeaveWindowMask | PointerMotionMask), - GrabModeAsync, GrabModeAsync, XNone, XNone, X11->time); +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) { + XIEventMask xieventmask; + uchar bitmask[2] = { 0, 0 }; + + xieventmask.deviceid = X11->xiMasterDeviceId; + xieventmask.mask = bitmask; + xieventmask.mask_len = sizeof(bitmask); + + XISetMask(bitmask, XI_ButtonPress); + XISetMask(bitmask, XI_ButtonRelease); + XISetMask(bitmask, XI_Motion); + XISetMask(bitmask, XI_Enter); + XISetMask(bitmask, XI_Leave); + + r = XIGrabDevice(X11->display, + xieventmask.deviceid, + popup->effectiveWinId(), + X11->time, + XNone, + GrabModeAsync, + GrabModeAsync, + true, + &xieventmask); + } else +#endif + { + r = XGrabPointer(dpy, popup->effectiveWinId(), true, + (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask + | EnterWindowMask | LeaveWindowMask | PointerMotionMask), + GrabModeAsync, GrabModeAsync, XNone, XNone, X11->time); + } if (!(popupGrabOk = (r == GrabSuccess))) { // transfer grab back to the keyboard grabber if any if (QWidgetPrivate::keyboardGrabber != 0) @@ -4102,10 +4568,16 @@ void QApplicationPrivate::closePopup(QWidget *popup) replayPopupMouseEvent = true; } // transfer grab back to mouse grabber if any, otherwise release the grab - if (QWidgetPrivate::mouseGrabber != 0) + if (QWidgetPrivate::mouseGrabber != 0) { QWidgetPrivate::mouseGrabber->grabMouse(); - else - XUngrabPointer(dpy, X11->time); + } else { +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) + XIUngrabDevice(X11->display, X11->xiMasterDeviceId, X11->time); + else +#endif + XUngrabPointer(dpy, X11->time); + } // transfer grab back to keyboard grabber if any, otherwise release the grab if (QWidgetPrivate::keyboardGrabber != 0) @@ -4141,10 +4613,38 @@ void QApplicationPrivate::closePopup(QWidget *popup) int r = XGrabKeyboard(dpy, aw->effectiveWinId(), false, GrabModeAsync, GrabModeAsync, X11->time); if ((popupGrabOk = (r == GrabSuccess))) { - r = XGrabPointer(dpy, aw->effectiveWinId(), true, - (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask - | EnterWindowMask | LeaveWindowMask | PointerMotionMask), - GrabModeAsync, GrabModeAsync, XNone, XNone, X11->time); +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) { + XIEventMask xieventmask; + uchar bitmask[2] = { 0, 0 }; + + xieventmask.deviceid = X11->xiMasterDeviceId; + xieventmask.mask = bitmask; + xieventmask.mask_len = sizeof(bitmask); + + XISetMask(bitmask, XI_ButtonPress); + XISetMask(bitmask, XI_ButtonRelease); + XISetMask(bitmask, XI_Motion); + XISetMask(bitmask, XI_Enter); + XISetMask(bitmask, XI_Leave); + + r = XIGrabDevice(X11->display, + xieventmask.deviceid, + aw->effectiveWinId(), + X11->time, + XNone, + GrabModeAsync, + GrabModeAsync, + true, + &xieventmask); + } else +#endif + { + r = XGrabPointer(dpy, aw->effectiveWinId(), true, + (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask + | EnterWindowMask | LeaveWindowMask | PointerMotionMask), + GrabModeAsync, GrabModeAsync, XNone, XNone, X11->time); + } if (!(popupGrabOk = (r == GrabSuccess))) { // transfer grab back to keyboard grabber if (QWidgetPrivate::keyboardGrabber != 0) @@ -4202,17 +4702,29 @@ bool QETWidget::translateMouseEvent(const XEvent *event) Q_ASSERT(internalWinId()); Q_D(QWidget); - QEvent::Type type; // event parameters - QPoint pos; - QPoint globalPos; - Qt::MouseButton button = Qt::NoButton; - Qt::MouseButtons buttons; - Qt::KeyboardModifiers modifiers; - XEvent nextEvent; if (qt_sm_blockUserInput) // block user interaction during session management return true; +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput + && event->type == GenericEvent + && event->xcookie.extension == X11->xinput_opcode) { + // translate XI2 events differently + return translateXI2Event((XIEvent *) event->xcookie.data); + } +#endif + + // event parameters + int xtype = event->type; + Window window = event->xany.window; + QEvent::Type type = QEvent::None; + QPoint pos; + QPoint globalPos; + Qt::MouseButton button = Qt::NoButton; + Qt::MouseButtons buttons = 0; + Qt::KeyboardModifiers modifiers = 0; + if (event->type == MotionNotify) { // mouse move if (event->xmotion.root != RootWindow(X11->display, x11Info().screen()) && ! qt_xdnd_dragging) @@ -4220,6 +4732,7 @@ bool QETWidget::translateMouseEvent(const XEvent *event) XMotionEvent lastMotion = event->xmotion; while(XPending(X11->display)) { // compress mouse moves + XEvent nextEvent; XNextEvent(X11->display, &nextEvent); if (nextEvent.type == ConfigureNotify || nextEvent.type == PropertyNotify @@ -4276,6 +4789,7 @@ bool QETWidget::translateMouseEvent(const XEvent *event) if (qt_button_down) return true; } else { // button press or release + type = event->type == ButtonPress ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease; pos.rx() = event->xbutton.x; pos.ry() = event->xbutton.y; pos = d->mapFromWS(pos); @@ -4325,67 +4839,79 @@ bool QETWidget::translateMouseEvent(const XEvent *event) case 8: button = Qt::XButton1; break; case 9: button = Qt::XButton2; break; } - if (event->type == ButtonPress) { // mouse button pressed - buttons |= button; + } + + return sendMouseEvent(xtype, window, type, pos, globalPos, button, buttons, modifiers); +} + +bool QETWidget::sendMouseEvent(int xtype, + Window window, + QEvent::Type type, + QPoint pos, + QPoint globalPos, + Qt::MouseButton button, + Qt::MouseButtons buttons, + Qt::KeyboardModifiers modifiers) +{ + if (xtype == ButtonPress) { // mouse button pressed + buttons |= button; #if defined(Q_OS_IRIX) && !defined(QT_NO_TABLET) - QTabletDeviceDataList *tablets = qt_tablet_devices(); - for (int i = 0; i < tablets->size(); ++i) { - QTabletDeviceData &tab = tablets->operator[](i); - XEvent myEv; - if (XCheckTypedEvent(X11->display, tab.xinput_button_press, &myEv)) { - if (translateXinputEvent(&myEv, &tab)) { - //Spontaneous event sent. Check if we need to continue. - if (qt_tabletChokeMouse) { - qt_tabletChokeMouse = false; - return false; - } + QTabletDeviceDataList *tablets = qt_tablet_devices(); + for (int i = 0; i < tablets->size(); ++i) { + QTabletDeviceData &tab = tablets->operator[](i); + XEvent myEv; + if (XCheckTypedEvent(X11->display, tab.xinput_button_press, &myEv)) { + if (translateXinputEvent(&myEv, &tab)) { + //Spontaneous event sent. Check if we need to continue. + if (qt_tabletChokeMouse) { + qt_tabletChokeMouse = false; + return false; } - } + } } + } #endif - if (!qt_button_down) { - qt_button_down = childAt(pos); //magic for masked widgets - if (!qt_button_down) - qt_button_down = this; - } - if (mouseActWindow == event->xbutton.window && - mouseButtonPressed == button && - (long)event->xbutton.time -(long)mouseButtonPressTime - < QApplication::doubleClickInterval() && - qAbs(event->xbutton.x - mouseXPos) < QT_GUI_DOUBLE_CLICK_RADIUS && - qAbs(event->xbutton.y - mouseYPos) < QT_GUI_DOUBLE_CLICK_RADIUS) { - type = QEvent::MouseButtonDblClick; - mouseButtonPressTime -= 2000; // no double-click next time - } else { - type = QEvent::MouseButtonPress; - mouseButtonPressTime = event->xbutton.time; - } - mouseButtonPressed = button; // save event params for - mouseXPos = event->xbutton.x; // future double click tests - mouseYPos = event->xbutton.y; - mouseGlobalXPos = globalPos.x(); - mouseGlobalYPos = globalPos.y(); - } else { // mouse button released - buttons &= ~button; + if (!qt_button_down) { + qt_button_down = childAt(pos); //magic for masked widgets + if (!qt_button_down) + qt_button_down = this; + } + if (mouseActWindow == window && + mouseButtonPressed == button && + (long)X11->time -(long)mouseButtonPressTime + < QApplication::doubleClickInterval() && + qAbs(globalPos.x() - mouseGlobalXPos) < QT_GUI_DOUBLE_CLICK_RADIUS && + qAbs(globalPos.y() - mouseGlobalYPos) < QT_GUI_DOUBLE_CLICK_RADIUS) { + type = QEvent::MouseButtonDblClick; + mouseButtonPressTime -= 2000; // no double-click next time + } else { + mouseButtonPressTime = X11->time; + } + // save params for future double-click tests + mouseButtonPressed = button; + mouseGlobalXPos = globalPos.x(); + mouseGlobalYPos = globalPos.y(); + } else if (xtype == ButtonRelease) { + // mouse button released + buttons &= ~button; #if defined(Q_OS_IRIX) && !defined(QT_NO_TABLET) - QTabletDeviceDataList *tablets = qt_tablet_devices(); - for (int i = 0; i < tablets->size(); ++i) { - QTabletDeviceData &tab = tablets->operator[](i); - XEvent myEv; - if (XCheckTypedEvent(X11->display, tab.xinput_button_press, &myEv)) { - if (translateXinputEvent(&myEv, &tab)) { - //Spontaneous event sent. Check if we need to continue. - if (qt_tabletChokeMouse) { - qt_tabletChokeMouse = false; - return false; - } + QTabletDeviceDataList *tablets = qt_tablet_devices(); + for (int i = 0; i < tablets->size(); ++i) { + QTabletDeviceData &tab = tablets->operator[](i); + XEvent myEv; + if (XCheckTypedEvent(X11->display, tab.xinput_button_press, &myEv)) { + if (translateXinputEvent(&myEv, &tab)) { + //Spontaneous event sent. Check if we need to continue. + if (qt_tabletChokeMouse) { + qt_tabletChokeMouse = false; + return false; } - } + } } -#endif - type = QEvent::MouseButtonRelease; } +#endif } + mouseActWindow = effectiveWinId(); // save some event params mouseButtonState = buttons; if (type == 0) // don't send event @@ -4395,7 +4921,7 @@ bool QETWidget::translateMouseEvent(const XEvent *event) QWidget *activePopupWidget = qApp->activePopupWidget(); QWidget *popup = qApp->activePopupWidget(); if (popup != this) { - if (event->type == LeaveNotify) + if (xtype == LeaveNotify) return false; if ((windowType() == Qt::Popup) && rect().contains(pos) && 0) popup = this; @@ -4515,6 +5041,412 @@ bool QETWidget::translateMouseEvent(const XEvent *event) return true; } +#if !defined(QT_NO_XINPUT2) + +static Atom mapXI2ButtonToButtonLabel(int button) +{ + Atom buttonatom = XNone; + + if (X11->xibuttonclassinfo) { + // map buttons based on the labeling + buttonatom = X11->xibuttonclassinfo->labels[button - 1]; + } + + // if there is no label, fall back to how we handle core pointer buttons + if (!buttonatom) { + switch (button) { + case Button1: buttonatom = ATOM(ButtonLeft); break; + case Button2: buttonatom = ATOM(ButtonMiddle); break; + case Button3: buttonatom = ATOM(ButtonRight); break; + case 4: buttonatom = ATOM(ButtonWheelUp); break; + case 5: buttonatom = ATOM(ButtonWheelDown); break; + case 6: buttonatom = ATOM(ButtonHorizWheelLeft); break; + case 7: buttonatom = ATOM(ButtonHorizWheelRight); break; + } + } + + return buttonatom; +} + +static Qt::MouseButton mapButtonLabelToQtButton(Atom buttonatom, int button) +{ + Qt::MouseButton qtbutton = Qt::NoButton; + if (buttonatom == ATOM(ButtonLeft)) { + qtbutton = Qt::LeftButton; + } else if (buttonatom == ATOM(ButtonMiddle)) { + qtbutton = Qt::MidButton; + } else if (buttonatom == ATOM(ButtonRight)) { + qtbutton = Qt::RightButton; + } else if (buttonatom == XNone) { + // ### TODO: find a better way to map the extra buttons + switch (button) { + case 8: qtbutton = Qt::XButton1; break; + case 9: qtbutton = Qt::XButton2; break; + } + } + return qtbutton; +} + +static Qt::MouseButton mapXI2ButtonToQtButton(int button) +{ + return mapButtonLabelToQtButton(mapXI2ButtonToButtonLabel(button), button); +} + +static Qt::MouseButtons translateXI2MouseButtons(XIButtonState *state) +{ + Qt::MouseButtons returnValue = 0; + for (int b = 0; b < state->mask_len; ++b) { + for (int t = 0; t < 8; ++t) { + int button = (b << 3) + t; + bool down = (state->mask[b] & (1 << t)) != 0; + if (!down) + continue; + Qt::MouseButton qtbutton = mapXI2ButtonToQtButton(button); + if (qtbutton != Qt::NoButton) + returnValue |= qtbutton; + } + } + return returnValue; +} + +bool QETWidget::translateXI2Event(const XIEvent *xievent) +{ + Q_D(QWidget); + static int width = 0; + static int height = 0; + if (width == 0 || height == 0) { + width = DisplayWidth(X11->display, 0); + height = DisplayHeight(X11->display, 0); + } + + if (xievent->evtype == XI_Motion) { + XIDeviceEvent *motionevent = (XIDeviceEvent *) xievent; + + XIDeviceEvent lastMotion = *motionevent; + int index = xiFindActiveDevice(motionevent->deviceid); + bool isTouch = (index == -1) ? false : X11->xiIsTouch[index]; + + Qt::MouseButtons buttons; + Qt::KeyboardModifiers modifiers; + Qt::MouseButtons lastButtons; + uint activeTouchPoints = 0; + Qt::KeyboardModifiers lastModifiers = X11->translateModifiers(lastMotion.mods.effective); + if (!isTouch) { // if mouse + lastButtons = translateXI2MouseButtons(&lastMotion.buttons); + } else { // else touch + // get a list of currently active touch points + QList<QTouchEvent::TouchPoint> touchPoints = qApp->d_func()->appAllTouchPoints; + for (int i = 0; i < touchPoints.size(); ++i) { + if (touchPoints.at(i).state() != Qt::TouchPointReleased) + activeTouchPoints |= 1 << i; + } + } + + while (XPending(X11->display)) { + XEvent ev; + XNextEvent(X11->display, &ev); + + // process certain types of events that often come together with motion events + if (ev.type == ConfigureNotify + || ev.type == PropertyNotify + || ev.type == Expose + || ev.type == GraphicsExpose + || ev.type == NoExpose + || ev.type == KeymapNotify + || (ev.type == GenericEvent + && ev.xcookie.extension == X11->xinput_opcode + && (ev.xcookie.evtype == XI_Enter || ev.xcookie.evtype == XI_Leave) + // ### XI_ButtonPress || XI_ButtonRelease + && qt_button_down == this) + || (ev.type == ClientMessage + && (ev.xclient.message_type == ATOM(_QT_SCROLL_DONE) || + (ev.xclient.message_type == ATOM(WM_PROTOCOLS) && + (Atom)ev.xclient.data.l[0] == ATOM(_NET_WM_SYNC_REQUEST))))) { + qApp->x11ProcessEvent(&ev); + continue; + } else if (ev.type == GenericEvent + && ev.xcookie.extension == X11->xinput_opcode + && ev.xcookie.evtype == XI_Motion + && XGetEventData(X11->display, &ev.xcookie)) { + // this is an XI_Motion event... + motionevent = (XIDeviceEvent *) ev.xcookie.data; + + modifiers = X11->translateModifiers(motionevent->mods.effective); + int index = xiFindActiveDevice(motionevent->deviceid); + if (index == -1) { + // Can not find device in our Active list; likely from multiple master pointers... + XPutBackEvent(X11->display, &ev); + XFreeEventData(X11->display, &ev.xcookie); + break; + } + uint active = 0; + bool isTouch = X11->xiIsTouch[index]; + if (isTouch) + { + for (int i = 0; i < X11->xiDeviceInfo[index].num_classes; ++i) { + XIAnyClassInfo *classinfo = X11->xiDeviceInfo[index].classes[i]; + if (classinfo->type == XIValuatorClass) { + XIValuatorClassInfo *valuatorclassinfo = reinterpret_cast<XIValuatorClassInfo *>(classinfo); + int n = valuatorclassinfo->number; + + if (!XIMaskIsSet(motionevent->valuators.mask, n)) + continue; + + if (valuatorclassinfo->label == ATOM(AbsMTTrackingID)) { + int id = motionevent->valuators.values[n]; + active |= 1 << id; + } + } + } + } + else + { + buttons = translateXI2MouseButtons(&motionevent->buttons); + } + + // .. can we compress it? + if (motionevent->event == lastMotion.event + && modifiers == lastModifiers + && (isTouch || buttons == lastButtons) + && (!isTouch || activeTouchPoints == active)) { + // send event through filters + if (!qt_x11EventFilter(&ev) && !x11Event(&ev)) { + // compress this motion event + lastMotion = *motionevent; + if (isTouch) + lastButtons = buttons; + lastModifiers = modifiers; + XFreeEventData(X11->display, &ev.xcookie); + continue; + } else { + // filtered + XFreeEventData(X11->display, &ev.xcookie); + break; + } + } + + // different state or different window, put this event back and stop compression + XPutBackEvent(X11->display, &ev); + XFreeEventData(X11->display, &ev.xcookie); + break; + } + + // not the right type of event, stop compression + XPutBackEvent(X11->display, &ev); + break; + } + } + + bool isTouch = false; + if (xievent->evtype == XI_ButtonPress + || xievent->evtype == XI_ButtonRelease + || xievent->evtype == XI_Motion) { + const XIDeviceEvent *xideviceevent = reinterpret_cast<const XIDeviceEvent *>(xievent); + int index = xiFindActiveDevice(xideviceevent->deviceid); + isTouch = (index == -1) ? false : X11->xiIsTouch[index]; + if (isTouch) { + QList<QTouchEvent::TouchPoint> touchPoints = qApp->d_func()->appAllTouchPoints; + if (touchPoints.count() != 10) { //X11->xiMaxContacts) { + // initial event, allocate space for all (potential) touch points + touchPoints.reserve(10); + for (int i = 0; i < 10; ++i) + touchPoints << QTouchEvent::TouchPoint(i); + } + qreal x, y, nx, ny, w = 0.0, h = 0.0, p = -1.0; + x = y = nx = ny = 0.0; + int id; + uint active = 0; + for (int i = 0; i < X11->xiDeviceInfo[index].num_classes; ++i) { + XIAnyClassInfo *classinfo = X11->xiDeviceInfo[index].classes[i]; + if (classinfo->type == XIValuatorClass) { + XIValuatorClassInfo *valuatorclassinfo = reinterpret_cast<XIValuatorClassInfo *>(classinfo); + int n = valuatorclassinfo->number; + + if (!XIMaskIsSet(xideviceevent->valuators.mask, n)) + continue; + + if (valuatorclassinfo->label == ATOM(AbsMTPositionX)) { + x = xideviceevent->valuators.values[n]; + nx = (x - valuatorclassinfo->min) / (valuatorclassinfo->max - valuatorclassinfo->min); + } else if (valuatorclassinfo->label == ATOM(AbsMTPositionY)) { + y = xideviceevent->valuators.values[n]; + ny = (y - valuatorclassinfo->min) / (valuatorclassinfo->max - valuatorclassinfo->min); + } else if (valuatorclassinfo->label == ATOM(AbsMTTouchMajor)) { + w = xideviceevent->valuators.values[n]; + } else if (valuatorclassinfo->label == ATOM(AbsMTTouchMinor)) { + h = xideviceevent->valuators.values[n]; + } else if (valuatorclassinfo->label == ATOM(AbsMTPressure)) { + p = (xideviceevent->valuators.values[n] - valuatorclassinfo->min) / (valuatorclassinfo->max - valuatorclassinfo->min); + } else if (valuatorclassinfo->label == ATOM(AbsMTTrackingID)) { + id = xideviceevent->valuators.values[n]; + active |= 1 << id; + QTouchEvent::TouchPoint &touchPoint = touchPoints[id]; + + Qt::TouchPointStates newstate; + if (touchPoint.state() == Qt::TouchPointReleased) { + newstate |= Qt::TouchPointPressed; + } else { + if (touchPoint.screenPos() != QPoint(x, y)) + newstate |= Qt::TouchPointMoved; + else + newstate |= Qt::TouchPointStationary; + } + + if (id == 0) + newstate |= Qt::TouchPointPrimary; + + touchPoint.setState(newstate); + if (w != 0.0 && h != 0.0) + touchPoint.setScreenRect(QRectF((nx * width) - w/2, (ny * height) - h/2, w, h)); + else + touchPoint.setScreenPos(QPoint(nx * width, ny * height)); + touchPoint.setNormalizedPos(QPointF(nx, ny)); + touchPoint.setPressure(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) { + Qt::TouchPointStates newstate = Qt::TouchPointReleased; + + if (touchPoints.at(i).id() == 0) + newstate |= Qt::TouchPointPrimary; + + touchPoints[i].setState(newstate); + } + } + + if (xideviceevent->evtype == XI_ButtonRelease) { + // final event, forget touch state + qApp->d_func()->appAllTouchPoints.clear(); + } else { + // save current state so that we have something to reuse later + qApp->d_func()->appAllTouchPoints = touchPoints; + } + + QApplicationPrivate::translateRawTouchEvent(this, QTouchEvent::TouchScreen, touchPoints); + } + } + + // event parameters + int xtype = 0; + Window window = XNone; + QEvent::Type type = QEvent::None; + QPoint pos; + QPoint globalPos; + Qt::MouseButton button = Qt::NoButton; + Qt::MouseButtons buttons = 0; + Qt::KeyboardModifiers modifiers = 0; + + if (xievent->evtype == XI_Motion) { + XIDeviceEvent *motionevent = (XIDeviceEvent *) xievent; + + if (X11->xiMasterIndex == -1 || motionevent->deviceid != X11->xiMasterDeviceId) + return sendMouseEvent(xtype, window, type, pos, globalPos, button, buttons, modifiers); + + XIDeviceEvent lastMotion = *motionevent; + Qt::MouseButtons lastButtons = translateXI2MouseButtons(&lastMotion.buttons); + Qt::KeyboardModifiers lastModifiers = X11->translateModifiers(lastMotion.mods.effective); + + xtype = MotionNotify; + window = lastMotion.event; + type = QEvent::MouseMove; + pos.rx() = lastMotion.event_x; + pos.ry() = lastMotion.event_y; + pos = d->mapFromWS(pos); + globalPos.rx() = lastMotion.root_x; + globalPos.ry() = lastMotion.root_y; + buttons = lastButtons; + modifiers = lastModifiers; + if (qt_button_down && !buttons) + qt_button_down = 0; + } else if (xievent->evtype == XI_Enter || xievent->evtype == XI_Leave) { + XIEnterEvent *enterevent = (XIEnterEvent *) xievent; + + if (X11->xiMasterIndex == -1 || enterevent->deviceid != X11->xiMasterDeviceId) + return sendMouseEvent(xtype, window, type, pos, globalPos, button, buttons, modifiers); + + xtype = enterevent->evtype == XI_Enter ? EnterNotify : LeaveNotify; + window = enterevent->event; + type = QEvent::MouseMove; + pos.rx() = enterevent->event_x; + pos.ry() = enterevent->event_y; + pos = d->mapFromWS(pos); + globalPos.rx() = enterevent->root_x; + globalPos.ry() = enterevent->root_y; + buttons = translateXI2MouseButtons(&enterevent->buttons); + modifiers = X11->translateModifiers(enterevent->mods.effective); + if (qt_button_down && !buttons) + qt_button_down = 0; + if (qt_button_down) + return true; + } else if (xievent->evtype == XI_ButtonPress || xievent->evtype == XI_ButtonRelease){ + // XI_ButtonPress or XI_ButtonRelease + XIDeviceEvent *deviceevent = (XIDeviceEvent *) xievent; + + if (X11->xiMasterIndex == -1 || deviceevent->deviceid != X11->xiMasterDeviceId) + return sendMouseEvent(xtype, window, type, pos, globalPos, button, buttons, modifiers); + + xtype = deviceevent->evtype == XI_ButtonPress ? ButtonPress : ButtonRelease; + window = deviceevent->event; + type = deviceevent->evtype == XI_ButtonPress ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease; + pos.rx() = deviceevent->event_x; + pos.ry() = deviceevent->event_y; + pos = d->mapFromWS(pos); + globalPos.rx() = deviceevent->root_x; + globalPos.ry() = deviceevent->root_y; + buttons = translateXI2MouseButtons(&deviceevent->buttons); + modifiers = X11->translateModifiers(deviceevent->mods.effective); + + // map the device button to a button label + Atom buttonatom = mapXI2ButtonToButtonLabel(deviceevent->detail); + button = mapButtonLabelToQtButton(buttonatom, deviceevent->detail); + + if (button == Qt::NoButton + && (buttonatom == ATOM(ButtonWheelUp) + || buttonatom == ATOM(ButtonWheelDown) + || buttonatom == ATOM(ButtonHorizWheelLeft) + || buttonatom == ATOM(ButtonHorizWheelRight))) { + // mouse wheel, we are only interested in the presses for these buttons + + if (deviceevent->evtype == XI_ButtonPress) { + // compress wheel events (the X Server will simply + // send a button press for each single notch, + // regardless whether the application can catch up + // or not) + int delta = 1; + + XEvent ev; + while (XCheckIfEvent(X11->display, &ev, qt_XI_Wheel_scanner, (XPointer) deviceevent)) + delta++; + + // the delta is defined as multiples of + // WHEEL_DELTA, which is set to 120. Future wheels + // may offer a finer-resolution. A positive delta + // indicates forward rotation, a negative one + // backward rotation respectively. + delta *= 120 * ((buttonatom == ATOM(ButtonWheelUp) || buttonatom == ATOM(ButtonHorizWheelLeft)) ? 1 : -1); + bool hor = (((buttonatom == ATOM(ButtonWheelUp) + || buttonatom == ATOM(ButtonWheelDown)) + && (modifiers & Qt::AltModifier)) + || (buttonatom == ATOM(ButtonHorizWheelLeft) + || buttonatom == ATOM(ButtonHorizWheelRight))); + translateWheelEvent(globalPos.x(), globalPos.y(), delta, buttons, + modifiers, (hor) ? Qt::Horizontal: Qt::Vertical); + } + return true; + } + } else { + // unreachable + qFatal("Qt: Internal Error: Unknown XI2 event passed to translateXI2Event()"); + } + + return sendMouseEvent(xtype, window, type, pos, globalPos, button, buttons, modifiers); +} + +#endif // !QT_NO_XINPUT2 // // Wheel event translation diff --git a/src/gui/kernel/qcursor_x11.cpp b/src/gui/kernel/qcursor_x11.cpp index e22462d..740fd80 100644 --- a/src/gui/kernel/qcursor_x11.cpp +++ b/src/gui/kernel/qcursor_x11.cpp @@ -152,16 +152,42 @@ Qt::HANDLE QCursor::handle() const QPoint QCursor::pos() { - Window root; - Window child; - int root_x, root_y, win_x, win_y; - uint buttons; - Display* dpy = X11->display; - for (int i = 0; i < ScreenCount(dpy); ++i) { - if (XQueryPointer(dpy, QX11Info::appRootWindow(i), &root, &child, &root_x, &root_y, - &win_x, &win_y, &buttons)) - - return QPoint(root_x, root_y); +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) { + Window root; + Window child; + double root_x, root_y, win_x, win_y; + XIButtonState buttons; + XIModifierState modifiers; + XIGroupState groups; + for (int i = 0; i < ScreenCount(X11->display); ++i) { + if (XIQueryPointer(X11->display, + X11->xiMasterDeviceId, + QX11Info::appRootWindow(i), + &root, + &child, + &root_x, &root_y, + &win_x, &win_y, + &buttons, + &modifiers, + &groups)) { + free(buttons.mask); + return QPoint(root_x, root_y); + } + } + } else +#endif + { + Window root; + Window child; + int root_x, root_y, win_x, win_y; + uint buttons; + for (int i = 0; i < ScreenCount(X11->display); ++i) { + if (XQueryPointer(X11->display, QX11Info::appRootWindow(i), &root, &child, &root_x, &root_y, + &win_x, &win_y, &buttons)) + + return QPoint(root_x, root_y); + } } return QPoint(); } @@ -171,15 +197,41 @@ QPoint QCursor::pos() #ifndef QT_NO_CURSOR int QCursor::x11Screen() { - Window root; - Window child; - int root_x, root_y, win_x, win_y; - uint buttons; - Display* dpy = X11->display; - for (int i = 0; i < ScreenCount(dpy); ++i) { - if (XQueryPointer(dpy, QX11Info::appRootWindow(i), &root, &child, &root_x, &root_y, - &win_x, &win_y, &buttons)) - return i; +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) { + Window root; + Window child; + double root_x, root_y, win_x, win_y; + XIButtonState buttons; + XIModifierState modifiers; + XIGroupState groups; + for (int i = 0; i < ScreenCount(X11->display); ++i) { + if (XIQueryPointer(X11->display, + X11->xiMasterDeviceId, + QX11Info::appRootWindow(i), + &root, + &child, + &root_x, &root_y, + &win_x, &win_y, + &buttons, + &modifiers, + &groups)) { + free(buttons.mask); + return i; + } + } + } else +#endif + { + Window root; + Window child; + int root_x, root_y, win_x, win_y; + uint buttons; + for (int i = 0; i < ScreenCount(X11->display); ++i) { + if (XQueryPointer(X11->display, QX11Info::appRootWindow(i), &root, &child, &root_x, &root_y, + &win_x, &win_y, &buttons)) + return i; + } } return -1; } @@ -191,21 +243,47 @@ void QCursor::setPos(int x, int y) // this is copied from pos(), since we need the screen number for the correct // root window in the XWarpPointer call - Window root; - Window child; - int root_x, root_y, win_x, win_y; - uint buttons; - Display* dpy = X11->display; int screen; - for (screen = 0; screen < ScreenCount(dpy); ++screen) { - if (XQueryPointer(dpy, QX11Info::appRootWindow(screen), &root, &child, &root_x, &root_y, - &win_x, &win_y, &buttons)) { - current = QPoint(root_x, root_y); - break; +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) { + Window root; + Window child; + double root_x, root_y, win_x, win_y; + XIButtonState buttons; + XIModifierState modifiers; + XIGroupState groups; + for (screen = 0; screen < ScreenCount(X11->display); ++screen) { + if (XIQueryPointer(X11->display, + X11->xiMasterDeviceId, + QX11Info::appRootWindow(screen), + &root, + &child, + &root_x, &root_y, + &win_x, &win_y, + &buttons, + &modifiers, + &groups)) { + current = QPoint(root_x, root_y); + break; + } + } + } else +#endif + { + Window root; + Window child; + int root_x, root_y, win_x, win_y; + uint buttons; + for (screen = 0; screen < ScreenCount(X11->display); ++screen) { + if (XQueryPointer(X11->display, QX11Info::appRootWindow(screen), &root, &child, &root_x, &root_y, + &win_x, &win_y, &buttons)) { + current = QPoint(root_x, root_y); + break; + } } } - if (screen >= ScreenCount(dpy)) + if (screen >= ScreenCount(X11->display)) return; // Need to check, since some X servers generate null mouse move @@ -215,7 +293,12 @@ void QCursor::setPos(int x, int y) if (current == target) return; - XWarpPointer(X11->display, XNone, QX11Info::appRootWindow(screen), 0, 0, 0, 0, x, y); +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) + XIWarpPointer(X11->display, X11->xiMasterDeviceId, XNone, QX11Info::appRootWindow(screen), 0, 0, 0, 0, x, y); + else +#endif + XWarpPointer(X11->display, XNone, QX11Info::appRootWindow(screen), 0, 0, 0, 0, x, y); } diff --git a/src/gui/kernel/qt_x11_p.h b/src/gui/kernel/qt_x11_p.h index c44d151..5486eaa 100644 --- a/src/gui/kernel/qt_x11_p.h +++ b/src/gui/kernel/qt_x11_p.h @@ -98,13 +98,14 @@ # include <X11/extensions/shape.h> #endif // QT_NO_SHAPE - -#if !defined (QT_NO_TABLET) +#if !defined(QT_NO_XINPUT2) +# include <X11/extensions/XInput2.h> +#elif !defined (QT_NO_TABLET) # include <X11/extensions/XInput.h> -#if defined (Q_OS_IRIX) -# include <X11/extensions/SGIMisc.h> -# include <wacom.h> -#endif +# if defined (Q_OS_IRIX) +# include <X11/extensions/SGIMisc.h> +# include <wacom.h> +# endif #endif // QT_NO_TABLET @@ -424,23 +425,33 @@ struct QX11Data PtrXFixesSelectSelectionInput ptrXFixesSelectSelectionInput; #endif -#ifndef QT_NO_XINPUT +#if !defined(QT_NO_XINPUT2) + XIDeviceInfo *xiDeviceInfo; // device info for all connected devices + int xiDeviceCount; + int xiMasterIndex; // Index in xiDeviceInfo of the Master core pointer device + int xiMasterDeviceId; // Device Id of the Master core pointer + XIButtonClassInfo *xibuttonclassinfo; // button class information for the Master pointer + // Touch devices can be slaves or floating; they are not necessarily bound to a core pointer + // So we create a list of all of the 'active' devices to listen to and grab. This list + // will contain any device that supports the TrackingID and the Master Core Pointer + QList<int> xiActiveDevices; + QList<bool> xiIsTouch; +#elif !defined(QT_NO_XINPUT) PtrXCloseDevice ptrXCloseDevice; PtrXListInputDevices ptrXListInputDevices; PtrXOpenDevice ptrXOpenDevice; PtrXFreeDeviceList ptrXFreeDeviceList; PtrXSelectExtensionEvent ptrXSelectExtensionEvent; -#endif // QT_NO_XINPUT - +#endif // true if Qt is compiled w/ MIT-SHM support and MIT-SHM is supported on the connected Display bool use_mitshm; bool use_mitshm_pixmaps; int mitshm_major; - // true if Qt is compiled w/ Tablet support and we have a tablet. + // true if Qt is compiled w/ XInput2 or Tablet support and we have a tablet. bool use_xinput; - int xinput_major; + int xinput_opcode; int xinput_eventbase; int xinput_errorbase; @@ -689,6 +700,22 @@ struct QX11Data XTabletStylus, XTabletEraser, + // XInput2 + ButtonLeft, + ButtonMiddle, + ButtonRight, + ButtonWheelUp, + ButtonWheelDown, + ButtonHorizWheelLeft, + ButtonHorizWheelRight, + AbsMTPositionX, + AbsMTPositionY, + AbsMTTouchMajor, + AbsMTTouchMinor, + AbsMTPressure, + AbsMTTrackingID, + MaxContacts, + NPredefinedAtoms, _QT_SETTINGS_TIMESTAMP = NPredefinedAtoms, diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 190a77c..cb2abd2 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -10979,7 +10979,7 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) #endif break; case Qt::WA_AcceptTouchEvents: -#if defined(Q_WS_WIN) || defined(Q_WS_MAC) || defined(Q_OS_SYMBIAN) +#if !defined(Q_WS_X11) && (defined(Q_WS_WIN) || defined(Q_WS_MAC) || defined(Q_OS_SYMBIAN)) if (on) d->registerTouchWindow(); #endif diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index 56df48e..5d22f58 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -688,6 +688,11 @@ public: inline QRect mapFromWS(const QRect &r) const { QRect rr(r); rr.translate(data.wrect.topLeft()); return rr; } + +#endif + +#if !defined(Q_WS_QWS) && !defined(Q_WS_X11) + void registerTouchWindow(); #endif // Variables. @@ -787,6 +792,7 @@ public: void x11UpdateIsOpaque(); bool isBackgroundInherited() const; void updateX11AcceptFocus(); + void grabMouse_sys(Qt::HANDLE xcursorid); QPoint mapToGlobal(const QPoint &pos) const; QPoint mapFromGlobal(const QPoint &pos) const; #elif defined(Q_WS_WIN) // <--------------------------------------------------------- WIN @@ -797,12 +803,13 @@ public: bool shouldShowMaximizeButton(); void winUpdateIsOpaque(); void reparentChildren(); + void registerTouchWindow(); + #ifndef QT_NO_DRAGANDDROP QOleDropTarget *registerOleDnd(QWidget *widget); void unregisterOleDnd(QWidget *widget, QOleDropTarget *target); #endif void grabMouseWhileInWindow(); - void registerTouchWindow(); void winSetupGestures(); #elif defined(Q_WS_MAC) // <--------------------------------------------------------- MAC // This is new stuff diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp index aec4ade..7d2d19c 100644 --- a/src/gui/kernel/qwidget_x11.cpp +++ b/src/gui/kernel/qwidget_x11.cpp @@ -327,18 +327,42 @@ void qt_x11_enforce_cursor(QWidget * w, bool force) if (w->isWindow() || w->testAttribute(Qt::WA_SetCursor)) { #ifndef QT_NO_CURSOR QCursor *oc = QApplication::overrideCursor(); - if (oc) { - XDefineCursor(X11->display, winid, oc->handle()); - } else if (w->isEnabled()) { - XDefineCursor(X11->display, winid, w->cursor().handle()); - } else { - // enforce the windows behavior of clearing the cursor on - // disabled widgets - XDefineCursor(X11->display, winid, XNone); +# if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) { + // duplicated below (more or less) + if (oc) { + XIDefineCursor(X11->display, X11->xiMasterDeviceId, winid, oc->handle()); + } else if (w->isEnabled()) { + XIDefineCursor(X11->display, X11->xiMasterDeviceId, winid, w->cursor().handle()); + } else { + // enforce the windows behavior of clearing the cursor on + // disabled widgets + XIDefineCursor(X11->display, X11->xiMasterDeviceId, winid, XNone); + } + } else +# endif + { + // duplicated above (more or less) + if (oc) { + XDefineCursor(X11->display, winid, oc->handle()); + } else if (w->isEnabled()) { + XDefineCursor(X11->display, winid, w->cursor().handle()); + } else { + // enforce the windows behavior of clearing the cursor on + // disabled widgets + XDefineCursor(X11->display, winid, XNone); + } } #endif } else { - XDefineCursor(X11->display, winid, XNone); +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) { + XIDefineCursor(X11->display, X11->xiMasterDeviceId, winid, XNone); + } else +#endif + { + XDefineCursor(X11->display, winid, XNone); + } } } @@ -869,7 +893,41 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO // else XSelectInput(dpy, id, stdDesktopEventMask); } else if (q->internalWinId()) { - XSelectInput(dpy, id, stdWidgetEventMask); + uint eventmask = stdWidgetEventMask; + +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) { + foreach (int deviceId, X11->xiActiveDevices) { + XIEventMask xieventmask; + uchar bitmask[2] = { 0, 0 }; + + xieventmask.deviceid = deviceId; + xieventmask.mask = bitmask; + xieventmask.mask_len = sizeof(bitmask); + + XISetMask(bitmask, XI_ButtonPress); + XISetMask(bitmask, XI_ButtonRelease); + XISetMask(bitmask, XI_Motion); + if (deviceId == X11->xiMasterDeviceId) { + XISetMask(bitmask, XI_Enter); + XISetMask(bitmask, XI_Leave); + } + + XISelectEvents(dpy, id, &xieventmask, 1); + } + + // remove the corresponding core events from the standard event mask + eventmask &= ~(ButtonPressMask + | ButtonReleaseMask + | ButtonMotionMask + | PointerMotionMask + | EnterWindowMask + | LeaveWindowMask); + } +#endif + + XSelectInput(dpy, id, eventmask); + #if !defined (QT_NO_TABLET) QTabletDeviceDataList *tablet_list = qt_tablet_devices(); if (X11->ptrXSelectExtensionEvent) { @@ -1572,22 +1630,49 @@ void QWidgetPrivate::setWindowIconText_sys(const QString &iconText) PropModeReplace, (unsigned char *) icon_name.constData(), icon_name.size()); } - -void QWidget::grabMouse() +void QWidgetPrivate::grabMouse_sys(Qt::HANDLE xcursorid) { - if (isVisible() && !qt_nograb()) { - if (QWidgetPrivate::mouseGrabber && QWidgetPrivate::mouseGrabber != this) - QWidgetPrivate::mouseGrabber->releaseMouse(); - Q_ASSERT(testAttribute(Qt::WA_WState_Created)); -#ifndef QT_NO_DEBUG - int status = + Q_Q(QWidget); + if (q->isVisible() && !qt_nograb()) { + if (mouseGrabber && mouseGrabber != q) + mouseGrabber->releaseMouse(); + + Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); + int status; +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) { + XIEventMask xieventmask; + uchar bitmask[2] = { 0, 0 }; + + xieventmask.deviceid = X11->xiMasterDeviceId; + xieventmask.mask = bitmask; + xieventmask.mask_len = sizeof(bitmask); + + XISetMask(bitmask, XI_ButtonPress); + XISetMask(bitmask, XI_ButtonRelease); + XISetMask(bitmask, XI_Motion); + XISetMask(bitmask, XI_Enter); + XISetMask(bitmask, XI_Leave); + + status = XIGrabDevice(X11->display, + X11->xiMasterDeviceId, + q->effectiveWinId(), + X11->time, + xcursorid, + GrabModeAsync, + GrabModeAsync, + False, + &xieventmask); + } else #endif - XGrabPointer(X11->display, effectiveWinId(), False, - (uint)(ButtonPressMask | ButtonReleaseMask | - PointerMotionMask | EnterWindowMask | - LeaveWindowMask), - GrabModeAsync, GrabModeAsync, - XNone, XNone, X11->time); + { + status = XGrabPointer(X11->display, q->effectiveWinId(), False, + (uint)(ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | EnterWindowMask | + LeaveWindowMask), + GrabModeAsync, GrabModeAsync, + XNone, xcursorid, X11->time); + } #ifndef QT_NO_DEBUG if (status) { const char *s = @@ -1598,54 +1683,41 @@ void QWidget::grabMouse() "<?>"; qWarning("QWidget::grabMouse: Failed with %s", s); } +#else + Q_UNUSED(status); #endif - QWidgetPrivate::mouseGrabber = this; + mouseGrabber = q; } } +void QWidget::grabMouse() +{ + Q_D(QWidget); + d->grabMouse_sys(XNone); +} #ifndef QT_NO_CURSOR void QWidget::grabMouse(const QCursor &cursor) { - if (!qt_nograb()) { - if (QWidgetPrivate::mouseGrabber && QWidgetPrivate::mouseGrabber != this) - QWidgetPrivate::mouseGrabber->releaseMouse(); - Q_ASSERT(testAttribute(Qt::WA_WState_Created)); -#ifndef QT_NO_DEBUG - int status = -#endif - XGrabPointer(X11->display, effectiveWinId(), False, - (uint)(ButtonPressMask | ButtonReleaseMask | - PointerMotionMask | EnterWindowMask | LeaveWindowMask), - GrabModeAsync, GrabModeAsync, - XNone, cursor.handle(), X11->time); -#ifndef QT_NO_DEBUG - if (status) { - const char *s = - status == GrabNotViewable ? "\"GrabNotViewable\"" : - status == AlreadyGrabbed ? "\"AlreadyGrabbed\"" : - status == GrabFrozen ? "\"GrabFrozen\"" : - status == GrabInvalidTime ? "\"GrabInvalidTime\"" : - "<?>"; - qWarning("QWidget::grabMouse: Failed with %s", s); - } -#endif - QWidgetPrivate::mouseGrabber = this; - } + Q_D(QWidget); + d->grabMouse_sys(cursor.handle()); } #endif - void QWidget::releaseMouse() { if (!qt_nograb() && QWidgetPrivate::mouseGrabber == this) { - XUngrabPointer(X11->display, X11->time); +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) + XIUngrabDevice(X11->display, X11->xiMasterDeviceId, X11->time); + else +#endif + XUngrabPointer(X11->display, X11->time); XFlush(X11->display); QWidgetPrivate::mouseGrabber = 0; } } - void QWidget::grabKeyboard() { if (!qt_nograb()) { diff --git a/src/gui/kernel/qx11embed_x11.cpp b/src/gui/kernel/qx11embed_x11.cpp index a968300..440d91e 100644 --- a/src/gui/kernel/qx11embed_x11.cpp +++ b/src/gui/kernel/qx11embed_x11.cpp @@ -1539,13 +1539,24 @@ bool QX11EmbedContainer::x11Event(XEvent *event) case XButtonPress: if (!d->clientIsXEmbed) { setFocus(Qt::MouseFocusReason); - XAllowEvents(x11Info().display(), ReplayPointer, CurrentTime); +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) + XIAllowEvents(x11Info().display(), X11->xiMasterDeviceId, ReplayPointer, CurrentTime); + else +#endif + XAllowEvents(x11Info().display(), ReplayPointer, CurrentTime); return true; } break; case XButtonRelease: - if (!d->clientIsXEmbed) - XAllowEvents(x11Info().display(), SyncPointer, CurrentTime); + if (!d->clientIsXEmbed) { +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) + XIAllowEvents(x11Info().display(), X11->xiMasterDeviceId, SyncPointer, CurrentTime); + else +#endif + XAllowEvents(x11Info().display(), SyncPointer, CurrentTime); + } break; default: break; @@ -1778,14 +1789,41 @@ void QX11EmbedContainerPrivate::checkGrab() Q_Q(QX11EmbedContainer); if (!clientIsXEmbed && q->isActiveWindow() && !q->hasFocus()) { if (!xgrab) { - XGrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId(), - true, ButtonPressMask, GrabModeSync, GrabModeAsync, - None, None); +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) { + XIEventMask xieventmask; + uchar bitmask[1] = { 0 }; + xieventmask.deviceid = X11->xiMasterDeviceId; + xieventmask.mask = bitmask; + xieventmask.mask_len = sizeof(bitmask); + XISetMask(bitmask, XI_ButtonPress); + + XIGrabModifiers anymods; + anymods.modifiers = XIAnyModifier; + + XIGrabButton(q->x11Info().display(), X11->xiMasterDeviceId, XIAnyButton, q->internalWinId(), XNone, GrabModeSync, GrabModeAsync, true, &xieventmask, 1, &anymods); + } else +#endif + { + XGrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId(), + true, ButtonPressMask, GrabModeSync, GrabModeAsync, + None, None); + } } xgrab = true; } else { - if (xgrab) - XUngrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId()); + if (xgrab) { +#if !defined(QT_NO_XINPUT2) + if (X11->use_xinput) { + XIGrabModifiers anymods; + anymods.modifiers = XIAnyModifier; + XIUngrabButton(q->x11Info().display(), X11->xiMasterDeviceId, AnyButton, q->internalWinId(), 1, &anymods); + } else +#endif + { + XUngrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId()); + } + } xgrab = false; } } -- 1.7.10.3