1
/****************************************************************************
3
** Copyright (C) 2016 The Qt Company Ltd.
4
** Contact: https://www.qt.io/licensing/
6
** This file is part of the plugins of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
9
** Commercial License Usage
10
** Licensees holding valid commercial Qt licenses may use this file in
11
** accordance with the commercial license agreement provided with the
12
** Software or, alternatively, in accordance with the terms contained in
13
** a written agreement between you and The Qt Company. For licensing terms
14
** and conditions see https://www.qt.io/terms-conditions. For further
15
** information use the contact form at https://www.qt.io/contact-us.
17
** GNU Lesser General Public License Usage
18
** Alternatively, this file may be used under the terms of the GNU Lesser
19
** General Public License version 3 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL3 included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 3 requirements
23
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25
** GNU General Public License Usage
26
** Alternatively, this file may be used under the terms of the GNU
27
** General Public License version 2.0 or (at your option) the GNU General
28
** Public license version 3 or any later version approved by the KDE Free
29
** Qt Foundation. The licenses are as published by the Free Software
30
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31
** included in the packaging of this file. Please review the following
32
** information to ensure the GNU General Public License requirements will
33
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34
** https://www.gnu.org/licenses/gpl-3.0.html.
38
****************************************************************************/
40
#include "qxcbconnection.h"
41
#include "qxcbkeyboard.h"
42
#include "qxcbscreen.h"
43
#include "qxcbwindow.h"
44
#include "qtouchdevice.h"
45
#include <qpa/qwindowsysteminterface_p.h>
49
#ifdef XCB_USE_XINPUT2
51
#include <X11/extensions/XInput2.h>
52
#include <X11/extensions/XI2proto.h>
54
struct XInput2TouchDeviceData {
55
XInput2TouchDeviceData()
58
, providesTouchOrientation(false)
61
XIDeviceInfo *xiDeviceInfo;
62
QTouchDevice *qtTouchDevice;
63
QHash<int, QWindowSystemInterface::TouchPoint> touchPoints;
65
// Stuff that is relevant only for touchpads
66
QHash<int, QPointF> pointPressedPosition; // in screen coordinates where each point was pressed
67
QPointF firstPressedPosition; // in screen coordinates where the first point was pressed
68
QPointF firstPressedNormalPosition; // device coordinates (0 to 1, 0 to 1) where the first point was pressed
69
QSizeF size; // device size in mm
70
bool providesTouchOrientation;
73
void QXcbConnection::initializeXInput2()
75
// TODO Qt 6 (or perhaps earlier): remove these redundant env variables
76
if (qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT"))
77
const_cast<QLoggingCategory&>(lcQpaXInput()).setEnabled(QtDebugMsg, true);
78
if (qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT_DEVICES"))
79
const_cast<QLoggingCategory&>(lcQpaXInputDevices()).setEnabled(QtDebugMsg, true);
80
Display *xDisplay = static_cast<Display *>(m_xlib_display);
81
if (XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) {
83
m_xi2Minor = 2; // try 2.2 first, needed for TouchBegin/Update/End
84
if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
85
m_xi2Minor = 1; // for smooth scrolling 2.1 is enough
86
if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
87
m_xi2Minor = 0; // for tablet support 2.0 is enough
88
m_xi2Enabled = XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) != BadRequest;
94
#ifdef XCB_USE_XINPUT22
95
qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.2 or greater", xiMajor, m_xi2Minor);
97
qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.0", xiMajor, m_xi2Minor);
105
void QXcbConnection::xi2SetupDevices()
107
#ifndef QT_NO_TABLETEVENT
108
m_tabletData.clear();
110
m_scrollingDevices.clear();
115
Display *xDisplay = static_cast<Display *>(m_xlib_display);
117
XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount);
118
for (int i = 0; i < deviceCount; ++i) {
119
// Only non-master pointing devices are relevant here.
120
if (devices[i].use != XISlavePointer)
122
qCDebug(lcQpaXInputDevices) << "input device " << devices[i].name << "ID" << devices[i].deviceid;
123
#ifndef QT_NO_TABLETEVENT
124
TabletData tabletData;
126
ScrollingDevice scrollingDevice;
127
for (int c = 0; c < devices[i].num_classes; ++c) {
128
switch (devices[i].classes[c]->type) {
129
case XIValuatorClass: {
130
XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(devices[i].classes[c]);
131
const int valuatorAtom = qatom(vci->label);
132
qCDebug(lcQpaXInputDevices) << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms);
133
#ifndef QT_NO_TABLETEVENT
134
if (valuatorAtom < QXcbAtom::NAtoms) {
135
TabletData::ValuatorClassInfo info;
136
info.minVal = vci->min;
137
info.maxVal = vci->max;
138
info.number = vci->number;
139
tabletData.valuatorInfo[valuatorAtom] = info;
141
#endif // QT_NO_TABLETEVENT
142
if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
143
scrollingDevice.lastScrollPosition.setX(vci->value);
144
else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
145
scrollingDevice.lastScrollPosition.setY(vci->value);
148
#ifdef XCB_USE_XINPUT21
149
case XIScrollClass: {
150
XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(devices[i].classes[c]);
151
if (sci->scroll_type == XIScrollTypeVertical) {
152
scrollingDevice.orientations |= Qt::Vertical;
153
scrollingDevice.verticalIndex = sci->number;
154
scrollingDevice.verticalIncrement = sci->increment;
156
else if (sci->scroll_type == XIScrollTypeHorizontal) {
157
scrollingDevice.orientations |= Qt::Horizontal;
158
scrollingDevice.horizontalIndex = sci->number;
159
scrollingDevice.horizontalIncrement = sci->increment;
163
case XIButtonClass: {
164
XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(devices[i].classes[c]);
165
if (bci->num_buttons >= 5) {
166
Atom label4 = bci->labels[3];
167
Atom label5 = bci->labels[4];
168
// Some drivers have no labels on the wheel buttons, some have no label on just one and some have no label on
169
// button 4 and the wrong one on button 5. So we just check that they are not labelled with unrelated buttons.
170
if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp || qatom(label4) == QXcbAtom::ButtonWheelDown) &&
171
(!label5 || qatom(label5) == QXcbAtom::ButtonWheelUp || qatom(label5) == QXcbAtom::ButtonWheelDown))
172
scrollingDevice.legacyOrientations |= Qt::Vertical;
174
if (bci->num_buttons >= 7) {
175
Atom label6 = bci->labels[5];
176
Atom label7 = bci->labels[6];
177
if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight))
178
scrollingDevice.legacyOrientations |= Qt::Horizontal;
180
qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons);
185
qCDebug(lcQpaXInputDevices) << " it's a keyboard";
187
#ifdef XCB_USE_XINPUT22
189
// will be handled in deviceForId()
193
qCDebug(lcQpaXInputDevices) << " has class" << devices[i].classes[c]->type;
197
bool isTablet = false;
198
#ifndef QT_NO_TABLETEVENT
199
// If we have found the valuators which we expect a tablet to have, it might be a tablet.
200
if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) &&
201
tabletData.valuatorInfo.contains(QXcbAtom::AbsY) &&
202
tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure))
205
// But we need to be careful not to take the touch and tablet-button devices as tablets.
206
QByteArray name = QByteArray(devices[i].name).toLower();
207
QString dbgType = QLatin1String("UNKNOWN");
208
if (name.contains("eraser")) {
210
tabletData.pointerType = QTabletEvent::Eraser;
211
dbgType = QLatin1String("eraser");
212
} else if (name.contains("cursor")) {
214
tabletData.pointerType = QTabletEvent::Cursor;
215
dbgType = QLatin1String("cursor");
216
} else if ((name.contains("pen") || name.contains("stylus")) && isTablet) {
217
tabletData.pointerType = QTabletEvent::Pen;
218
dbgType = QLatin1String("pen");
219
} else if (name.contains("wacom") && isTablet && !name.contains("touch")) {
220
// combined device (evdev) rather than separate pen/eraser (wacom driver)
221
tabletData.pointerType = QTabletEvent::Pen;
222
dbgType = QLatin1String("pen");
223
} else if (name.contains("aiptek") /* && device == QXcbAtom::KEYBOARD */) {
224
// some "Genius" tablets
226
tabletData.pointerType = QTabletEvent::Pen;
227
dbgType = QLatin1String("pen");
228
} else if (name.contains("waltop") && name.contains("tablet")) {
229
// other "Genius" tablets
230
// WALTOP International Corp. Slim Tablet
232
tabletData.pointerType = QTabletEvent::Pen;
233
dbgType = QLatin1String("pen");
239
tabletData.deviceId = devices[i].deviceid;
240
m_tabletData.append(tabletData);
241
qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << dbgType;
243
#endif // QT_NO_TABLETEVENT
245
#ifdef XCB_USE_XINPUT21
246
if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) {
247
scrollingDevice.deviceId = devices[i].deviceid;
248
// Only use legacy wheel button events when we don't have real scroll valuators.
249
scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations;
250
m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice);
251
qCDebug(lcQpaXInputDevices) << " it's a scrolling device";
256
// touchDeviceForId populates XInput2DeviceData the first time it is called
257
// with a new deviceId. On subsequent calls it will return the cached object.
258
XInput2TouchDeviceData *dev = touchDeviceForId(devices[i].deviceid);
259
if (dev && lcQpaXInputDevices().isDebugEnabled()) {
260
if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen)
261
qCDebug(lcQpaXInputDevices, " it's a touchscreen with type %d capabilities 0x%X max touch points %d",
262
dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
263
dev->qtTouchDevice->maximumTouchPoints());
264
else if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad)
265
qCDebug(lcQpaXInputDevices, " it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f",
266
dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
267
dev->qtTouchDevice->maximumTouchPoints(),
268
dev->size.width(), dev->size.height());
272
XIFreeDeviceInfo(devices);
275
void QXcbConnection::finalizeXInput2()
277
for (XInput2TouchDeviceData *dev : qAsConst(m_touchDevices)) {
278
if (dev->xiDeviceInfo)
279
XIFreeDeviceInfo(dev->xiDeviceInfo);
284
void QXcbConnection::xi2Select(xcb_window_t window)
286
if (!m_xi2Enabled || window == rootWindow())
289
Display *xDisplay = static_cast<Display *>(m_xlib_display);
290
unsigned int bitMask = 0;
291
unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
293
#ifdef XCB_USE_XINPUT22
294
if (isAtLeastXI22()) {
295
bitMask |= XI_TouchBeginMask;
296
bitMask |= XI_TouchUpdateMask;
297
bitMask |= XI_TouchEndMask;
298
bitMask |= XI_PropertyEventMask; // for tablets
299
if (xi2MouseEvents()) {
300
// We want both mouse and touch through XI2 if touch is supported (>= 2.2).
301
// The plain xcb press and motion events will not be delivered after this.
302
bitMask |= XI_ButtonPressMask;
303
bitMask |= XI_ButtonReleaseMask;
304
bitMask |= XI_MotionMask;
306
// There is a check for enter/leave events in plain xcb enter/leave event handler
307
bitMask |= XI_EnterMask;
308
bitMask |= XI_LeaveMask;
310
qCDebug(lcQpaXInput, "XInput 2.2: Selecting press/release/motion events in addition to touch");
313
mask.mask_len = sizeof(bitMask);
314
mask.mask = xiBitMask;
315
// When xi2MouseEvents() is true (the default), pointer emulation for touch and tablet
316
// events will get disabled. This is preferable, as Qt Quick handles touch events
317
// directly, while for other applications QtGui synthesizes mouse events.
318
mask.deviceid = XIAllMasterDevices;
319
Status result = XISelectEvents(xDisplay, window, &mask, 1);
320
if (result == Success)
321
QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
323
qCDebug(lcQpaXInput, "XInput 2.2: failed to select pointer/touch events, window %x, result %d", window, result);
326
const bool pointerSelected = isAtLeastXI22() && xi2MouseEvents();
328
const bool pointerSelected = false;
329
#endif // XCB_USE_XINPUT22
331
QSet<int> tabletDevices;
332
#ifndef QT_NO_TABLETEVENT
333
if (!m_tabletData.isEmpty()) {
334
unsigned int tabletBitMask;
335
unsigned char *xiTabletBitMask = reinterpret_cast<unsigned char *>(&tabletBitMask);
336
QVector<XIEventMask> xiEventMask(m_tabletData.count());
337
tabletBitMask = XI_PropertyEventMask;
338
if (!pointerSelected)
339
tabletBitMask |= XI_ButtonPressMask | XI_ButtonReleaseMask | XI_MotionMask;
340
for (int i = 0; i < m_tabletData.count(); ++i) {
341
int deviceId = m_tabletData.at(i).deviceId;
342
tabletDevices.insert(deviceId);
343
xiEventMask[i].deviceid = deviceId;
344
xiEventMask[i].mask_len = sizeof(tabletBitMask);
345
xiEventMask[i].mask = xiTabletBitMask;
347
XISelectEvents(xDisplay, window, xiEventMask.data(), m_tabletData.count());
349
#endif // QT_NO_TABLETEVENT
351
#ifdef XCB_USE_XINPUT21
352
// Enable each scroll device
353
if (!m_scrollingDevices.isEmpty() && !pointerSelected) {
354
// Only when XI2 mouse events are not enabled, otherwise motion and release are selected already.
355
QVector<XIEventMask> xiEventMask(m_scrollingDevices.size());
356
unsigned int scrollBitMask;
357
unsigned char *xiScrollBitMask = reinterpret_cast<unsigned char *>(&scrollBitMask);
359
scrollBitMask = XI_MotionMask;
360
scrollBitMask |= XI_ButtonReleaseMask;
362
for (const ScrollingDevice& scrollingDevice : qAsConst(m_scrollingDevices)) {
363
if (tabletDevices.contains(scrollingDevice.deviceId))
364
continue; // All necessary events are already captured.
365
xiEventMask[i].deviceid = scrollingDevice.deviceId;
366
xiEventMask[i].mask_len = sizeof(scrollBitMask);
367
xiEventMask[i].mask = xiScrollBitMask;
370
XISelectEvents(xDisplay, window, xiEventMask.data(), i);
377
// Listen for hotplug events
378
XIEventMask xiEventMask;
379
bitMask = XI_HierarchyChangedMask;
380
bitMask |= XI_DeviceChangedMask;
381
xiEventMask.deviceid = XIAllDevices;
382
xiEventMask.mask_len = sizeof(bitMask);
383
xiEventMask.mask = xiBitMask;
384
XISelectEvents(xDisplay, window, &xiEventMask, 1);
388
XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
390
XInput2TouchDeviceData *dev = Q_NULLPTR;
391
QHash<int, XInput2TouchDeviceData*>::const_iterator devIt = m_touchDevices.constFind(id);
392
if (devIt != m_touchDevices.cend()) {
396
QTouchDevice::Capabilities caps = 0;
397
dev = new XInput2TouchDeviceData;
398
dev->xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), id, &nrDevices);
399
if (nrDevices <= 0) {
404
int maxTouchPoints = 1;
405
bool hasRelativeCoords = false;
406
for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
407
XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
408
switch (classinfo->type) {
409
#ifdef XCB_USE_XINPUT22
411
XITouchClassInfo *tci = reinterpret_cast<XITouchClassInfo *>(classinfo);
412
maxTouchPoints = tci->num_touches;
413
qCDebug(lcQpaXInputDevices, " has touch class with mode %d", tci->mode);
415
case XIDependentTouch:
416
type = QTouchDevice::TouchPad;
419
type = QTouchDevice::TouchScreen;
424
#endif // XCB_USE_XINPUT22
425
case XIValuatorClass: {
426
XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
427
// Some devices (mice) report a resolution of 0; they will be excluded later,
428
// for now just prevent a division by zero
429
const int vciResolution = vci->resolution ? vci->resolution : 1;
430
if (vci->label == atom(QXcbAtom::AbsMTPositionX))
431
caps |= QTouchDevice::Position | QTouchDevice::NormalizedPosition;
432
else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor))
433
caps |= QTouchDevice::Area;
434
else if (vci->label == atom(QXcbAtom::AbsMTOrientation))
435
dev->providesTouchOrientation = true;
436
else if (vci->label == atom(QXcbAtom::AbsMTPressure) || vci->label == atom(QXcbAtom::AbsPressure))
437
caps |= QTouchDevice::Pressure;
438
else if (vci->label == atom(QXcbAtom::RelX)) {
439
hasRelativeCoords = true;
440
dev->size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
441
} else if (vci->label == atom(QXcbAtom::RelY)) {
442
hasRelativeCoords = true;
443
dev->size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
444
} else if (vci->label == atom(QXcbAtom::AbsX)) {
445
caps |= QTouchDevice::Position;
446
dev->size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
447
} else if (vci->label == atom(QXcbAtom::AbsY)) {
448
caps |= QTouchDevice::Position;
449
dev->size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
457
if (type < 0 && caps && hasRelativeCoords) {
458
type = QTouchDevice::TouchPad;
459
if (dev->size.width() < 10 || dev->size.height() < 10 ||
460
dev->size.width() > 10000 || dev->size.height() > 10000)
461
dev->size = QSizeF(130, 110);
463
if (!isAtLeastXI22() || type == QTouchDevice::TouchPad)
464
caps |= QTouchDevice::MouseEmulation;
466
if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
467
dev->qtTouchDevice = new QTouchDevice;
468
dev->qtTouchDevice->setName(QString::fromUtf8(dev->xiDeviceInfo->name));
469
dev->qtTouchDevice->setType((QTouchDevice::DeviceType)type);
470
dev->qtTouchDevice->setCapabilities(caps);
471
dev->qtTouchDevice->setMaximumTouchPoints(maxTouchPoints);
473
QWindowSystemInterface::registerTouchDevice(dev->qtTouchDevice);
474
m_touchDevices[id] = dev;
476
XIFreeDeviceInfo(dev->xiDeviceInfo);
484
#if defined(XCB_USE_XINPUT21) || !defined(QT_NO_TABLETEVENT)
485
static inline qreal fixed1616ToReal(FP1616 val)
487
return qreal(val) / 0x10000;
489
#endif // defined(XCB_USE_XINPUT21) || !defined(QT_NO_TABLETEVENT)
491
void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
493
xi2PrepareXIGenericDeviceEvent(event);
494
xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
495
int sourceDeviceId = xiEvent->deviceid; // may be the master id
496
xXIDeviceEvent *xiDeviceEvent = 0;
497
xXIEnterEvent *xiEnterEvent = 0;
498
QXcbWindowEventListener *eventListener = 0;
500
switch (xiEvent->evtype) {
502
case XI_ButtonRelease:
504
#ifdef XCB_USE_XINPUT22
510
xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
511
eventListener = windowEventListenerFromId(xiDeviceEvent->event);
512
sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master
517
xiEnterEvent = reinterpret_cast<xXIEnterEvent *>(event);
518
eventListener = windowEventListenerFromId(xiEnterEvent->event);
519
sourceDeviceId = xiEnterEvent->sourceid; // use the actual device id instead of the master
522
case XI_HierarchyChanged:
523
xi2HandleHierachyEvent(xiEvent);
525
case XI_DeviceChanged:
526
xi2HandleDeviceChangedEvent(xiEvent);
534
if (eventListener->handleGenericEvent(reinterpret_cast<xcb_generic_event_t *>(event), &result))
538
#ifndef QT_NO_TABLETEVENT
540
QXcbConnection::TabletData *tablet = tabletDataForDevice(sourceDeviceId);
541
if (tablet && xi2HandleTabletEvent(xiEvent, tablet))
544
#endif // QT_NO_TABLETEVENT
546
#ifdef XCB_USE_XINPUT21
547
QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(sourceDeviceId);
548
if (device != m_scrollingDevices.end())
549
xi2HandleScrollEvent(xiEvent, device.value());
550
#endif // XCB_USE_XINPUT21
552
#ifdef XCB_USE_XINPUT22
554
switch (xiDeviceEvent->evtype) {
556
case XI_ButtonRelease:
558
if (xi2MouseEvents() && eventListener && !(xiDeviceEvent->flags & XIPointerEmulated))
559
eventListener->handleXIMouseEvent(event);
565
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
566
qCDebug(lcQpaXInputEvents, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x",
567
event->event_type, xiDeviceEvent->sequenceNumber, xiDeviceEvent->detail,
568
fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y),
569
fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y),xiDeviceEvent->event);
570
if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event))
571
xi2ProcessTouch(xiDeviceEvent, platformWindow);
574
} else if (xiEnterEvent && xi2MouseEvents() && eventListener) {
575
switch (xiEnterEvent->evtype) {
578
eventListener->handleXIEnterLeave(event);
582
#endif // XCB_USE_XINPUT22
585
#ifdef XCB_USE_XINPUT22
586
static qreal valuatorNormalized(double value, XIValuatorClassInfo *vci)
588
if (value > vci->max)
590
if (value < vci->min)
592
return (value - vci->min) / (vci->max - vci->min);
595
void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow)
597
xXIDeviceEvent *xiDeviceEvent = static_cast<xXIDeviceEvent *>(xiDevEvent);
598
XInput2TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
600
const bool firstTouch = dev->touchPoints.isEmpty();
601
if (xiDeviceEvent->evtype == XI_TouchBegin) {
602
QWindowSystemInterface::TouchPoint tp;
603
tp.id = xiDeviceEvent->detail % INT_MAX;
604
tp.state = Qt::TouchPointPressed;
606
dev->touchPoints[tp.id] = tp;
608
QWindowSystemInterface::TouchPoint &touchPoint = dev->touchPoints[xiDeviceEvent->detail];
609
QXcbScreen* screen = platformWindow->xcbScreen();
610
qreal x = fixed1616ToReal(xiDeviceEvent->root_x);
611
qreal y = fixed1616ToReal(xiDeviceEvent->root_y);
612
qreal nx = -1.0, ny = -1.0;
613
qreal w = 0.0, h = 0.0;
614
bool majorAxisIsY = touchPoint.area.height() > touchPoint.area.width();
615
for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
616
XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
617
if (classinfo->type == XIValuatorClass) {
618
XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
621
if (!xi2GetValuatorValueIfSet(xiDeviceEvent, n, &value))
623
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
624
qCDebug(lcQpaXInputEvents, " valuator %20s value %lf from range %lf -> %lf",
625
atomName(vci->label).constData(), value, vci->min, vci->max );
626
if (vci->label == atom(QXcbAtom::RelX)) {
627
nx = valuatorNormalized(value, vci);
628
} else if (vci->label == atom(QXcbAtom::RelY)) {
629
ny = valuatorNormalized(value, vci);
630
} else if (vci->label == atom(QXcbAtom::AbsX)) {
631
nx = valuatorNormalized(value, vci);
632
} else if (vci->label == atom(QXcbAtom::AbsY)) {
633
ny = valuatorNormalized(value, vci);
634
} else if (vci->label == atom(QXcbAtom::AbsMTPositionX)) {
635
nx = valuatorNormalized(value, vci);
636
} else if (vci->label == atom(QXcbAtom::AbsMTPositionY)) {
637
ny = valuatorNormalized(value, vci);
638
} else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) {
639
const qreal sw = screen->geometry().width();
640
const qreal sh = screen->geometry().height();
641
w = valuatorNormalized(value, vci) * std::sqrt(sw * sw + sh * sh);
642
} else if (vci->label == atom(QXcbAtom::AbsMTTouchMinor)) {
643
const qreal sw = screen->geometry().width();
644
const qreal sh = screen->geometry().height();
645
h = valuatorNormalized(value, vci) * std::sqrt(sw * sw + sh * sh);
646
} else if (vci->label == atom(QXcbAtom::AbsMTOrientation)) {
647
// Find the closest axis.
648
// 0 corresponds to the Y axis, vci->max to the X axis.
649
// Flipping over the Y axis and rotating by 180 degrees
650
// don't change the result, so normalize value to range
651
// [0, vci->max] first.
653
while (value > vci->max)
654
value -= 2 * vci->max;
656
majorAxisIsY = value < vci->max - value;
657
} else if (vci->label == atom(QXcbAtom::AbsMTPressure) ||
658
vci->label == atom(QXcbAtom::AbsPressure)) {
659
touchPoint.pressure = valuatorNormalized(value, vci);
663
// If any value was not updated, use the last-known value.
665
x = touchPoint.area.center().x();
666
nx = x / screen->geometry().width();
669
y = touchPoint.area.center().y();
670
ny = y / screen->geometry().height();
672
if (xiDeviceEvent->evtype != XI_TouchEnd) {
673
if (!dev->providesTouchOrientation) {
675
w = touchPoint.area.width();
679
w = qMax(touchPoint.area.width(), touchPoint.area.height());
681
h = qMin(touchPoint.area.width(), touchPoint.area.height());
687
switch (xiDeviceEvent->evtype) {
690
dev->firstPressedPosition = QPointF(x, y);
691
dev->firstPressedNormalPosition = QPointF(nx, ny);
693
dev->pointPressedPosition.insert(touchPoint.id, QPointF(x, y));
695
// Touches must be accepted when we are grabbing touch events. Otherwise the entire sequence
696
// will get replayed when the grab ends.
698
// XIAllowTouchEvents deadlocks with libXi < 1.7.4 (this has nothing to do with the XI2 versions like 2.2)
699
// http://lists.x.org/archives/xorg-devel/2014-July/043059.html
701
static bool allowTouchWarningShown = false;
702
if (!allowTouchWarningShown) {
703
allowTouchWarningShown = true;
704
qWarning("Skipping XIAllowTouchEvents() because it was not possible to detect libXi version at build time."
705
" Minimum libXi version required is 1.7.4."
706
" Expect issues with touch behavior.");
708
#elif LIBXI_MAJOR == 1 && (LIBXI_MINOR < 7 || (LIBXI_MINOR == 7 && LIBXI_PATCH < 4))
709
static bool allowTouchWarningShown = false;
710
if (!allowTouchWarningShown) {
711
allowTouchWarningShown = true;
712
qWarning("Skipping XIAllowTouchEvents() due to not having libXi >= 1.7.4."
713
" libXi version at build time was %d.%d.%d."
714
" Expect issues with touch behavior.", LIBXI_MAJOR, LIBXI_MINOR, LIBXI_PATCH);
717
XIAllowTouchEvents(static_cast<Display *>(m_xlib_display), xiDeviceEvent->deviceid,
718
xiDeviceEvent->detail, xiDeviceEvent->event, XIAcceptTouch);
723
if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
724
qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
725
dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
726
qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
727
dev->size.height() * screen->geometry().height() / screen->physicalSize().height();
728
x = dev->firstPressedPosition.x() + dx;
729
y = dev->firstPressedPosition.y() + dy;
730
touchPoint.state = Qt::TouchPointMoved;
731
} else if (touchPoint.area.center() != QPoint(x, y)) {
732
touchPoint.state = Qt::TouchPointMoved;
733
dev->pointPressedPosition[touchPoint.id] = QPointF(x, y);
737
touchPoint.state = Qt::TouchPointReleased;
738
if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
739
qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
740
dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
741
qreal dy = (ny - dev->firstPressedNormalPosition.y()) *
742
dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
743
x = dev->firstPressedPosition.x() + dx;
744
y = dev->firstPressedPosition.y() + dy;
746
dev->pointPressedPosition.remove(touchPoint.id);
748
touchPoint.area = QRectF(x - w/2, y - h/2, w, h);
749
touchPoint.normalPosition = QPointF(nx, ny);
751
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
752
qCDebug(lcQpaXInputEvents) << " touchpoint " << touchPoint.id << " state " << touchPoint.state << " pos norm " << touchPoint.normalPosition <<
753
" area " << touchPoint.area << " pressure " << touchPoint.pressure;
754
QWindowSystemInterface::handleTouchEvent(platformWindow->window(), xiDeviceEvent->time, dev->qtTouchDevice, dev->touchPoints.values());
755
if (touchPoint.state == Qt::TouchPointReleased)
756
// If a touchpoint was released, we can forget it, because the ID won't be reused.
757
dev->touchPoints.remove(touchPoint.id);
759
// Make sure that we don't send TouchPointPressed/Moved in more than one QTouchEvent
760
// with this touch point if the next XI2 event is about a different touch point.
761
touchPoint.state = Qt::TouchPointStationary;
764
bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
766
if (grab && !canGrab())
770
Display *xDisplay = static_cast<Display *>(xlib_display());
771
XIDeviceInfo *info = XIQueryDevice(xDisplay, XIAllMasterDevices, &num_devices);
776
unsigned char mask[XIMaskLen(XI_LASTEVENT)];
778
evmask.mask_len = sizeof(mask);
779
memset(mask, 0, sizeof(mask));
780
evmask.deviceid = XIAllMasterDevices;
782
XISetMask(mask, XI_ButtonPress);
783
XISetMask(mask, XI_ButtonRelease);
784
XISetMask(mask, XI_Motion);
785
XISetMask(mask, XI_Enter);
786
XISetMask(mask, XI_Leave);
787
XISetMask(mask, XI_TouchBegin);
788
XISetMask(mask, XI_TouchUpdate);
789
XISetMask(mask, XI_TouchEnd);
792
for (int i = 0; i < num_devices; i++) {
793
int id = info[i].deviceid, n = 0;
794
XIDeviceInfo *deviceInfo = XIQueryDevice(xDisplay, id, &n);
796
const bool grabbable = deviceInfo->use != XIMasterKeyboard;
797
XIFreeDeviceInfo(deviceInfo);
802
Status result = XIUngrabDevice(xDisplay, id, CurrentTime);
803
if (result != Success) {
805
qCDebug(lcQpaXInput, "XInput 2.2: failed to ungrab events for device %d (result %d)", id, result);
808
Status result = XIGrabDevice(xDisplay, id, w, CurrentTime, None, XIGrabModeAsync,
809
XIGrabModeAsync, False, &evmask);
810
if (result != Success) {
812
qCDebug(lcQpaXInput, "XInput 2.2: failed to grab events for device %d on window %x (result %d)", id, w, result);
817
XIFreeDeviceInfo(info);
823
#endif // XCB_USE_XINPUT22
825
void QXcbConnection::xi2HandleHierachyEvent(void *event)
827
xXIHierarchyEvent *xiEvent = reinterpret_cast<xXIHierarchyEvent *>(event);
828
// We only care about hotplugged devices
829
if (!(xiEvent->flags & (XISlaveRemoved | XISlaveAdded)))
832
// Reselect events for all event-listening windows.
833
for (auto it = m_mapper.cbegin(), end = m_mapper.cend(); it != end; ++it)
837
void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
839
xXIDeviceChangedEvent *xiEvent = reinterpret_cast<xXIDeviceChangedEvent *>(event);
841
// ### If a slave device changes (XIDeviceChange), we should probably run setup on it again.
842
if (xiEvent->reason != XISlaveSwitch)
845
#ifdef XCB_USE_XINPUT21
846
// This code handles broken scrolling device drivers that reset absolute positions
847
// when they are made active. Whenever a new slave device is made active the
848
// primary pointer sends a DeviceChanged event with XISlaveSwitch, and the new
849
// active slave in sourceid.
851
QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(xiEvent->sourceid);
852
if (device == m_scrollingDevices.end())
856
XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), xiEvent->sourceid, &nrDevices);
857
if (nrDevices <= 0) {
858
qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", xiEvent->sourceid);
861
updateScrollingDevice(*device, xiDeviceInfo->num_classes, xiDeviceInfo->classes);
862
XIFreeDeviceInfo(xiDeviceInfo);
866
void QXcbConnection::updateScrollingDevice(ScrollingDevice &scrollingDevice, int num_classes, void *classInfo)
868
#ifdef XCB_USE_XINPUT21
869
XIAnyClassInfo **classes = reinterpret_cast<XIAnyClassInfo**>(classInfo);
870
QPointF lastScrollPosition;
871
if (lcQpaXInput().isDebugEnabled())
872
lastScrollPosition = scrollingDevice.lastScrollPosition;
873
for (int c = 0; c < num_classes; ++c) {
874
if (classes[c]->type == XIValuatorClass) {
875
XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classes[c]);
876
const int valuatorAtom = qatom(vci->label);
877
if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
878
scrollingDevice.lastScrollPosition.setX(vci->value);
879
else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
880
scrollingDevice.lastScrollPosition.setY(vci->value);
883
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled() && lastScrollPosition != scrollingDevice.lastScrollPosition))
884
qCDebug(lcQpaXInputEvents, "scrolling device %d moved from (%f, %f) to (%f, %f)", scrollingDevice.deviceId,
885
lastScrollPosition.x(), lastScrollPosition.y(),
886
scrollingDevice.lastScrollPosition.x(),
887
scrollingDevice.lastScrollPosition.y());
889
Q_UNUSED(scrollingDevice);
890
Q_UNUSED(num_classes);
895
#ifdef XCB_USE_XINPUT21
896
void QXcbConnection::handleEnterEvent()
898
QHash<int, ScrollingDevice>::iterator it = m_scrollingDevices.begin();
899
const QHash<int, ScrollingDevice>::iterator end = m_scrollingDevices.end();
901
ScrollingDevice& scrollingDevice = it.value();
903
XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), scrollingDevice.deviceId, &nrDevices);
904
if (nrDevices <= 0) {
905
qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", scrollingDevice.deviceId);
906
it = m_scrollingDevices.erase(it);
909
updateScrollingDevice(scrollingDevice, xiDeviceInfo->num_classes, xiDeviceInfo->classes);
910
XIFreeDeviceInfo(xiDeviceInfo);
916
void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice)
918
#ifdef XCB_USE_XINPUT21
919
xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
921
if (xiEvent->evtype == XI_Motion && scrollingDevice.orientations) {
922
xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
923
if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) {
927
if (scrollingDevice.orientations & Qt::Vertical) {
928
if (xi2GetValuatorValueIfSet(xiDeviceEvent, scrollingDevice.verticalIndex, &value)) {
929
double delta = scrollingDevice.lastScrollPosition.y() - value;
930
scrollingDevice.lastScrollPosition.setY(value);
931
angleDelta.setY((delta / scrollingDevice.verticalIncrement) * 120);
932
// We do not set "pixel" delta if it is only measured in ticks.
933
if (scrollingDevice.verticalIncrement > 1)
934
rawDelta.setY(delta);
935
else if (scrollingDevice.verticalIncrement < -1)
936
rawDelta.setY(-delta);
939
if (scrollingDevice.orientations & Qt::Horizontal) {
940
if (xi2GetValuatorValueIfSet(xiDeviceEvent, scrollingDevice.horizontalIndex, &value)) {
941
double delta = scrollingDevice.lastScrollPosition.x() - value;
942
scrollingDevice.lastScrollPosition.setX(value);
943
angleDelta.setX((delta / scrollingDevice.horizontalIncrement) * 120);
944
// We do not set "pixel" delta if it is only measured in ticks.
945
if (scrollingDevice.horizontalIncrement > 1)
946
rawDelta.setX(delta);
947
else if (scrollingDevice.horizontalIncrement < -1)
948
rawDelta.setX(-delta);
951
if (!angleDelta.isNull()) {
952
QPoint local(fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y));
953
QPoint global(fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y));
954
Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective_mods);
955
if (modifiers & Qt::AltModifier) {
956
std::swap(angleDelta.rx(), angleDelta.ry());
957
std::swap(rawDelta.rx(), rawDelta.ry());
959
QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiEvent->time, local, global, rawDelta, angleDelta, modifiers);
962
} else if (xiEvent->evtype == XI_ButtonRelease && scrollingDevice.legacyOrientations) {
963
xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
964
if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) {
966
if (scrollingDevice.legacyOrientations & Qt::Vertical) {
967
if (xiDeviceEvent->detail == 4)
968
angleDelta.setY(120);
969
else if (xiDeviceEvent->detail == 5)
970
angleDelta.setY(-120);
972
if (scrollingDevice.legacyOrientations & Qt::Horizontal) {
973
if (xiDeviceEvent->detail == 6)
974
angleDelta.setX(120);
975
else if (xiDeviceEvent->detail == 7)
976
angleDelta.setX(-120);
978
if (!angleDelta.isNull()) {
979
QPoint local(fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y));
980
QPoint global(fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y));
981
Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective_mods);
982
if (modifiers & Qt::AltModifier)
983
std::swap(angleDelta.rx(), angleDelta.ry());
984
QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiEvent->time, local, global, QPoint(), angleDelta, modifiers);
990
Q_UNUSED(scrollingDevice);
991
#endif // XCB_USE_XINPUT21
994
Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b)
997
case 1: return Qt::LeftButton;
998
case 2: return Qt::MiddleButton;
999
case 3: return Qt::RightButton;
1000
// 4-7 are for scrolling
1003
if (b >= 8 && b <= Qt::MaxMouseButton)
1004
return static_cast<Qt::MouseButton>(Qt::BackButton << (b - 8));
1005
return Qt::NoButton;
1008
static QTabletEvent::TabletDevice toolIdToTabletDevice(quint32 toolId) {
1009
// keep in sync with wacom_intuos_inout() in Linux kernel driver wacom_wac.c
1014
case 0x913: /* Intuos3 Airbrush */
1015
case 0x91b: /* Intuos3 Airbrush Eraser */
1016
case 0x902: /* Intuos4/5 13HD/24HD Airbrush */
1017
case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
1018
case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */
1019
case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
1020
return QTabletEvent::Airbrush;
1021
case 0x007: /* Mouse 4D and 2D */
1024
return QTabletEvent::FourDMouse;
1025
case 0x017: /* Intuos3 2D Mouse */
1026
case 0x806: /* Intuos4 Mouse */
1027
case 0x096: /* Lens cursor */
1028
case 0x097: /* Intuos3 Lens cursor */
1029
case 0x006: /* Intuos4 Lens cursor */
1030
return QTabletEvent::Puck;
1031
case 0x885: /* Intuos3 Art Pen (Marker Pen) */
1032
case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */
1033
case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
1034
return QTabletEvent::RotationStylus;
1036
return QTabletEvent::NoDevice;
1038
return QTabletEvent::Stylus; // Safe default assumption if nonzero
1041
#ifndef QT_NO_TABLETEVENT
1042
bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletData)
1044
bool handled = true;
1045
Display *xDisplay = static_cast<Display *>(m_xlib_display);
1046
const xXIGenericDeviceEvent *xiEvent = static_cast<const xXIGenericDeviceEvent *>(event);
1047
const xXIDeviceEvent *xiDeviceEvent = reinterpret_cast<const xXIDeviceEvent *>(xiEvent);
1049
switch (xiEvent->evtype) {
1050
case XI_ButtonPress: {
1051
Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail);
1052
tabletData->buttons |= b;
1053
xi2ReportTabletEvent(xiEvent, tabletData);
1056
case XI_ButtonRelease: {
1057
Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail);
1058
tabletData->buttons ^= b;
1059
xi2ReportTabletEvent(xiEvent, tabletData);
1063
// Report TabletMove only when the stylus is touching the tablet or any button is pressed.
1064
// TODO: report proximity (hover) motion (no suitable Qt event exists yet).
1065
if (tabletData->buttons != Qt::NoButton)
1066
xi2ReportTabletEvent(xiEvent, tabletData);
1068
case XI_PropertyEvent: {
1069
// This is the wacom driver's way of reporting tool proximity.
1070
// The evdev driver doesn't do it this way.
1071
const xXIPropertyEvent *ev = reinterpret_cast<const xXIPropertyEvent *>(event);
1072
if (ev->what == XIPropertyModified) {
1073
if (ev->property == atom(QXcbAtom::WacomSerialIDs)) {
1074
enum WacomSerialIndex {
1076
_WACSER_LAST_TOOL_SERIAL,
1077
_WACSER_LAST_TOOL_ID,
1078
_WACSER_TOOL_SERIAL,
1084
unsigned long numItems, bytesAfter;
1085
unsigned char *data;
1086
if (XIGetProperty(xDisplay, tabletData->deviceId, ev->property, 0, 100,
1087
0, AnyPropertyType, &propType, &propFormat,
1088
&numItems, &bytesAfter, &data) == Success) {
1089
if (propType == atom(QXcbAtom::INTEGER) && propFormat == 32 && numItems == _WACSER_COUNT) {
1090
quint32 *ptr = reinterpret_cast<quint32 *>(data);
1091
quint32 tool = ptr[_WACSER_TOOL_ID];
1092
// Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/
1093
// e.g. on Thinkpad Helix, tool ID will be 0 and serial will be 1
1094
if (!tool && ptr[_WACSER_TOOL_SERIAL])
1095
tool = ptr[_WACSER_TOOL_SERIAL];
1097
// The property change event informs us which tool is in proximity or which one left proximity.
1099
tabletData->inProximity = true;
1100
tabletData->tool = toolIdToTabletDevice(tool);
1101
tabletData->serialId = qint64(ptr[_WACSER_USB_ID]) << 32 | qint64(ptr[_WACSER_TOOL_SERIAL]);
1102
QWindowSystemInterface::handleTabletEnterProximityEvent(ev->time,
1103
tabletData->tool, tabletData->pointerType, tabletData->serialId);
1105
tabletData->inProximity = false;
1106
tabletData->tool = toolIdToTabletDevice(ptr[_WACSER_LAST_TOOL_ID]);
1107
// Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/
1108
// e.g. on Thinkpad Helix, tool ID will be 0 and serial will be 1
1109
if (!tabletData->tool)
1110
tabletData->tool = toolIdToTabletDevice(ptr[_WACSER_LAST_TOOL_SERIAL]);
1111
tabletData->serialId = qint64(ptr[_WACSER_USB_ID]) << 32 | qint64(ptr[_WACSER_LAST_TOOL_SERIAL]);
1112
QWindowSystemInterface::handleTabletLeaveProximityEvent(ev->time,
1113
tabletData->tool, tabletData->pointerType, tabletData->serialId);
1115
// TODO maybe have a hash of tabletData->deviceId to device data so we can
1116
// look up the tablet name here, and distinguish multiple tablets
1117
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
1118
qCDebug(lcQpaXInputEvents, "XI2 proximity change on tablet %d (USB %x): last tool: %x id %x current tool: %x id %x TabletDevice %d",
1119
tabletData->deviceId, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID],
1120
ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], tabletData->tool);
1136
void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletData)
1138
const xXIDeviceEvent *ev = reinterpret_cast<const xXIDeviceEvent *>(event);
1139
QXcbWindow *xcbWindow = platformWindowFromId(ev->event);
1142
QWindow *window = xcbWindow->window();
1143
const double scale = 65536.0;
1144
QPointF local(ev->event_x / scale, ev->event_y / scale);
1145
QPointF global(ev->root_x / scale, ev->root_y / scale);
1146
double pressure = 0, rotation = 0, tangentialPressure = 0;
1147
int xTilt = 0, yTilt = 0;
1149
for (QHash<int, TabletData::ValuatorClassInfo>::iterator it = tabletData->valuatorInfo.begin(),
1150
ite = tabletData->valuatorInfo.end(); it != ite; ++it) {
1151
int valuator = it.key();
1152
TabletData::ValuatorClassInfo &classInfo(it.value());
1153
xi2GetValuatorValueIfSet(event, classInfo.number, &classInfo.curVal);
1154
double normalizedValue = (classInfo.curVal - classInfo.minVal) / (classInfo.maxVal - classInfo.minVal);
1156
case QXcbAtom::AbsPressure:
1157
pressure = normalizedValue;
1159
case QXcbAtom::AbsTiltX:
1160
xTilt = classInfo.curVal;
1162
case QXcbAtom::AbsTiltY:
1163
yTilt = classInfo.curVal;
1165
case QXcbAtom::AbsWheel:
1166
switch (tabletData->tool) {
1167
case QTabletEvent::Airbrush:
1168
tangentialPressure = normalizedValue * 2.0 - 1.0; // Convert 0..1 range to -1..+1 range
1170
case QTabletEvent::RotationStylus:
1171
rotation = normalizedValue * 360.0 - 180.0; // Convert 0..1 range to -180..+180 degrees
1173
default: // Other types of styli do not use this valuator
1182
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
1183
qCDebug(lcQpaXInputEvents, "XI2 event on tablet %d with tool %d type %d seq %d detail %d time %d "
1184
"pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf",
1185
tabletData->deviceId, tabletData->tool, ev->evtype, ev->sequenceNumber, ev->detail, ev->time,
1186
fixed1616ToReal(ev->event_x), fixed1616ToReal(ev->event_y),
1187
fixed1616ToReal(ev->root_x), fixed1616ToReal(ev->root_y),
1188
(int)tabletData->buttons, pressure, xTilt, yTilt, rotation);
1190
QWindowSystemInterface::handleTabletEvent(window, ev->time, local, global,
1191
tabletData->tool, tabletData->pointerType,
1192
tabletData->buttons, pressure,
1193
xTilt, yTilt, tangentialPressure,
1194
rotation, 0, tabletData->serialId);
1197
QXcbConnection::TabletData *QXcbConnection::tabletDataForDevice(int id)
1199
for (int i = 0; i < m_tabletData.count(); ++i) {
1200
if (m_tabletData.at(i).deviceId == id)
1201
return &m_tabletData[i];
1206
#endif // QT_NO_TABLETEVENT
1208
#endif // XCB_USE_XINPUT2