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>
This commit is contained in:
parent
ba89230596
commit
26c85a5f79
5
configure
vendored
5
configure
vendored
@ -4648,8 +4648,7 @@ if [ "$CFG_XCB" != "no" ]; then
|
||||
QT_CONFIG="$QT_CONFIG xcb-xlib"
|
||||
fi
|
||||
|
||||
if [ "$XPLATFORM_MAEMO" = "yes" ]; then
|
||||
# auto-detect XInput2/Xinput support
|
||||
# auto-detect XInput2 support. Needed by xcb too.
|
||||
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 $D_FLAGS $l_FLAGS; then
|
||||
CFG_XINPUT2=yes
|
||||
@ -4666,7 +4665,6 @@ if [ "$CFG_XCB" != "no" ]; then
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if [ "$CFG_XCB" = "yes" ]; then
|
||||
echo "The XCB test failed!"
|
||||
@ -6052,6 +6050,7 @@ echo "Xcursor support ........ $CFG_XCURSOR"
|
||||
echo "Xfixes support ......... $CFG_XFIXES"
|
||||
echo "Xrandr support ......... $CFG_XRANDR"
|
||||
echo "Xi support ............. $CFG_XINPUT"
|
||||
echo "Xi2 support ............ $CFG_XINPUT2"
|
||||
echo "MIT-SHM support ........ $CFG_MITSHM"
|
||||
echo "FontConfig support ..... $CFG_FONTCONFIG"
|
||||
echo "XKB Support ............ $CFG_XKB"
|
||||
|
@ -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
|
||||
|
||||
@ -604,7 +614,12 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
|
||||
break;
|
||||
#ifdef XCB_USE_XINPUT2_MAEMO
|
||||
case GenericEvent:
|
||||
handleGenericEvent((xcb_ge_event_t*)event);
|
||||
handleGenericEventMaemo((xcb_ge_event_t*)event);
|
||||
break;
|
||||
#elif defined(XCB_USE_XINPUT2)
|
||||
case GenericEvent:
|
||||
if (m_xi2Enabled)
|
||||
xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@ -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
|
||||
|
@ -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,12 +386,49 @@ private:
|
||||
void initializeDri2();
|
||||
#endif
|
||||
#ifdef XCB_USE_XINPUT2_MAEMO
|
||||
void initializeXInput2();
|
||||
void finalizeXInput2();
|
||||
void handleGenericEvent(xcb_ge_event_t *event);
|
||||
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 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
|
||||
|
||||
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;
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
310
src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
Normal file
310
src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
Normal file
@ -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
|
@ -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());
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user