diff options
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 9 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.h | 17 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection_xi2.cpp | 458 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbintegration.cpp | 5 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbkeyboard.cpp | 9 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbscreen.cpp | 6 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.cpp | 1 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/xcb-plugin.pro | 1 |
8 files changed, 315 insertions, 191 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 1b72bb0da1..7f23c84cb9 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -1028,6 +1028,15 @@ void QXcbEventReader::registerForEvents() connect(dispatcher, SIGNAL(awake()), m_connection, SLOT(processXcbEvents())); } +void QXcbEventReader::registerEventDispatcher(QAbstractEventDispatcher *dispatcher) +{ + // flush the xcb connection before the EventDispatcher is going to block + // In the non-threaded case processXcbEvents is called before going to block, + // which flushes the connection. + if (m_xcb_poll_for_queued_event) + connect(dispatcher, SIGNAL(aboutToBlock()), m_connection, SLOT(flush())); +} + void QXcbEventReader::run() { xcb_generic_event_t *event; diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index f96541318c..60a4efff4e 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -311,6 +311,8 @@ public: void start(); + void registerEventDispatcher(QAbstractEventDispatcher *dispatcher); + signals: void eventPending(); @@ -410,7 +412,6 @@ public: void sync(); - void flush() { xcb_flush(m_connection); } void handleXcbError(xcb_generic_error_t *error); void handleXcbEvent(xcb_generic_event_t *event); @@ -464,8 +465,11 @@ public: void handleEnterEvent(const xcb_enter_notify_event_t *); #endif + QXcbEventReader *eventReader() const { return m_reader; } + public slots: void syncWindow(QXcbWindow *window); + void flush() { xcb_flush(m_connection); } private slots: void processXcbEvents(); @@ -496,27 +500,32 @@ private: #ifdef XCB_USE_XINPUT2 void initializeXInput2(); void finalizeXInput2(); + void xi2SetupDevices(); XInput2DeviceData *deviceForId(int id); void xi2HandleEvent(xcb_ge_event_t *event); + void xi2HandleHierachyEvent(void *event); int m_xiOpCode, m_xiEventBase, m_xiErrorBase; #ifndef QT_NO_TABLETEVENT struct TabletData { - TabletData() : deviceId(0), down(false), serialId(0), inProximity(false) { } + TabletData() : deviceId(0), pointerType(QTabletEvent::UnknownPointer), + tool(QTabletEvent::Stylus), down(false), serialId(0), inProximity(false) { } int deviceId; QTabletEvent::PointerType pointerType; + QTabletEvent::TabletDevice tool; bool down; qint64 serialId; bool inProximity; struct ValuatorClassInfo { - ValuatorClassInfo() : minVal(0), maxVal(0) { } + ValuatorClassInfo() : minVal(0.), maxVal(0.), curVal(0.) { } double minVal; double maxVal; + double curVal; int number; }; QHash<int, ValuatorClassInfo> valuatorInfo; }; bool xi2HandleTabletEvent(void *event, TabletData *tabletData); - void xi2ReportTabletEvent(const TabletData &tabletData, void *event); + void xi2ReportTabletEvent(TabletData &tabletData, void *event); QVector<TabletData> m_tabletData; #endif struct ScrollingDevice { diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index eb7b220c43..e21db89a20 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -73,10 +73,6 @@ void QXcbConnection::initializeXInput2() { debug_xinput = qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT"); debug_xinput_devices = qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT_DEVICES"); -#ifndef QT_NO_TABLETEVENT - m_tabletData.clear(); -#endif - m_scrollingDevices.clear(); Display *xDisplay = static_cast<Display *>(m_xlib_display); if (XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) { int xiMajor = 2; @@ -97,126 +93,146 @@ void QXcbConnection::initializeXInput2() #else qDebug("XInput version %d.%d is available and Qt supports 2.0", xiMajor, m_xi2Minor); #endif - int deviceCount = 0; - XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount); - for (int i = 0; i < deviceCount; ++i) { - // Only non-master pointing devices are relevant here. - if (devices[i].use != XISlavePointer) - continue; - if (Q_UNLIKELY(debug_xinput_devices)) - qDebug() << "input device "<< devices[i].name; + } + + xi2SetupDevices(); + } +} + +void QXcbConnection::xi2SetupDevices() +{ #ifndef QT_NO_TABLETEVENT - TabletData tabletData; + m_tabletData.clear(); #endif - ScrollingDevice scrollingDevice; - for (int c = 0; c < devices[i].num_classes; ++c) { - switch (devices[i].classes[c]->type) { - case XIValuatorClass: { - XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(devices[i].classes[c]); - const int valuatorAtom = qatom(vci->label); - if (Q_UNLIKELY(debug_xinput_devices)) - qDebug() << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms); + m_scrollingDevices.clear(); + + if (!m_xi2Enabled) + return; + + Display *xDisplay = static_cast<Display *>(m_xlib_display); + int deviceCount = 0; + XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount); + for (int i = 0; i < deviceCount; ++i) { + // Only non-master pointing devices are relevant here. + if (devices[i].use != XISlavePointer) + continue; + if (Q_UNLIKELY(debug_xinput_devices)) + qDebug() << "input device "<< devices[i].name; #ifndef QT_NO_TABLETEVENT - if (valuatorAtom < QXcbAtom::NAtoms) { - TabletData::ValuatorClassInfo info; - info.minVal = vci->min; - info.maxVal = vci->max; - info.number = vci->number; - tabletData.valuatorInfo[valuatorAtom] = info; - } + TabletData tabletData; +#endif + ScrollingDevice scrollingDevice; + for (int c = 0; c < devices[i].num_classes; ++c) { + switch (devices[i].classes[c]->type) { + case XIValuatorClass: { + XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(devices[i].classes[c]); + const int valuatorAtom = qatom(vci->label); + if (Q_UNLIKELY(debug_xinput_devices)) + qDebug() << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms); +#ifndef QT_NO_TABLETEVENT + if (valuatorAtom < QXcbAtom::NAtoms) { + TabletData::ValuatorClassInfo info; + info.minVal = vci->min; + info.maxVal = vci->max; + info.number = vci->number; + tabletData.valuatorInfo[valuatorAtom] = info; + } #endif // QT_NO_TABLETEVENT - if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel) - scrollingDevice.lastScrollPosition.setX(vci->value); - else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel) - scrollingDevice.lastScrollPosition.setY(vci->value); - break; - } + if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel) + scrollingDevice.lastScrollPosition.setX(vci->value); + else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel) + scrollingDevice.lastScrollPosition.setY(vci->value); + break; + } #ifdef XCB_USE_XINPUT21 - case XIScrollClass: { - XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(devices[i].classes[c]); - if (sci->scroll_type == XIScrollTypeVertical) { - scrollingDevice.orientations |= Qt::Vertical; - scrollingDevice.verticalIndex = sci->number; - scrollingDevice.verticalIncrement = sci->increment; - } - else if (sci->scroll_type == XIScrollTypeHorizontal) { - scrollingDevice.orientations |= Qt::Horizontal; - scrollingDevice.horizontalIndex = sci->number; - scrollingDevice.horizontalIncrement = sci->increment; - } - break; - } - case XIButtonClass: { - XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(devices[i].classes[c]); - if (bci->num_buttons >= 5) { - Atom label4 = bci->labels[3]; - Atom label5 = bci->labels[4]; - if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp) && (!label5 || qatom(label5) == QXcbAtom::ButtonWheelDown)) - scrollingDevice.legacyOrientations |= Qt::Vertical; - } - if (bci->num_buttons >= 7) { - Atom label6 = bci->labels[5]; - Atom label7 = bci->labels[6]; - if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight)) - scrollingDevice.legacyOrientations |= Qt::Horizontal; - } - break; - } -#endif - default: - break; - } + case XIScrollClass: { + XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(devices[i].classes[c]); + if (sci->scroll_type == XIScrollTypeVertical) { + scrollingDevice.orientations |= Qt::Vertical; + scrollingDevice.verticalIndex = sci->number; + scrollingDevice.verticalIncrement = sci->increment; } - bool isTablet = false; -#ifndef QT_NO_TABLETEVENT - // If we have found the valuators which we expect a tablet to have, assume it's a tablet. - if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) && - tabletData.valuatorInfo.contains(QXcbAtom::AbsY) && - tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure)) { - tabletData.deviceId = devices[i].deviceid; - tabletData.pointerType = QTabletEvent::Pen; - if (QByteArray(devices[i].name).toLower().contains("eraser")) - tabletData.pointerType = QTabletEvent::Eraser; - m_tabletData.append(tabletData); - isTablet = true; - if (Q_UNLIKELY(debug_xinput_devices)) - qDebug() << " it's a tablet with pointer type" << tabletData.pointerType; + else if (sci->scroll_type == XIScrollTypeHorizontal) { + scrollingDevice.orientations |= Qt::Horizontal; + scrollingDevice.horizontalIndex = sci->number; + scrollingDevice.horizontalIncrement = sci->increment; } + break; + } + case XIButtonClass: { + XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(devices[i].classes[c]); + if (bci->num_buttons >= 5) { + Atom label4 = bci->labels[3]; + Atom label5 = bci->labels[4]; + if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp) && (!label5 || qatom(label5) == QXcbAtom::ButtonWheelDown)) + scrollingDevice.legacyOrientations |= Qt::Vertical; + } + if (bci->num_buttons >= 7) { + Atom label6 = bci->labels[5]; + Atom label7 = bci->labels[6]; + if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight)) + scrollingDevice.legacyOrientations |= Qt::Horizontal; + } + break; + } +#endif + default: + break; + } + } + bool isTablet = false; +#ifndef QT_NO_TABLETEVENT + // If we have found the valuators which we expect a tablet to have, assume it's a tablet. + if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) && + tabletData.valuatorInfo.contains(QXcbAtom::AbsY) && + tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure)) { + tabletData.deviceId = devices[i].deviceid; + tabletData.pointerType = QTabletEvent::Pen; + if (QByteArray(devices[i].name).toLower().contains("eraser")) + tabletData.pointerType = QTabletEvent::Eraser; + m_tabletData.append(tabletData); + isTablet = true; + if (Q_UNLIKELY(debug_xinput_devices)) + qDebug() << " it's a tablet with pointer type" << tabletData.pointerType; + } #endif // QT_NO_TABLETEVENT #ifdef XCB_USE_XINPUT21 - if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) { - scrollingDevice.deviceId = devices[i].deviceid; - // Only use legacy wheel button events when we don't have real scroll valuators. - scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations; - m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice); - if (Q_UNLIKELY(debug_xinput_devices)) - qDebug() << " it's a scrolling device"; - } + if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) { + scrollingDevice.deviceId = devices[i].deviceid; + // Only use legacy wheel button events when we don't have real scroll valuators. + scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations; + m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice); + if (Q_UNLIKELY(debug_xinput_devices)) + qDebug() << " it's a scrolling device"; + } #endif - if (!isTablet) { - XInput2DeviceData *dev = deviceForId(devices[i].deviceid); - if (Q_UNLIKELY(debug_xinput_devices)) { - if (dev && dev->qtTouchDevice->type() == QTouchDevice::TouchScreen) - qDebug(" it's a touchscreen with type %d capabilities 0x%X max touch points %d", - dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(), - dev->qtTouchDevice->maximumTouchPoints()); - else if (dev && dev->qtTouchDevice->type() == QTouchDevice::TouchPad) - qDebug(" it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f", - dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(), - dev->qtTouchDevice->maximumTouchPoints(), - dev->size.width(), dev->size.height()); - } - } + if (!isTablet) { + XInput2DeviceData *dev = deviceForId(devices[i].deviceid); + if (Q_UNLIKELY(debug_xinput_devices)) { + if (dev && dev->qtTouchDevice->type() == QTouchDevice::TouchScreen) + qDebug(" it's a touchscreen with type %d capabilities 0x%X max touch points %d", + dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(), + dev->qtTouchDevice->maximumTouchPoints()); + else if (dev && dev->qtTouchDevice->type() == QTouchDevice::TouchPad) + qDebug(" it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f", + dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(), + dev->qtTouchDevice->maximumTouchPoints(), + dev->size.width(), dev->size.height()); } - XIFreeDeviceInfo(devices); } } + XIFreeDeviceInfo(devices); } void QXcbConnection::finalizeXInput2() { + foreach (XInput2DeviceData *dev, m_touchDevices) { + if (dev->xiDeviceInfo) + XIFreeDeviceInfo(dev->xiDeviceInfo); + delete dev; + } } void QXcbConnection::xi2Select(xcb_window_t window) @@ -235,14 +251,13 @@ void QXcbConnection::xi2Select(xcb_window_t window) XIEventMask mask; mask.mask_len = sizeof(bitMask); mask.mask = xiBitMask; - // Enable each touchscreen - foreach (XInput2DeviceData *dev, m_touchDevices) { - mask.deviceid = dev->xiDeviceInfo->deviceid; + if (!m_touchDevices.isEmpty()) { + mask.deviceid = XIAllMasterDevices; Status result = XISelectEvents(xDisplay, window, &mask, 1); - // If we have XInput >= 2.2 and successfully enable a touchscreen, then - // it will provide touch only. In most other cases, there will be - // emulated mouse events from the driver. If not, then Qt must do its - // own mouse emulation to enable interaction with mouse-oriented QWidgets. + // If we select for touch events on the master pointer, XInput2 + // will not synthesize mouse events. This means Qt must do it, + // which is also preferable, since Qt can control better when + // to do so. if (m_xi2Minor >= 2 && result == Success) has_touch_without_mouse_emulation = true; } @@ -256,10 +271,10 @@ void QXcbConnection::xi2Select(xcb_window_t window) // similar handlers useless and we have no intention to infect // all the pure xcb code with Xlib-based XI2. if (!m_tabletData.isEmpty()) { - unsigned int tabletBitMask = bitMask; + unsigned int tabletBitMask; unsigned char *xiTabletBitMask = reinterpret_cast<unsigned char *>(&tabletBitMask); QVector<XIEventMask> xiEventMask(m_tabletData.count()); - tabletBitMask |= XI_ButtonPressMask; + tabletBitMask = XI_ButtonPressMask; tabletBitMask |= XI_ButtonReleaseMask; tabletBitMask |= XI_MotionMask; tabletBitMask |= XI_PropertyEventMask; @@ -278,24 +293,18 @@ void QXcbConnection::xi2Select(xcb_window_t window) // Enable each scroll device if (!m_scrollingDevices.isEmpty()) { QVector<XIEventMask> xiEventMask(m_scrollingDevices.size()); - unsigned int scrollBitMask = 0; + unsigned int scrollBitMask; unsigned char *xiScrollBitMask = reinterpret_cast<unsigned char *>(&scrollBitMask); + scrollBitMask = XI_MotionMask; scrollBitMask |= XI_ButtonReleaseMask; - bitMask |= XI_MotionMask; - bitMask |= XI_ButtonReleaseMask; int i=0; Q_FOREACH (const ScrollingDevice& scrollingDevice, m_scrollingDevices) { if (tabletDevices.contains(scrollingDevice.deviceId)) continue; // All necessary events are already captured. xiEventMask[i].deviceid = scrollingDevice.deviceId; - if (m_touchDevices.contains(scrollingDevice.deviceId)) { - xiEventMask[i].mask_len = sizeof(bitMask); - xiEventMask[i].mask = xiBitMask; - } else { - xiEventMask[i].mask_len = sizeof(scrollBitMask); - xiEventMask[i].mask = xiScrollBitMask; - } + xiEventMask[i].mask_len = sizeof(scrollBitMask); + xiEventMask[i].mask = xiScrollBitMask; i++; } XISelectEvents(xDisplay, window, xiEventMask.data(), i); @@ -303,6 +312,16 @@ void QXcbConnection::xi2Select(xcb_window_t window) #else Q_UNUSED(xiBitMask); #endif + + { + // Listen for hotplug events + XIEventMask xiEventMask; + bitMask = XI_HierarchyChangedMask; + xiEventMask.deviceid = XIAllDevices; + xiEventMask.mask_len = sizeof(bitMask); + xiEventMask.mask = xiBitMask; + XISelectEvents(xDisplay, window, &xiEventMask, 1); + } } XInput2DeviceData *QXcbConnection::deviceForId(int id) @@ -326,10 +345,10 @@ XInput2DeviceData *QXcbConnection::deviceForId(int id) if (Q_UNLIKELY(debug_xinput_devices)) qDebug(" has touch class with mode %d", tci->mode); switch (tci->mode) { - case XIModeRelative: + case XIDependentTouch: type = QTouchDevice::TouchPad; break; - case XIModeAbsolute: + case XIDirectTouch: type = QTouchDevice::TouchScreen; break; } @@ -372,6 +391,7 @@ XInput2DeviceData *QXcbConnection::deviceForId(int id) m_touchDevices[id] = dev; } else { m_touchDevices.remove(id); + XIFreeDeviceInfo(dev->xiDeviceInfo); delete dev; dev = 0; } @@ -402,6 +422,10 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) if (xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) { xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event); + if (xiEvent->evtype == XI_HierarchyChanged) { + xi2HandleHierachyEvent(xiEvent); + return; + } #ifndef QT_NO_TABLETEVENT for (int i = 0; i < m_tabletData.count(); ++i) { if (m_tabletData.at(i).deviceId == xiEvent->deviceid) { @@ -427,7 +451,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y) ); if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) { - XInput2DeviceData *dev = deviceForId(xiEvent->deviceid); + XInput2DeviceData *dev = deviceForId(xiDeviceEvent->sourceid); Q_ASSERT(dev); const bool firstTouch = m_touchPoints.isEmpty(); if (xiEvent->evtype == XI_TouchBegin) { @@ -532,22 +556,6 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) qDebug() << " touchpoint " << touchPoint.id << " state " << touchPoint.state << " pos norm " << touchPoint.normalPosition << " area " << touchPoint.area << " pressure " << touchPoint.pressure; QWindowSystemInterface::handleTouchEvent(platformWindow->window(), xiEvent->time, dev->qtTouchDevice, m_touchPoints.values()); - if (has_touch_without_mouse_emulation) { - // We need to grab the touch event to prevent mouse emulation. - if (xiEvent->evtype == XI_TouchBegin) { - XIEventMask xieventmask; - unsigned int bitMask = 0; - unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask); - xieventmask.deviceid = xiEvent->deviceid; - xieventmask.mask = xiBitMask; - xieventmask.mask_len = sizeof(bitMask); - bitMask |= XI_TouchBeginMask; - bitMask |= XI_TouchUpdateMask; - bitMask |= XI_TouchEndMask; - XIGrabDevice(static_cast<Display *>(m_xlib_display), xiEvent->deviceid, platformWindow->winId(), xiEvent->time, None, GrabModeAsync, GrabModeAsync, true, &xieventmask); - } else if (xiEvent->evtype == XI_TouchEnd) - XIUngrabDevice(static_cast<Display *>(m_xlib_display), xiEvent->deviceid, xiEvent->time); - } if (touchPoint.state == Qt::TouchPointReleased) // If a touchpoint was released, we can forget it, because the ID won't be reused. m_touchPoints.remove(touchPoint.id); @@ -561,6 +569,19 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) } } +void QXcbConnection::xi2HandleHierachyEvent(void *event) +{ + xXIHierarchyEvent *xiEvent = reinterpret_cast<xXIHierarchyEvent *>(event); + // We only care about hotplugged devices + if (!(xiEvent->flags & (XISlaveRemoved | XISlaveAdded))) + return; + xi2SetupDevices(); + // Reselect events for all event-listening windows. + Q_FOREACH (xcb_window_t window, m_mapper.keys()) { + xi2Select(window); + } +} + void QXcbConnection::handleEnterEvent(const xcb_enter_notify_event_t *) { #ifdef XCB_USE_XINPUT21 @@ -664,6 +685,39 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin #endif // XCB_USE_XINPUT21 } +static QTabletEvent::TabletDevice toolIdToTabletDevice(quint32 toolId) { + // keep in sync with wacom_intuos_inout() in Linux kernel driver wacom_wac.c + switch (toolId) { + case 0xd12: + case 0x912: + case 0x112: + case 0x913: /* Intuos3 Airbrush */ + case 0x91b: /* Intuos3 Airbrush Eraser */ + case 0x902: /* Intuos4/5 13HD/24HD Airbrush */ + case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ + case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */ + case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ + return QTabletEvent::Airbrush; + case 0x007: /* Mouse 4D and 2D */ + case 0x09c: + case 0x094: + return QTabletEvent::FourDMouse; + case 0x017: /* Intuos3 2D Mouse */ + case 0x806: /* Intuos4 Mouse */ + case 0x096: /* Lens cursor */ + case 0x097: /* Intuos3 Lens cursor */ + case 0x006: /* Intuos4 Lens cursor */ + return QTabletEvent::Puck; + case 0x885: /* Intuos3 Art Pen (Marker Pen) */ + case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */ + case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */ + return QTabletEvent::RotationStylus; + case 0: + return QTabletEvent::NoDevice; + } + return QTabletEvent::Stylus; // Safe default assumption if nonzero +} + #ifndef QT_NO_TABLETEVENT bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData) { @@ -692,9 +746,19 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData) xi2ReportTabletEvent(*tabletData, xiEvent); break; case XI_PropertyEvent: { + // This is the wacom driver's way of reporting tool proximity. + // The evdev driver doesn't do it this way. xXIPropertyEvent *ev = reinterpret_cast<xXIPropertyEvent *>(event); if (ev->what == XIPropertyModified) { if (ev->property == atom(QXcbAtom::WacomSerialIDs)) { + enum WacomSerialIndex { + _WACSER_USB_ID = 0, + _WACSER_LAST_TOOL_SERIAL, + _WACSER_LAST_TOOL_ID, + _WACSER_TOOL_SERIAL, + _WACSER_TOOL_ID, + _WACSER_COUNT + }; Atom propType; int propFormat; unsigned long numItems, bytesAfter; @@ -702,27 +766,44 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData) 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); + if (propType == atom(QXcbAtom::INTEGER) && propFormat == 32 && numItems == _WACSER_COUNT) { + quint32 *ptr = reinterpret_cast<quint32 *>(data); + quint32 tool = ptr[_WACSER_TOOL_ID]; + // Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/ + // e.g. on Thinkpad Helix, tool ID will be 0 and serial will be 1 + if (!tool && ptr[_WACSER_TOOL_SERIAL]) + tool = ptr[_WACSER_TOOL_SERIAL]; + + // The property change event informs us which tool is in proximity or which one left proximity. + if (tool) { + tabletData->inProximity = true; + tabletData->tool = toolIdToTabletDevice(tool); + tabletData->serialId = qint64(ptr[_WACSER_USB_ID]) << 32 | qint64(ptr[_WACSER_TOOL_SERIAL]); + QWindowSystemInterface::handleTabletEnterProximityEvent(tabletData->tool, + tabletData->pointerType, + tabletData->serialId); + } else { + tabletData->inProximity = false; + tabletData->tool = toolIdToTabletDevice(ptr[_WACSER_LAST_TOOL_ID]); + // Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/ + // e.g. on Thinkpad Helix, tool ID will be 0 and serial will be 1 + if (!tabletData->tool) + tabletData->tool = toolIdToTabletDevice(ptr[_WACSER_LAST_TOOL_SERIAL]); + tabletData->serialId = qint64(ptr[_WACSER_USB_ID]) << 32 | qint64(ptr[_WACSER_LAST_TOOL_SERIAL]); + QWindowSystemInterface::handleTabletLeaveProximityEvent(tabletData->tool, + tabletData->pointerType, + tabletData->serialId); + } + if (Q_UNLIKELY(debug_xinput)) { + // TODO maybe have a hash of tabletData->deviceId to device data so we can + // look up the tablet name here, and distinguish multiple tablets + qDebug("XI2 proximity change on tablet %d (USB %x): last tool: %x id %x current tool: %x id %x TabletDevice %d", + ev->deviceid, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID], + ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], tabletData->tool); + } } 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; @@ -734,7 +815,7 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData) return handled; } -void QXcbConnection::xi2ReportTabletEvent(const TabletData &tabletData, void *event) +void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event) { xXIDeviceEvent *ev = reinterpret_cast<xXIDeviceEvent *>(event); QXcbWindow *xcbWindow = platformWindowFromId(ev->event); @@ -744,45 +825,52 @@ void QXcbConnection::xi2ReportTabletEvent(const TabletData &tabletData, void *ev 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; + double pressure = 0, rotation = 0, tangentialPressure = 0; int xTilt = 0, yTilt = 0; - for (QHash<int, TabletData::ValuatorClassInfo>::const_iterator it = tabletData.valuatorInfo.constBegin(), - ite = tabletData.valuatorInfo.constEnd(); it != ite; ++it) { + for (QHash<int, TabletData::ValuatorClassInfo>::iterator it = tabletData.valuatorInfo.begin(), + ite = tabletData.valuatorInfo.end(); 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; + TabletData::ValuatorClassInfo &classInfo(it.value()); + xi2GetValuatorValueIfSet(event, classInfo.number, &classInfo.curVal); + double normalizedValue = (classInfo.curVal - classInfo.minVal) / (classInfo.maxVal - classInfo.minVal); + switch (valuator) { + case QXcbAtom::AbsPressure: + pressure = normalizedValue; + break; + case QXcbAtom::AbsTiltX: + xTilt = classInfo.curVal; + break; + case QXcbAtom::AbsTiltY: + yTilt = classInfo.curVal; + break; + case QXcbAtom::AbsWheel: + switch (tabletData.tool) { + case QTabletEvent::Airbrush: + tangentialPressure = normalizedValue * 2.0 - 1.0; // Convert 0..1 range to -1..+1 range break; - case QXcbAtom::AbsTiltY: - yTilt = value; + case QTabletEvent::RotationStylus: + rotation = normalizedValue * 360.0 - 180.0; // Convert 0..1 range to -180..+180 degrees break; - case QXcbAtom::AbsWheel: - rotation = value / 64.0; - break; - default: + default: // Other types of styli do not use this valuator break; } + break; + default: + break; } } if (Q_UNLIKELY(debug_xinput)) - qDebug("XI2 tablet event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f pressure %4.2lf tilt %d, %d rotation %6.2lf", - ev->type, ev->sequenceNumber, ev->detail, + qDebug("XI2 event on tablet %d with tool %d type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f pressure %4.2lf tilt %d, %d rotation %6.2lf", + ev->deviceid, tabletData.tool, ev->type, ev->sequenceNumber, ev->detail, fixed1616ToReal(ev->event_x), fixed1616ToReal(ev->event_y), fixed1616ToReal(ev->root_x), fixed1616ToReal(ev->root_y), pressure, xTilt, yTilt, rotation); QWindowSystemInterface::handleTabletEvent(window, tabletData.down, local, global, - QTabletEvent::Stylus, tabletData.pointerType, - pressure, xTilt, yTilt, 0, + tabletData.tool, tabletData.pointerType, + pressure, xTilt, yTilt, tangentialPressure, rotation, 0, tabletData.serialId); } #endif // QT_NO_TABLETEVENT diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index ddb164bf07..1b1c20f02c 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -303,7 +303,10 @@ bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const QAbstractEventDispatcher *QXcbIntegration::createEventDispatcher() const { - return createUnixEventDispatcher(); + QAbstractEventDispatcher *dispatcher = createUnixEventDispatcher(); + for (int i = 0; i < m_connections.size(); i++) + m_connections[i]->eventReader()->registerEventDispatcher(dispatcher); + return dispatcher; } void QXcbIntegration::initialize() diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index 69601f44d4..4c84b19f82 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -929,6 +929,15 @@ int QXcbKeyboard::keysymToQtKey(xcb_keysym_t key) const i += 2; } + if (rmod_masks.meta) { + // translate Super/Hyper keys to Meta if we're using them as the MetaModifier + if (rmod_masks.meta == rmod_masks.super && (code == Qt::Key_Super_L || code == Qt::Key_Super_R)) { + code = Qt::Key_Meta; + } else if (rmod_masks.meta == rmod_masks.hyper && (code == Qt::Key_Hyper_L || code == Qt::Key_Hyper_R)) { + code = Qt::Key_Meta; + } + } + return code; } diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 9f19841437..01e78465b6 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -106,6 +106,11 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr, qDebug(" root ID........: %x", screen()->root); #endif + QScopedPointer<xcb_get_window_attributes_reply_t, QScopedPointerPodDeleter> rootAttribs( + xcb_get_window_attributes_reply(xcb_connection(), + xcb_get_window_attributes_unchecked(xcb_connection(), screen()->root), NULL)); + const quint32 existingEventMask = rootAttribs.isNull() ? 0 : rootAttribs->your_event_mask; + const quint32 mask = XCB_CW_EVENT_MASK; const quint32 values[] = { // XCB_CW_EVENT_MASK @@ -113,6 +118,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr, | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY // for the "MANAGER" atom (system tray notification). + | existingEventMask // don't overwrite the event mask on the root window }; xcb_change_window_attributes(xcb_connection(), screen()->root, mask, values); diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 74d8b7c2c8..586068d8d9 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -1342,6 +1342,7 @@ void QXcbWindow::setWindowTitle(const QString &title) 8, ba.length(), ba.constData())); + xcb_flush(xcb_connection()); } void QXcbWindow::setWindowIcon(const QIcon &icon) diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro index a52aaa4a2e..4d76e4d449 100644 --- a/src/plugins/platforms/xcb/xcb-plugin.pro +++ b/src/plugins/platforms/xcb/xcb-plugin.pro @@ -109,7 +109,6 @@ QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XCB CONFIG += qpa/genericunixfontdatabase contains(QT_CONFIG, dbus) { -DEFINES += XCB_USE_IBUS QT += dbus LIBS += -ldbus-1 } |