summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.p.agocs@nokia.com>2012-06-05 16:04:02 +0300
committerQt by Nokia <qt-info@nokia.com>2012-06-28 12:35:14 +0200
commit26c85a5f79875a25367527462e8d0864021078ab (patch)
treefce112bb5ed906eeff1ded77c0863315a66f595b /src/plugins
parentba892305960b37846ee49a90c5684aa8dbfd3d72 (diff)
Basic tablet support in xcb through XI2
The Maemo-specific function have been renamed a bit to prevent them clashing with the more generic stuff. Task-number: QTBUG-25865 Change-Id: Id55693159e15d5a0c679546eb48308feb48acac9 Reviewed-by: Samuel Rødal <samuel.rodal@nokia.com>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp89
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h70
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_maemo.cpp59
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp310
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp6
-rw-r--r--src/plugins/platforms/xcb/xcb.pro6
6 files changed, 464 insertions, 76 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 679f3e7a1f..3b2e2bbf96 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -87,6 +87,11 @@ extern "C" {
#include <EGL/eglext.h>
#endif
+#if defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO)
+#include <X11/extensions/XInput2.h>
+#include <X11/extensions/XI2proto.h>
+#endif
+
QT_BEGIN_NAMESPACE
#ifdef XCB_USE_XLIB
@@ -185,7 +190,10 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, const char
initializeXFixes();
initializeXRender();
+ m_xi2Enabled = false;
#ifdef XCB_USE_XINPUT2_MAEMO
+ initializeXInput2Maemo();
+#elif defined(XCB_USE_XINPUT2)
initializeXInput2();
#endif
initializeXShape();
@@ -218,6 +226,8 @@ QXcbConnection::~QXcbConnection()
delete m_screens.takeLast();
#ifdef XCB_USE_XINPUT2_MAEMO
+ finalizeXInput2Maemo();
+#elif defined(XCB_USE_XINPUT2)
finalizeXInput2();
#endif
@@ -602,11 +612,16 @@ 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
+#ifdef XCB_USE_XINPUT2_MAEMO
+ case GenericEvent:
+ handleGenericEventMaemo((xcb_ge_event_t*)event);
+ break;
+#elif defined(XCB_USE_XINPUT2)
case GenericEvent:
- handleGenericEvent((xcb_ge_event_t*)event);
+ if (m_xi2Enabled)
+ xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event));
break;
- #endif
+#endif
default:
handled = false;
break;
@@ -951,15 +966,6 @@ static const char * xcb_atomnames = {
"_XEMBED\0"
"_XEMBED_INFO\0"
- // Wacom old. (before version 0.10)
- "Wacom Stylus\0"
- "Wacom Cursor\0"
- "Wacom Eraser\0"
-
- // Tablet
- "STYLUS\0"
- "ERASER\0"
-
// XInput2
"Button Left\0"
"Button Middle\0"
@@ -975,6 +981,16 @@ static const char * xcb_atomnames = {
"Abs MT Pressure\0"
"Abs MT Tracking ID\0"
"Max Contacts\0"
+ // XInput2 tablet
+ "Abs X\0"
+ "Abs Y\0"
+ "Abs Pressure\0"
+ "Abs Tilt X\0"
+ "Abs Tilt Y\0"
+ "Abs Wheel\0"
+ "Abs Distance\0"
+ "Wacom Serial IDs\0"
+ "INTEGER\0"
#if XCB_USE_MAEMO_WINDOW_PROPERTIES
"_MEEGOTOUCH_ORIENTATION_ANGLE\0"
#endif
@@ -1245,4 +1261,53 @@ bool QXcbConnection::hasSupportForDri2() const
}
#endif //XCB_USE_DRI2
+#if defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO)
+// Borrowed from libXi.
+int QXcbConnection::xi2CountBits(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;
+}
+
+bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value)
+{
+ xXIDeviceEvent *xideviceevent = static_cast<xXIDeviceEvent *>(event);
+ 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 = xi2CountBits(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;
+}
+
+bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode)
+{
+ // xGenericEvent has "extension" on the second byte, xcb_ge_event_t has "pad0".
+ if (event->pad0 == 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, overwriting the full_sequence field.
+ memmove((char*) event + 32, (char*) event + 36, event->length * 4);
+ return true;
+ }
+ return false;
+}
+#endif // defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO)
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index 3b17f93917..2c7b11fb10 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -52,8 +52,12 @@
#include <QVector>
#include <QVarLengthArray>
+#ifndef QT_NO_TABLETEVENT
+#include <QTabletEvent>
+#endif
+
#ifdef XCB_USE_XINPUT2_MAEMO
-struct XInput2Data;
+struct XInput2MaemoData;
#endif
//#define Q_XCB_DEBUG
@@ -222,13 +226,6 @@ namespace QXcbAtom {
_XEMBED,
_XEMBED_INFO,
- XWacomStylus,
- XWacomCursor,
- XWacomEraser,
-
- XTabletStylus,
- XTabletEraser,
-
// XInput2
ButtonLeft,
ButtonMiddle,
@@ -244,6 +241,16 @@ namespace QXcbAtom {
AbsMTPressure,
AbsMTTrackingID,
MaxContacts,
+ // XInput2 tablet
+ AbsX,
+ AbsY,
+ AbsPressure,
+ AbsTiltX,
+ AbsTiltY,
+ AbsWheel,
+ AbsDistance,
+ WacomSerialIDs,
+ INTEGER,
#if XCB_USE_MAEMO_WINDOW_PROPERTIES
MeegoTouchOrientationAngle,
@@ -335,7 +342,9 @@ public:
void *egl_display() const { return m_egl_display; }
#endif
#ifdef XCB_USE_XINPUT2_MAEMO
- bool isUsingXInput2();
+ bool isUsingXInput2Maemo();
+#elif defined(XCB_USE_XINPUT2)
+ void xi2Select(xcb_window_t window);
#endif
void sync();
@@ -377,11 +386,48 @@ private:
void initializeDri2();
#endif
#ifdef XCB_USE_XINPUT2_MAEMO
+ void initializeXInput2Maemo();
+ void finalizeXInput2Maemo();
+ void handleGenericEventMaemo(xcb_ge_event_t *event);
+#endif
+ void handleClientMessageEvent(const xcb_client_message_event_t *event);
+
+ bool m_xi2Enabled;
+ int m_xi2Minor;
+#ifdef XCB_USE_XINPUT2
void initializeXInput2();
void finalizeXInput2();
- void handleGenericEvent(xcb_ge_event_t *event);
+ void xi2HandleEvent(xcb_ge_event_t *event);
+ int m_xiOpCode, m_xiEventBase, m_xiErrorBase;
+#ifndef QT_NO_TABLETEVENT
+ struct TabletData {
+ TabletData() : deviceId(0), down(false), serialId(0), inProximity(false) { }
+ int deviceId;
+ QTabletEvent::PointerType pointerType;
+ bool down;
+ qint64 serialId;
+ bool inProximity;
+ struct ValuatorClassInfo {
+ ValuatorClassInfo() : minVal(0), maxVal(0) { }
+ double minVal;
+ double maxVal;
+ int number;
+ };
+ QHash<int, ValuatorClassInfo> valuatorInfo;
+ };
+ void xi2QueryTabletData(void *dev, TabletData *tabletData); // use no XI stuff in headers
+ void xi2SetupTabletDevices();
+ void xi2HandleTabletEvent(void *event, TabletData *tabletData);
+ void xi2ReportTabletEvent(const TabletData &tabletData, void *event);
+ QVector<TabletData> m_tabletData;
+#endif
+#endif // XCB_USE_XINPUT2
+
+#if defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO)
+ static int xi2CountBits(unsigned char *ptr, int len);
+ static bool xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value);
+ static bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode);
#endif
- void handleClientMessageEvent(const xcb_client_message_event_t *event);
xcb_connection_t *m_connection;
const xcb_setup_t *m_setup;
@@ -412,7 +458,7 @@ private:
#endif
QXcbEventReader *m_reader;
#ifdef XCB_USE_XINPUT2_MAEMO
- XInput2Data *m_xinputData;
+ XInput2MaemoData *m_xinputData;
#endif
#ifdef XCB_USE_DRI2
uint32_t m_dri2_major;
diff --git a/src/plugins/platforms/xcb/qxcbconnection_maemo.cpp b/src/plugins/platforms/xcb/qxcbconnection_maemo.cpp
index 1339df78b4..463e119ebd 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_maemo.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_maemo.cpp
@@ -53,8 +53,8 @@ QT_BEGIN_NAMESPACE
// 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()
+struct XInput2MaemoData {
+ XInput2MaemoData()
: use_xinput(false)
, xinput_opcode(0)
, xinput_eventbase(0)
@@ -78,15 +78,15 @@ struct XInput2Data {
QTouchDevice *qtTouchDevice;
};
-bool QXcbConnection::isUsingXInput2()
+bool QXcbConnection::isUsingXInput2Maemo()
{
return m_xinputData && m_xinputData->use_xinput && m_xinputData->xiMaxContacts != 0;
}
-void QXcbConnection::initializeXInput2()
+void QXcbConnection::initializeXInput2Maemo()
{
Q_ASSERT(!m_xinputData);
- m_xinputData = new XInput2Data;
+ m_xinputData = new XInput2MaemoData;
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) {
@@ -156,7 +156,7 @@ void QXcbConnection::initializeXInput2()
}
}
-void QXcbConnection::finalizeXInput2()
+void QXcbConnection::finalizeXInput2Maemo()
{
if (m_xinputData && m_xinputData->xideviceinfo) {
XIFreeDeviceInfo(m_xinputData->xideviceinfo);
@@ -164,49 +164,9 @@ void QXcbConnection::finalizeXInput2()
delete m_xinputData;
}
-// Borrowed from libXi.
-static int count_bits(unsigned char* ptr, int len)
+void QXcbConnection::handleGenericEventMaemo(xcb_ge_event_t *event)
{
- 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);
+ if (m_xinputData->use_xinput && xi2PrepareXIGenericDeviceEvent(event, m_xinputData->xinput_opcode)) {
xXIGenericDeviceEvent* xievent = (xXIGenericDeviceEvent*)event;
// On Harmattan XInput2 is hacked to give touch points updates into standard mouse button press/motion events.
@@ -235,7 +195,7 @@ void QXcbConnection::handleGenericEvent(xcb_ge_event_t *event)
XIValuatorClassInfo *valuatorclassinfo = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
int n = valuatorclassinfo->number;
double value;
- if (!getValuatorValueIfSet(xideviceevent, n, &value))
+ if (!xi2GetValuatorValueIfSet(xideviceevent, n, &value))
continue;
if (valuatorclassinfo->label == atom(QXcbAtom::AbsMTPositionX)) {
@@ -306,4 +266,3 @@ QT_END_NAMESPACE
#endif // XCB_USE_XINPUT2_MAEMO
-
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
new file mode 100644
index 0000000000..026fd8c5af
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -0,0 +1,310 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** 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"
+#include "qxcbwindow.h"
+#include <QtGui/QWindowSystemInterface>
+
+#ifdef XCB_USE_XINPUT2
+
+#include <X11/extensions/XInput2.h>
+#include <X11/extensions/XI2proto.h>
+
+#ifndef QT_NO_TABLETEVENT
+static inline bool q_xi2_is_tablet(XIDeviceInfo *dev)
+{
+ QByteArray name(dev->name);
+ name = name.toLower();
+ // Cannot just check for "wacom" because that would also pick up the touch and tablet-button devices.
+ return name.contains("stylus") || name.contains("eraser");
+}
+#endif // QT_NO_TABLETEVENT
+
+void QXcbConnection::initializeXInput2()
+{
+ Display *xDisplay = static_cast<Display *>(m_xlib_display);
+ if (XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) {
+ int xiMajor = 2;
+ m_xi2Minor = 2; // try 2.2 first, needed for TouchBegin/Update/End
+ if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
+ m_xi2Minor = 0; // for tablet support 2.0 is enough
+ m_xi2Enabled = XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) != BadRequest;
+ } else {
+ m_xi2Enabled = true;
+ }
+
+ if (m_xi2Enabled) {
+#ifndef QT_NO_TABLETEVENT
+ // Tablet support: Figure out the stylus-related devices. We will
+ // only select events on this device. Press, motion, etc. events
+ // must never be selected for _all_ devices as that would render
+ // the standard XCB_MOTION_NOTIFY and similar handlers useless and
+ // we have no intention to infect all the pure xcb code with
+ // Xlib-based XI2.
+ xi2SetupTabletDevices();
+#endif // QT_NO_TABLETEVENT
+ }
+ }
+}
+
+void QXcbConnection::finalizeXInput2()
+{
+}
+
+void QXcbConnection::xi2Select(xcb_window_t window)
+{
+ if (!m_xi2Enabled)
+ return;
+
+#ifndef QT_NO_TABLETEVENT
+ // Tablets.
+ if (!m_tabletData.isEmpty()) {
+ Display *xDisplay = static_cast<Display *>(m_xlib_display);
+ QVector<XIEventMask> xiEventMask(m_tabletData.count());
+ unsigned int bitMask = 0;
+ unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
+ bitMask |= XI_ButtonPressMask;
+ bitMask |= XI_ButtonReleaseMask;
+ bitMask |= XI_MotionMask;
+ bitMask |= XI_PropertyEventMask;
+ for (int i = 0; i < m_tabletData.count(); ++i) {
+ xiEventMask[i].deviceid = m_tabletData.at(i).deviceId;
+ xiEventMask[i].mask_len = sizeof(bitMask);
+ xiEventMask[i].mask = xiBitMask;
+ }
+ XISelectEvents(xDisplay, window, xiEventMask.data(), m_tabletData.count());
+ }
+#endif // QT_NO_TABLETEVENT
+}
+
+void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
+{
+ if (xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) {
+ xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
+#ifndef QT_NO_TABLETEVENT
+ for (int i = 0; i < m_tabletData.count(); ++i) {
+ if (m_tabletData.at(i).deviceId == xiEvent->deviceid) {
+ xi2HandleTabletEvent(xiEvent, &m_tabletData[i]);
+ return;
+ }
+ }
+#endif // QT_NO_TABLETEVENT
+ }
+}
+
+#ifndef QT_NO_TABLETEVENT
+void QXcbConnection::xi2QueryTabletData(void *dev, TabletData *tabletData)
+{
+ XIDeviceInfo *device = static_cast<XIDeviceInfo *>(dev);
+ tabletData->deviceId = device->deviceid;
+
+ tabletData->pointerType = QTabletEvent::Pen;
+ if (QByteArray(device->name).toLower().contains("eraser"))
+ tabletData->pointerType = QTabletEvent::Eraser;
+
+ for (int i = 0; i < device->num_classes; ++i) {
+ switch (device->classes[i]->type) {
+ case XIValuatorClass: {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(device->classes[i]);
+ int val = 0;
+ if (vci->label == atom(QXcbAtom::AbsX))
+ val = QXcbAtom::AbsX;
+ else if (vci->label == atom(QXcbAtom::AbsY))
+ val = QXcbAtom::AbsY;
+ else if (vci->label == atom(QXcbAtom::AbsPressure))
+ val = QXcbAtom::AbsPressure;
+ else if (vci->label == atom(QXcbAtom::AbsTiltX))
+ val = QXcbAtom::AbsTiltX;
+ else if (vci->label == atom(QXcbAtom::AbsTiltY))
+ val = QXcbAtom::AbsTiltY;
+ else if (vci->label == atom(QXcbAtom::AbsWheel))
+ val = QXcbAtom::AbsWheel;
+ else if (vci->label == atom(QXcbAtom::AbsDistance))
+ val = QXcbAtom::AbsDistance;
+ if (val) {
+ TabletData::ValuatorClassInfo info;
+ info.minVal = vci->min;
+ info.maxVal = vci->max;
+ info.number = vci->number;
+ tabletData->valuatorInfo[val] = info;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void QXcbConnection::xi2SetupTabletDevices()
+{
+ Display *xDisplay = static_cast<Display *>(m_xlib_display);
+ m_tabletData.clear();
+ int deviceCount = 0;
+ XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount);
+ if (devices) {
+ for (int i = 0; i < deviceCount; ++i) {
+ int unused = 0;
+ XIDeviceInfo *dev = XIQueryDevice(xDisplay, devices[i].deviceid, &unused);
+ if (dev) {
+ if (q_xi2_is_tablet(dev)) {
+ TabletData tabletData;
+ xi2QueryTabletData(dev, &tabletData);
+ m_tabletData.append(tabletData);
+ }
+ XIFreeDeviceInfo(dev);
+ }
+ }
+ XIFreeDeviceInfo(devices);
+ }
+}
+
+void QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData)
+{
+ Display *xDisplay = static_cast<Display *>(m_xlib_display);
+ xXIGenericDeviceEvent *xiEvent = static_cast<xXIGenericDeviceEvent *>(event);
+ switch (xiEvent->evtype) {
+ case XI_ButtonPress: // stylus down
+ if (reinterpret_cast<xXIDeviceEvent *>(event)->detail == 1) { // ignore the physical buttons on the stylus
+ tabletData->down = true;
+ xi2ReportTabletEvent(*tabletData, xiEvent);
+ }
+ break;
+ case XI_ButtonRelease: // stylus up
+ if (reinterpret_cast<xXIDeviceEvent *>(event)->detail == 1) {
+ tabletData->down = false;
+ xi2ReportTabletEvent(*tabletData, xiEvent);
+ }
+ break;
+ case XI_Motion:
+ // Report TabletMove only when the stylus is touching the tablet.
+ // No possiblity to report proximity motion (no suitable Qt event exists yet).
+ if (tabletData->down)
+ xi2ReportTabletEvent(*tabletData, xiEvent);
+ break;
+ case XI_PropertyEvent: {
+ xXIPropertyEvent *ev = reinterpret_cast<xXIPropertyEvent *>(event);
+ if (ev->what == XIPropertyModified) {
+ if (ev->property == atom(QXcbAtom::WacomSerialIDs)) {
+ Atom propType;
+ int propFormat;
+ unsigned long numItems, bytesAfter;
+ unsigned char *data;
+ if (XIGetProperty(xDisplay, tabletData->deviceId, ev->property, 0, 100,
+ 0, AnyPropertyType, &propType, &propFormat,
+ &numItems, &bytesAfter, &data) == Success) {
+ if (propType == atom(QXcbAtom::INTEGER) && propFormat == 32) {
+ int *ptr = reinterpret_cast<int *>(data);
+ for (unsigned long i = 0; i < numItems; ++i)
+ tabletData->serialId |= qint64(ptr[i]) << (i * 32);
+ }
+ XFree(data);
+ }
+ // With recent-enough X drivers this property change event seems to come always
+ // when entering and leaving proximity. Due to the lack of other options hook up
+ // the enter/leave events to it.
+ if (tabletData->inProximity) {
+ tabletData->inProximity = false;
+ QWindowSystemInterface::handleTabletLeaveProximityEvent(QTabletEvent::Stylus,
+ tabletData->pointerType,
+ tabletData->serialId);
+ } else {
+ tabletData->inProximity = true;
+ QWindowSystemInterface::handleTabletEnterProximityEvent(QTabletEvent::Stylus,
+ tabletData->pointerType,
+ tabletData->serialId);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void QXcbConnection::xi2ReportTabletEvent(const TabletData &tabletData, void *event)
+{
+ xXIDeviceEvent *ev = reinterpret_cast<xXIDeviceEvent *>(event);
+ QXcbWindow *xcbWindow = platformWindowFromId(ev->event);
+ if (!xcbWindow)
+ return;
+ QWindow *window = xcbWindow->window();
+ const double scale = 65536.0;
+ QPointF local(ev->event_x / scale, ev->event_y / scale);
+ QPointF global(ev->root_x / scale, ev->root_y / scale);
+ double pressure = 0, rotation = 0;
+ int xTilt = 0, yTilt = 0;
+
+ for (QHash<int, TabletData::ValuatorClassInfo>::const_iterator it = tabletData.valuatorInfo.constBegin(),
+ ite = tabletData.valuatorInfo.constEnd(); it != ite; ++it) {
+ int valuator = it.key();
+ const TabletData::ValuatorClassInfo &classInfo(it.value());
+ double value;
+ if (xi2GetValuatorValueIfSet(event, classInfo.number, &value)) {
+ double normalizedValue = (value - classInfo.minVal) / double(classInfo.maxVal - classInfo.minVal);
+ switch (valuator) {
+ case QXcbAtom::AbsPressure:
+ pressure = normalizedValue;
+ break;
+ case QXcbAtom::AbsTiltX:
+ xTilt = value;
+ break;
+ case QXcbAtom::AbsTiltY:
+ yTilt = value;
+ break;
+ case QXcbAtom::AbsWheel:
+ rotation = value / 64.0;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ QWindowSystemInterface::handleTabletEvent(window, tabletData.down, local, global,
+ QTabletEvent::Stylus, tabletData.pointerType,
+ pressure, xTilt, yTilt, 0,
+ rotation, 0, tabletData.serialId);
+}
+#endif // QT_NO_TABLETEVENT
+
+#endif // XCB_USE_XINPUT2
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 4cc4d6f199..249c6cf0ef 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -103,7 +103,7 @@
#endif // QT_NO_SHAPE
#endif
-#ifdef XCB_USE_XINPUT2_MAEMO
+#if defined(XCB_USE_XINPUT2_MAEMO) || defined(XCB_USE_XINPUT2)
#include <X11/extensions/XInput2.h>
#endif
@@ -359,7 +359,7 @@ void QXcbWindow::create()
1, &leader));
#ifdef XCB_USE_XINPUT2_MAEMO
- if (connection()->isUsingXInput2()) {
+ if (connection()->isUsingXInput2Maemo()) {
XIEventMask xieventmask;
uchar bitmask[2] = { 0, 0 };
@@ -373,6 +373,8 @@ void QXcbWindow::create()
XISelectEvents(DISPLAY_FROM_XCB(this), m_window, &xieventmask, 1);
}
+#elif defined(XCB_USE_XINPUT2)
+ connection()->xi2Select(m_window);
#endif
setWindowFlags(window()->windowFlags());
diff --git a/src/plugins/platforms/xcb/xcb.pro b/src/plugins/platforms/xcb/xcb.pro
index 8e6fbc6c63..70992fc1c0 100644
--- a/src/plugins/platforms/xcb/xcb.pro
+++ b/src/plugins/platforms/xcb/xcb.pro
@@ -56,6 +56,12 @@ contains(QT_CONFIG, xcb-xlib) {
LIBS += -lXi
}
DEFINES += XCB_USE_MAEMO_WINDOW_PROPERTIES
+ } else {
+ contains(QT_CONFIG, xinput2) {
+ DEFINES += XCB_USE_XINPUT2
+ SOURCES += qxcbconnection_xi2.cpp
+ LIBS += -lXi
+ }
}
}