diff --git a/3rdparty/ext_qt/0024-Fetch-stylus-button-remapping-from-WinTab-driver.patch b/3rdparty/ext_qt/0024-Fetch-stylus-button-remapping-from-WinTab-driver.patch index d67726efc5..85aa053426 100644 --- a/3rdparty/ext_qt/0024-Fetch-stylus-button-remapping-from-WinTab-driver.patch +++ b/3rdparty/ext_qt/0024-Fetch-stylus-button-remapping-from-WinTab-driver.patch @@ -1,137 +1,139 @@ -From 7d4d465771854cfa860b7cd2eb09f91cfdb8b5cd Mon Sep 17 00:00:00 2001 +From e14383479e200b0a4871bead44d1a16cd0a2623d Mon Sep 17 00:00:00 2001 From: Dmitry Kazakov Date: Sat, 13 Apr 2019 18:08:33 +0300 -Subject: [PATCH 13/27] Fetch stylus button remapping from WinTab driver +Subject: [PATCH 1/4] Fetch stylus button remapping from WinTab driver The user can remap the stylus buttons using tablet driver settings. This information is available to the application via CSR_SYSBTNMAP WinTab feature. We should fetch this information every time the stylus gets into proximity, because the user can change these settings on the fly. + +Change-Id: Idc839905c3485179d782814f78fa862fd4a99127 --- .../windows/qwindowstabletsupport.cpp | 72 ++++++++++++++++++- .../platforms/windows/qwindowstabletsupport.h | 2 + 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp -index fa209f09..31655101 100644 +index fa209f09c4..44b94d044d 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp +++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp @@ -435,6 +435,27 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L m_currentDevice = m_devices.size(); m_devices.push_back(tabletInit(uniqueId, cursorType)); } + + /** + * We should check button map for changes on every proximity event, not + * only during initialization phase. + * + * WARNING: in 2016 there were some Wacom table drivers, which could mess up + * button mapping if the remapped button was pressed, while the + * application **didn't have input focus**. This bug is somehow + * related to the fact that Wacom drivers allow user to configure + * per-application button-mappings. If the bug shows up again, + * just move this button-map fetching into initialization block. + * + * See https://bugs.kde.org/show_bug.cgi?id=359561 + */ + BYTE logicalButtons[32]; + memset(logicalButtons, 0, 32); + m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_SYSBTNMAP, &logicalButtons); + m_devices[m_currentDevice].buttonsMap[0x1] = logicalButtons[0]; + m_devices[m_currentDevice].buttonsMap[0x2] = logicalButtons[1]; + m_devices[m_currentDevice].buttonsMap[0x4] = logicalButtons[2]; + m_devices[m_currentDevice].currentPointerType = pointerType(currentCursor); m_state = PenProximity; qCDebug(lcQpaTablet) << "enter proximity for device #" -@@ -446,6 +467,50 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L +@@ -446,6 +467,52 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L return true; } +Qt::MouseButton buttonValueToEnum(DWORD button, + const QWindowsTabletDeviceData &tdd) { -+ const int leftButtonValue = 0x1; -+ const int middleButtonValue = 0x2; -+ const int rightButtonValue = 0x4; -+ const int doubleClickButtonValue = 0x7; ++ ++ enum : unsigned { ++ leftButtonValue = 0x1, ++ middleButtonValue = 0x2, ++ rightButtonValue = 0x4, ++ doubleClickButtonValue = 0x7 ++ }; + + button = tdd.buttonsMap.value(button); + + return button == leftButtonValue ? Qt::LeftButton : + button == rightButtonValue ? Qt::RightButton : + button == doubleClickButtonValue ? Qt::MiddleButton : + button == middleButtonValue ? Qt::MiddleButton : + button ? Qt::LeftButton /* fallback item */ : + Qt::NoButton; +} + -+void convertTabletButtons(DWORD btnNew, -+ Qt::MouseButtons *buttons, -+ const QWindowsTabletDeviceData &tdd, -+ const Qt::KeyboardModifiers keyboardModifiers) { ++Qt::MouseButtons convertTabletButtons(DWORD btnNew, ++ const QWindowsTabletDeviceData &tdd) { + -+ *buttons = Qt::NoButton; -+ for (int i = 0; i < 3; i++) { -+ int btn = 0x1 << i; ++ Qt::MouseButtons buttons = Qt::NoButton; ++ for (unsigned int i = 0; i < 3; i++) { ++ unsigned int btn = 0x1 << i; + + if (btn & btnNew) { + Qt::MouseButton convertedButton = + buttonValueToEnum(btn, tdd); + -+ *buttons |= convertedButton; ++ buttons |= convertedButton; + + /** + * If a button that is present in hardware input is + * mapped to a Qt::NoButton, it means that it is going + * to be eaten by the driver, for example by its + * "Pan/Scroll" feature. Therefore we shouldn't handle + * any of the events associated to it. We'll just return + * Qt::NoButtons here. + */ + } + } ++ return buttons; +} + bool QWindowsTabletSupport::translateTabletPacketEvent() { static PACKET localPacketBuf[TabletPacketQSize]; // our own tablet packet queue. -@@ -552,9 +617,14 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() +@@ -552,9 +619,12 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() << tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation; } -+ Qt::MouseButtons buttons; -+ convertTabletButtons(packet.pkButtons, &buttons, -+ m_devices.at(m_currentDevice), -+ keyboardModifiers); ++ Qt::MouseButtons buttons = ++ convertTabletButtons(packet.pkButtons, m_devices.at(m_currentDevice)); + QWindowSystemInterface::handleTabletEvent(target, packet.pkTime, QPointF(localPos), globalPosF, currentDevice, currentPointer, - static_cast(packet.pkButtons), + buttons, pressureNew, tiltX, tiltY, tangentialPressure, rotation, z, uniqueId, diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.h b/src/plugins/platforms/windows/qwindowstabletsupport.h -index d91701d6..5b1ddb52 100644 +index d91701d6a5..8f97982308 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.h +++ b/src/plugins/platforms/windows/qwindowstabletsupport.h @@ -45,6 +45,7 @@ #include #include -+#include ++#include #include @@ -100,6 +101,7 @@ struct QWindowsTabletDeviceData qint64 uniqueId = 0; int currentDevice = 0; int currentPointerType = 0; -+ QMap buttonsMap; ++ QHash buttonsMap; }; #ifndef QT_NO_DEBUG_STREAM -- -2.22.0.windows.1 +2.20.1.windows.1 diff --git a/3rdparty/ext_qt/0026-Fetch-mapped-screen-size-from-the-Wintab-driver.patch b/3rdparty/ext_qt/0026-Fetch-mapped-screen-size-from-the-Wintab-driver.patch index ad27e93407..c8edab8083 100644 --- a/3rdparty/ext_qt/0026-Fetch-mapped-screen-size-from-the-Wintab-driver.patch +++ b/3rdparty/ext_qt/0026-Fetch-mapped-screen-size-from-the-Wintab-driver.patch @@ -1,217 +1,219 @@ -From b33666c064dc255e3d39a0646684445ab907d299 Mon Sep 17 00:00:00 2001 +From 47be745d599f6cd11f3fe280b51c490682f7b2da Mon Sep 17 00:00:00 2001 From: Dmitry Kazakov Date: Sat, 13 Apr 2019 23:24:01 +0300 -Subject: [PATCH 15/27] Fetch mapped screen size from the Wintab driver +Subject: [PATCH 2/4] Fetch mapped screen size from the Wintab driver Some devices, like Microsoft Surface Pro 5, don't map tablet's input range to the entire virtual screen area, but map it to the primary display that has actual built-in tablet sensor. In such cases we should fetch actualy mapped aread from Wintab's lcSys{Org,Ext}{X,Y} fields and use it for cursor mapping. If one wants to fall back to the old screen size detection method, then an environment variable can be set: QT_IGNORE_WINTAB_MAPPING=1 When the variable is set, the scaling is done via virtual desktop area only. If the tablet driver is broken (e.g. Microsoft SP5, when primary display is set to an external monitor) the user might want to override mapping completely. Then the following variable can be used: QT_WINTAB_DESKTOP_RECT=x;y;width;height + +Change-Id: Idd8bcf0323ce0811d2ad8976eaed48ad13ac3af8 --- .../windows/qwindowstabletsupport.cpp | 89 ++++++++++++++++++- .../platforms/windows/qwindowstabletsupport.h | 13 ++- 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp -index 3e35b146..11e6769c 100644 +index 44b94d044d..c39242917f 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp +++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -216,6 +217,10 @@ QWindowsTabletSupport::QWindowsTabletSupport(HWND window, HCTX context) // Some tablets don't support tilt, check if it is possible, if (QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES, DVC_ORIENTATION, &orientation)) m_tiltSupport = orientation[0].axResolution && orientation[1].axResolution; + + connect(qGuiApp, &QGuiApplication::primaryScreenChanged, + this, QWindowsTabletSupport::slotPrimaryScreenChanged); + slotScreenGeometryChanged(); } QWindowsTabletSupport::~QWindowsTabletSupport() @@ -394,6 +399,84 @@ QWindowsTabletDeviceData QWindowsTabletSupport::tabletInit(qint64 uniqueId, UINT return result; } +void QWindowsTabletSupport::slotPrimaryScreenChanged(QScreen *screen) +{ + if (m_connectedScreen) + disconnect(m_connectedScreen, 0, this, 0); + + m_connectedScreen = screen; + + if (m_connectedScreen) + connect(m_connectedScreen, &QScreen::virtualGeometryChanged, + this, &QWindowsTabletSupport::slotScreenGeometryChanged); + + slotScreenGeometryChanged(); +} + +void QWindowsTabletSupport::slotScreenGeometryChanged() +{ + /** + * Some Wintab implementations map the tablet area to the entire + * virtual screen, but others (e.g. Microsoft SP5) don't. They + * may input range to a single (built-in) screen. The logic is + * quite obvious: when the screen has integrated tablet device, + * one cannot map this tablet device to another display. + * + * For such devices, we should always request mapped area from + * lcSys{Org,Ext}{X,Y} fields and use it accordingly. + */ + + LOGCONTEXT lc; + QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEFSYSCTX, 0, &lc); + m_wintabScreenGeometry = QRect(lc.lcSysOrgX, lc.lcSysOrgY, lc.lcSysExtX, lc.lcSysExtY); + + qCDebug(lcQpaTablet) << "Updated tablet mapping: " << m_wintabScreenGeometry; + if (QGuiApplication::primaryScreen()) { + qCDebug(lcQpaTablet) << " real desktop geometry: " << QWindowsScreen::virtualGeometry(QGuiApplication::primaryScreen()->handle()); + } +} + +void QWindowsTabletSupport::updateEffectiveScreenGeometry() +{ + QRect customGeometry; + bool dontUseWintabDesktopRect = false; + + const QString geometry = qEnvironmentVariable("QT_WINTAB_DESKTOP_RECT"); + if (!geometry.isEmpty()) { + QString tmp = QString::fromLatin1("([+-]?\\d+);([+-]?\\d+);(\\d+);(\\d+)"); + + QRegularExpression rex(tmp); + QRegularExpressionMatch match = rex.match(geometry); + + if (match.hasMatch()) { + customGeometry.setRect(match.captured(1).toInt(), + match.captured(2).toInt(), + match.captured(3).toInt(), + match.captured(4).toInt()); + + qCDebug(lcQpaTablet) << "apply QT_WINTAB_DESKTOP_RECT:" << customGeometry; + } else { + qCWarning(lcQpaTablet) << "failed to parse QT_WINTAB_DESKTOP_RECT:" << geometry; + } + } + + if (qEnvironmentVariableIsSet("QT_IGNORE_WINTAB_MAPPING")) { + if (!customGeometry.isValid()) { + qCDebug(lcQpaTablet) << "fallback mapping is requested via QT_IGNORE_WINTAB_MAPPING"; + } else { + qCWarning(lcQpaTablet) << "ignoring QT_IGNORE_WINTAB_MAPPING, because QT_WINTAB_DESKTOP_RECT is set"; + } + dontUseWintabDesktopRect = true; + } + + m_effectiveScreenGeometry = + !customGeometry.isValid() ? + (dontUseWintabDesktopRect ? + QWindowsScreen::virtualGeometry(QGuiApplication::primaryScreen()->handle()) : + m_wintabScreenGeometry) : + customGeometry; +} + bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, LPARAM lParam) { PACKET proximityBuffer[1]; // we are only interested in the first packet in this case @@ -421,6 +504,8 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L if (!totalPacks) return false; + updateEffectiveScreenGeometry(); + const UINT currentCursor = proximityBuffer[0].pkCursor; UINT physicalCursorId; QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_PHYSID, &physicalCursorId); -@@ -535,8 +620,8 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() +@@ -537,8 +622,8 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() // in which case we snap the position to the mouse position. // It seems there is no way to find out the mode programmatically, the LOGCONTEXT orgX/Y/Ext // area is always the virtual desktop. - const QRect virtualDesktopArea = - QWindowsScreen::virtualGeometry(QGuiApplication::primaryScreen()->handle()); + + const QRect virtualDesktopArea = m_effectiveScreenGeometry; if (QWindowsContext::verbose > 1) { qCDebug(lcQpaTablet) << __FUNCTION__ << "processing" << packetCount diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.h b/src/plugins/platforms/windows/qwindowstabletsupport.h -index 5b1ddb52..e8b0c01b 100644 +index 8f97982308..fe7e7815d6 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.h +++ b/src/plugins/platforms/windows/qwindowstabletsupport.h @@ -45,7 +45,9 @@ #include #include +#include - #include + #include +#include #include @@ -56,6 +58,7 @@ QT_BEGIN_NAMESPACE class QDebug; class QWindow; class QRect; +class QScreen; struct QWindowsWinTab32DLL { @@ -108,7 +111,7 @@ struct QWindowsTabletDeviceData QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t); #endif -class QWindowsTabletSupport +class QWindowsTabletSupport : public QObject { Q_DISABLE_COPY(QWindowsTabletSupport) @@ -141,9 +144,14 @@ public: int absoluteRange() const { return m_absoluteRange; } void setAbsoluteRange(int a) { m_absoluteRange = a; } +private Q_SLOTS: + void slotPrimaryScreenChanged(QScreen *screen); + void slotScreenGeometryChanged(); + private: unsigned options() const; QWindowsTabletDeviceData tabletInit(qint64 uniqueId, UINT cursorType) const; + void updateEffectiveScreenGeometry(); static QWindowsWinTab32DLL m_winTab32DLL; const HWND m_window; @@ -154,6 +162,9 @@ private: int m_currentDevice = -1; Mode m_mode = PenMode; State m_state = PenUp; + QScreen *m_connectedScreen = 0; + QRect m_wintabScreenGeometry; + QRect m_effectiveScreenGeometry; }; QT_END_NAMESPACE -- -2.22.0.windows.1 +2.20.1.windows.1 diff --git a/3rdparty/ext_qt/0027-Switch-stylus-pointer-type-when-the-tablet-is-in-the.patch b/3rdparty/ext_qt/0027-Switch-stylus-pointer-type-when-the-tablet-is-in-the.patch index 1e4657f9ca..085d712caa 100644 --- a/3rdparty/ext_qt/0027-Switch-stylus-pointer-type-when-the-tablet-is-in-the.patch +++ b/3rdparty/ext_qt/0027-Switch-stylus-pointer-type-when-the-tablet-is-in-the.patch @@ -1,61 +1,79 @@ -From ebfe32b8fe8ff7ce8b8e4330af1191c16b0a8be0 Mon Sep 17 00:00:00 2001 +From 10e1c6c6912ccd43fedd46c3f59a9b61043f4fec Mon Sep 17 00:00:00 2001 From: Dmitry Kazakov Date: Wed, 17 Apr 2019 17:39:10 +0300 -Subject: [PATCH 16/27] Switch stylus pointer type when the tablet is in the - tablet proximity +Subject: [PATCH] Switch stylus pointer type when the tablet is in the tablet + proximity Some convertible tablet devices have a special stylus button that converts the stylus into an eraser. Such button can be pressed right when the stylus is in tablet surface proximity, so we should check that not only during proximity event handling, but also while parsing normal wintab packets. +Make sure that we don't switch tablet pointer type while any **mapped** +stylus button is pressed. Pressing the "eraser" button is reported +in pkButtons, but it maps to none by CSR_SYSBTNMAP + https://bugs.kde.org/show_bug.cgi?id=405747 +https://bugs.kde.org/show_bug.cgi?id=408454 --- - .../windows/qwindowstabletsupport.cpp | 23 ++++++++++++++++++- - 1 file changed, 22 insertions(+), 1 deletion(-) + .../windows/qwindowstabletsupport.cpp | 29 ++++++++++++++++--- + 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp -index 11e6769c..b0431276 100644 +index 45dc8a7d1a..4fc8e993d7 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp +++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp -@@ -604,7 +604,6 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() +@@ -614,7 +614,6 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() return false; const int currentDevice = m_devices.at(m_currentDevice).currentDevice; - const int currentPointer = m_devices.at(m_currentDevice).currentPointerType; const qint64 uniqueId = m_devices.at(m_currentDevice).uniqueId; // The tablet can be used in 2 different modes (reflected in enum Mode), -@@ -634,6 +633,28 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() +@@ -644,6 +643,31 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() for (int i = 0; i < packetCount ; ++i) { const PACKET &packet = localPacketBuf[i]; + int currentPointer = m_devices.at(m_currentDevice).currentPointerType; + + const int packetPointerType = pointerType(packet.pkCursor); -+ if (!packet.pkButtons && packetPointerType != currentPointer) { ++ Qt::MouseButtons buttons = ++ convertTabletButtons(packet.pkButtons, m_devices.at(m_currentDevice)); ++ ++ if (buttons == Qt::NoButton && packetPointerType != currentPointer) { + + QWindowSystemInterface::handleTabletLeaveProximityEvent(packet.pkTime, + m_devices.at(m_currentDevice).currentDevice, + m_devices.at(m_currentDevice).currentPointerType, + m_devices.at(m_currentDevice).uniqueId); + + + + m_devices[m_currentDevice].currentPointerType = packetPointerType; + + QWindowSystemInterface::handleTabletEnterProximityEvent(packet.pkTime, + m_devices.at(m_currentDevice).currentDevice, + m_devices.at(m_currentDevice).currentPointerType, + m_devices.at(m_currentDevice).uniqueId); + + currentPointer = packetPointerType; + } + const int z = currentDevice == QTabletEvent::FourDMouse ? int(packet.pkZ) : 0; QPointF globalPosF = +@@ -712,9 +736,6 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() + << tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation; + } + +- Qt::MouseButtons buttons = +- convertTabletButtons(packet.pkButtons, m_devices.at(m_currentDevice)); +- + QWindowSystemInterface::handleTabletEvent(target, packet.pkTime, QPointF(localPos), globalPosF, + currentDevice, currentPointer, + buttons, -- -2.22.0.windows.1 +2.20.1.windows.1 diff --git a/3rdparty/ext_qt/0028-Fix-updating-tablet-pressure-resolution-on-every-pro.patch b/3rdparty/ext_qt/0028-Fix-updating-tablet-pressure-resolution-on-every-pro.patch index 82bcb7b98f..580e03372b 100644 --- a/3rdparty/ext_qt/0028-Fix-updating-tablet-pressure-resolution-on-every-pro.patch +++ b/3rdparty/ext_qt/0028-Fix-updating-tablet-pressure-resolution-on-every-pro.patch @@ -1,38 +1,40 @@ -From a354a9a7f0cf081aae223dfff3f1734add050b18 Mon Sep 17 00:00:00 2001 +From 41549d94ff3ad9a793c0c4c910a1a30976be9723 Mon Sep 17 00:00:00 2001 From: Dmitry Kazakov Date: Thu, 18 Apr 2019 15:42:17 +0300 -Subject: [PATCH 17/27] Fix updating tablet pressure resolution on every +Subject: [PATCH 3/4] Fix updating tablet pressure resolution on every proximity enter event The user can switch pressure sensitivity level in the driver, which will make our saved values invalid (this option is provided by Wacom drivers for compatibility reasons, and it can be adjusted on the fly) See the bug: https://bugs.kde.org/show_bug.cgi?id=391054 + +Change-Id: I6cfdff27eaf5a587bf714871f1495a7ea150c553 --- src/plugins/platforms/windows/qwindowstabletsupport.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp -index b0431276..2de87758 100644 +index c39242917f..45dc8a7d1a 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp +++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp @@ -519,6 +519,14 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L if (m_currentDevice < 0) { m_currentDevice = m_devices.size(); m_devices.push_back(tabletInit(uniqueId, cursorType)); + } else { + /** + * The user can switch pressure sensitivity level in the driver, + * which will make our saved values invalid (this option is + * provided by Wacom drivers for compatibility reasons, and + * it can be adjusted on the fly) + */ + m_devices[m_currentDevice] = tabletInit(uniqueId, cursorType); } /** -- -2.22.0.windows.1 +2.20.1.windows.1