11#include <qpa/qwindowsysteminterface.h>
13#include <QtGui/qevent.h>
14#include <QtGui/qscreen.h>
15#include <QtGui/qguiapplication.h>
16#include <QtGui/qwindow.h>
17#include <QtCore/qdebug.h>
18#include <QtCore/qvarlengtharray.h>
19#include <QtCore/qmath.h>
21#include <private/qguiapplication_p.h>
22#include <QtCore/private/qsystemlibrary_p.h>
25#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_Z | PK_TIME)
37LRESULT QT_WIN_CALLBACK qWindowsTabletSupportWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
41 if (QWindowsContext::instance()->tabletSupport()->translateTabletProximityEvent(wParam, lParam))
45 if (QWindowsContext::instance()->tabletSupport()->translateTabletPacketEvent())
49 return DefWindowProc(hwnd, message, wParam, lParam);
55static inline int sign(
int x)
57 return x >= 0 ? 1 : -1;
62 const int targetX = targetArea.x();
63 const int targetY = targetArea.y();
64 const int targetWidth = targetArea.width();
65 const int targetHeight = targetArea.height();
68 ((coordX -
minX) * qAbs(targetWidth) / qAbs(qreal(
maxX -
minX))) + targetX :
69 ((qAbs(
maxX) - (coordX -
minX)) * qAbs(targetWidth) / qAbs(qreal(
maxX -
minX))) + targetX;
72 ((coordY -
minY) * qAbs(targetHeight) / qAbs(qreal(
maxY -
minY))) + targetY :
73 ((qAbs(
maxY) - (coordY -
minY)) * qAbs(targetHeight) / qAbs(qreal(
maxY -
minY))) + targetY;
78template <
class Stream>
81 if (options & CXO_SYSTEM)
83 if (options & CXO_PEN)
85 if (options & CXO_MESSAGES)
86 str <<
" CXO_MESSAGES";
87 if (options & CXO_MARGIN)
89 if (options & CXO_MGNINSIDE)
90 str <<
" CXO_MGNINSIDE";
91 if (options & CXO_CSRMESSAGES)
92 str <<
" CXO_CSRMESSAGES";
95#ifndef QT_NO_DEBUG_STREAM
98 QDebugStateSaver saver(d);
100 d <<
"TabletDevice id:" << t.systemId <<
" pressure: " << t
.minPressure
107QDebug operator<<(QDebug d,
const LOGCONTEXT &lc)
109 QDebugStateSaver saver(d);
111 d <<
"LOGCONTEXT(\"" << QString::fromWCharArray(lc.lcName) <<
"\", options=0x"
112 << Qt::hex << lc.lcOptions << Qt::dec;
113 formatOptions(d, lc.lcOptions);
114 d <<
", status=0x" << Qt::hex << lc.lcStatus <<
", device=0x" << lc.lcDevice
115 << Qt::dec <<
", PktRate=" << lc.lcPktRate
116 <<
", PktData=" << lc.lcPktData <<
", PktMode=" << lc.lcPktMode
117 <<
", MoveMask=0x" << Qt::hex << lc.lcMoveMask <<
", BtnDnMask=0x" << lc.lcBtnDnMask
118 <<
", BtnUpMask=0x" << lc.lcBtnUpMask << Qt::dec <<
", SysMode=" << lc.lcSysMode
119 <<
", InOrg=(" << lc.lcInOrgX <<
", " << lc.lcInOrgY <<
", " << lc.lcInOrgZ
120 <<
"), InExt=(" << lc.lcInExtX <<
", " << lc.lcInExtY <<
", " << lc.lcInExtZ
121 <<
") OutOrg=(" << lc.lcOutOrgX <<
", " << lc.lcOutOrgY <<
", "
122 << lc.lcOutOrgZ <<
"), OutExt=(" << lc.lcOutExtX <<
", " << lc.lcOutExtY
123 <<
", " << lc.lcOutExtZ
124 <<
"), Sens=(" << lc.lcSensX <<
", " << lc.lcSensX <<
", " << lc.lcSensZ
125 <<
") SysOrg=(" << lc.lcSysOrgX <<
", " << lc.lcSysOrgY
126 <<
"), SysExt=(" << lc.lcSysExtX <<
", " << lc.lcSysExtY
127 <<
"), SysSens=(" << lc.lcSysSensX <<
", " << lc.lcSysSensY <<
"))";
133 QInputDevice::DeviceType devType,
134 QPointingDevice::PointerType pointerType)
136 const qint64 uniqueId = d->systemId | (qint64(devType) << 32)
137 | (qint64(pointerType) << 48L);
138 QInputDevice::Capabilities caps(QInputDevice::Capability::Position
139 | QInputDevice::Capability::Pressure
140 | QInputDevice::Capability::MouseEmulation
141 | QInputDevice::Capability::Hover);
143 caps |= QInputDevice::Capability::ZPosition;
144 if (d->tiltCapability) {
145 caps |= QInputDevice::Capability::XTilt
146 | QInputDevice::Capability::YTilt
147 | QInputDevice::Capability::Rotation
148 | QInputDevice::Capability::TangentialPressure;
151 auto result =
new QWinTabPointingDevice(d, QStringLiteral(
"wintab"), d->systemId,
152 devType, pointerType, caps, 1,
153 d->buttonsMap.size(), QString(),
154 QPointingDeviceUniqueId::fromNumericId(uniqueId));
155 QWindowSystemInterface::registerInputDevice(result);
160 const QString &name, qint64 systemId,
161 QInputDevice::DeviceType devType,
162 QPointingDevice::PointerType pType,
163 QInputDevice::Capabilities caps,
164 int maxPoints,
int buttonCount,
165 const QString &seatName,
166 QPointingDeviceUniqueId uniqueId,
177
178
179
180
181
187 QSystemLibrary library(QStringLiteral(
"wintab32"));
190 wTOpen = (PtrWTOpen)library.resolve(
"WTOpenW");
191 wTClose = (PtrWTClose)library.resolve(
"WTClose");
192 wTInfo = (PtrWTInfo)library.resolve(
"WTInfoW");
193 wTEnable = (PtrWTEnable)library.resolve(
"WTEnable");
194 wTOverlap = (PtrWTEnable)library.resolve(
"WTOverlap");
195 wTPacketsGet = (PtrWTPacketsGet)library.resolve(
"WTPacketsGet");
196 wTGet = (PtrWTGet)library.resolve(
"WTGetW");
197 wTQueueSizeGet = (PtrWTQueueSizeGet)library.resolve(
"WTQueueSizeGet");
198 wTQueueSizeSet = (PtrWTQueueSizeSet)library.resolve(
"WTQueueSizeSet");
199 return wTOpen && wTClose && wTInfo && wTEnable && wTOverlap && wTPacketsGet && wTQueueSizeGet && wTQueueSizeSet;
203
204
205
206
207
208
209
210
211
212
222 if (QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES, DVC_ORIENTATION, &orientation))
223 m_tiltSupport = orientation[0].axResolution && orientation[1].axResolution;
228 QWindowsTabletSupport::m_winTab32DLL.wTClose(m_context);
229 DestroyWindow(m_window);
236 const HWND window = QWindowsContext::instance()->createDummyWindow(QStringLiteral(
"TabletDummyWindow"),
237 L"TabletDummyWindow",
238 qWindowsTabletSupportWndProc);
240 qCWarning(lcQpaTablet) <<
__FUNCTION__ <<
"Unable to create window for tablet.";
245 QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEFSYSCTX, 0, &lcMine);
246 qCDebug(lcQpaTablet) <<
"Default: " << lcMine;
248 lcMine.lcOptions |= CXO_MESSAGES | CXO_CSRMESSAGES;
249 lcMine.lcPktData = lcMine.lcMoveMask =
PACKETDATA;
251 lcMine.lcOutOrgX = 0;
252 lcMine.lcOutExtX = lcMine.lcInExtX;
253 lcMine.lcOutOrgY = 0;
254 lcMine.lcOutExtY = -lcMine.lcInExtY;
255 qCDebug(lcQpaTablet) <<
"Requesting: " << lcMine;
258 qCDebug(lcQpaTablet) <<
__FUNCTION__ <<
"Unable to open tablet.";
259 DestroyWindow(window);
268 qWarning(
"Unable to set queue size on tablet. The tablet will not work.");
270 DestroyWindow(window);
275 qCDebug(lcQpaTablet) <<
"Opened tablet context " << context <<
" on window "
276 << window <<
"changed packet queue size " << currentQueueSize
277 <<
"->" << TabletPacketQSize <<
"\nobtained: " << lcMine;
284 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_CTXOPTIONS, &result);
290 const unsigned size = m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_WINTABID,
nullptr);
293 QVarLengthArray<TCHAR> winTabId(size + 1);
294 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_WINTABID, winTabId.data());
295 WORD implementationVersion = 0;
296 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_IMPLVERSION, &implementationVersion);
297 WORD specificationVersion = 0;
298 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_SPECVERSION, &specificationVersion);
299 const unsigned opts = options();
301 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_NDEVICES, &devices);
303 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_NCURSORS, &cursors);
305 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_NEXTENSIONS, &extensions);
307 QTextStream str(&result);
308 str <<
'"' << QString::fromWCharArray(winTabId.data())
309 <<
"\" specification: v" << (specificationVersion >> 8)
310 <<
'.' << (specificationVersion & 0xFF) <<
" implementation: v"
311 << (implementationVersion >> 8) <<
'.' << (implementationVersion & 0xFF)
312 <<
' ' << devices <<
" device(s), " << cursors <<
" cursor(s), "
313 << extensions <<
" extensions" <<
", options: 0x" << Qt::hex << opts << Qt::dec;
314 formatOptions(str, opts);
323 const bool result = QWindowsTabletSupport::m_winTab32DLL.wTEnable(m_context,
true)
324 && QWindowsTabletSupport::m_winTab32DLL.wTOverlap(m_context,
true);
325 qCDebug(lcQpaTablet) <<
__FUNCTION__ << result;
330 if (((cursorType & 0x0006) == 0x0002) && ((cursorType & CursorTypeBitMask) != 0x0902))
331 return QInputDevice::DeviceType::Stylus;
332 if (cursorType == 0x4020)
333 return QInputDevice::DeviceType::Stylus;
336 return QInputDevice::DeviceType::Stylus;
338 return QInputDevice::DeviceType::Airbrush;
340 return QInputDevice::DeviceType::Mouse;
342 return QInputDevice::DeviceType::Puck;
344 return QInputDevice::DeviceType::Stylus;
348 return QInputDevice::DeviceType::Unknown;
353 switch (currentCursor % 3) {
355 return QPointingDevice::PointerType::Cursor;
357 return QPointingDevice::PointerType::Pen;
359 return QPointingDevice::PointerType::Eraser;
363 return QPointingDevice::PointerType::Unknown;
368 enterLeaveProximity(
true, time, window);
373 enterLeaveProximity(
false, time, window);
378 Q_ASSERT(!m_currentDevice.isNull());
383 QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, m_eventTime,
384 m_currentDevice.data(),
390 for (
const auto &d : m_devices) {
391 if (d->deviceData()->systemId == systemId)
398 QInputDevice::DeviceType deviceType,
399 QPointingDevice::PointerType pointerType)
const
401 for (
const auto &d : m_devices) {
402 if (d->deviceData()->systemId == systemId && d->type() == deviceType
403 && d->pointerType() == pointerType) {
412 QInputDevice::DeviceType deviceType,
413 QPointingDevice::PointerType pointerType)
415 auto similar = findDevice(systemId);
416 if (similar.isNull())
418 DevicePtr result(createInputDevice(similar->deviceData(), deviceType, pointerType));
419 m_devices.append(result);
429 QWindowsTabletSupport::m_winTab32DLL.wTGet(m_context, &lc);
431 QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES + lc.lcDevice, DVC_NPRESSURE, &axis);
435 QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES + lc.lcDevice, DVC_TPRESSURE, &axis);
439 LOGCONTEXT defaultLc;
441 QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEFCONTEXT, 0, &defaultLc);
442 data
->maxX =
int(defaultLc.lcInExtX) -
int(defaultLc.lcInOrgX);
443 data
->maxY =
int(defaultLc.lcInExtY) -
int(defaultLc.lcInOrgY);
444 data
->maxZ =
int(defaultLc.lcInExtZ) -
int(defaultLc.lcInOrgZ);
459 BYTE logicalButtons[32];
460 memset(logicalButtons, 0, 32);
461 m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_SYSBTNMAP, &logicalButtons);
462 data->buttonsMap.clear();
463 data->buttonsMap[0x1] = logicalButtons[0];
464 data->buttonsMap[0x2] = logicalButtons[1];
465 data->buttonsMap[0x4] = logicalButtons[2];
470 PACKET proximityBuffer[1];
471 const int totalPacks = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, 1, proximityBuffer);
473 if (!LOWORD(lParam)) {
474 if (m_currentDevice.isNull())
476 qCDebug(lcQpaTablet) <<
"leave proximity for device #" << m_currentDevice.data();
478 leaveProximity(totalPacks > 0 ? proximityBuffer[0].pkTime : 0u);
485 const UINT currentCursor = proximityBuffer[0].pkCursor;
486 UINT physicalCursorId;
487 QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_PHYSID, &physicalCursorId);
488 const qint64 systemId = physicalCursorId;
490 QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_TYPE, &cursorType);
492 const QInputDevice::DeviceType currentType = deviceType(cursorType);
493 const QPointingDevice::PointerType currentPointerType = pointerType(currentCursor);
497 m_currentDevice = findDevice(systemId, currentType, currentPointerType);
498 if (m_currentDevice.isNull())
499 m_currentDevice = clonePhysicalDevice(systemId, currentType, currentPointerType);
500 if (m_currentDevice.isNull()) {
501 QWinTabPointingDevice::DeviceDataPtr data(
new QWindowsTabletDeviceData);
502 data->systemId = systemId;
503 data->tiltCapability = m_tiltSupport;
504 data->zCapability = (cursorType == 0x0004);
505 updateButtons(currentCursor, data.data());
506 m_currentDevice.reset(createInputDevice(data, currentType, currentPointerType));
507 m_devices.append(m_currentDevice);
513 updateData(m_currentDevice->deviceData().data());
516 qCDebug(lcQpaTablet) <<
"enter proximity for device #"
517 << m_currentDevice.data();
518 enterProximity(proximityBuffer[0].pkTime);
526 leftButtonValue = 0x1,
527 middleButtonValue = 0x2,
528 rightButtonValue = 0x4,
529 doubleClickButtonValue = 0x7
532 button = tdd.buttonsMap.value(button);
534 return button == leftButtonValue ? Qt::LeftButton :
535 button == rightButtonValue ? Qt::RightButton :
536 button == doubleClickButtonValue ? Qt::MiddleButton :
537 button == middleButtonValue ? Qt::MiddleButton :
538 button ? Qt::LeftButton :
545 Qt::MouseButtons buttons = Qt::NoButton;
546 for (
unsigned int i = 0; i < 3; i++) {
547 unsigned int btn = 0x1 << i;
550 Qt::MouseButton convertedButton =
551 buttonValueToEnum(btn, tdd);
553 buttons |= convertedButton;
556
557
558
559
560
561
562
571 const int packetCount = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, TabletPacketQSize, &localPacketBuf);
572 if (!packetCount || m_currentDevice.isNull())
590 const QRect virtualDesktopArea =
591 QWindowsScreen::virtualGeometry(QGuiApplication::primaryScreen()->handle());
594 qCDebug(lcQpaTablet) <<
__FUNCTION__ <<
"processing" << packetCount
595 <<
"mode=" << m_mode;
599 const Qt::KeyboardModifiers keyboardModifiers = keyMapper->queryKeyboardModifiers();
601 for (
int i = 0; i < packetCount ; ++i) {
602 const PACKET &packet = localPacketBuf[i];
604 const int z = current
.zCapability ?
int(packet.pkZ) : 0;
606 const auto packetPointerType = pointerType(packet.pkCursor);
608 const Qt::MouseButtons buttons =
609 convertTabletButtons(packet.pkButtons, current);
611 if (buttons == Qt::NoButton && packetPointerType != m_currentDevice->pointerType()) {
612 leaveProximity(packet.pkTime);
613 Q_ASSERT(!m_currentDevice.isNull());
615 const qint64 systemId = m_currentDevice->systemId();
616 const QInputDevice::DeviceType type = m_currentDevice->type();
617 m_currentDevice = findDevice(systemId, type, packetPointerType);
618 if (m_currentDevice.isNull())
619 m_currentDevice = clonePhysicalDevice(systemId, type, packetPointerType);
620 Q_ASSERT(!m_currentDevice.isNull());
621 enterProximity(packet.pkTime);
625 current.scaleCoordinates(packet.pkX, packet.pkY, virtualDesktopArea);
628 QWindow *target = QGuiApplicationPrivate::tabletDevicePoint(m_currentDevice->uniqueId().numericId()).target;
631 const QPoint mouseLocation = QWindowsCursor::mousePosition();
634 m_mode = (mouseLocation - globalPosF).manhattanLength() > m_absoluteRange
636 qCDebug(lcQpaTablet) <<
__FUNCTION__ <<
"mode=" << m_mode <<
"pen:"
637 << globalPosF <<
"mouse:" << mouseLocation;
640 globalPosF = mouseLocation;
641 const QPoint globalPos = globalPosF.toPoint();
644 target = QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT);
648 const QPlatformWindow *platformWindow = target->handle();
649 Q_ASSERT(platformWindow);
650 const QPoint localPos = platformWindow->mapFromGlobal(globalPos);
652 const qreal pressureNew = packet.pkButtons
653 && (m_currentDevice->pointerType() == QPointingDevice::PointerType::Pen
654 || m_currentDevice->pointerType() == QPointingDevice::PointerType::Eraser)
655 ? current.scalePressure(packet.pkNormalPressure) : qreal(0);
656 const qreal tangentialPressure = m_currentDevice->type() == QInputDevice::DeviceType::Airbrush
657 ? current.scaleTangentialPressure(packet.pkTangentPressure) : qreal(0);
670 const double radAzim = qDegreesToRadians(packet.pkOrientation.orAzimuth / 10.0);
671 const double tanAlt = std::tan(qDegreesToRadians(std::abs(packet.pkOrientation.orAltitude / 10.0)));
673 const double radX = std::atan(std::sin(radAzim) / tanAlt);
674 const double radY = std::atan(std::cos(radAzim) / tanAlt);
675 tiltX = qRadiansToDegrees(radX);
676 tiltY = qRadiansToDegrees(-radY);
677 rotation = 360.0 - (packet.pkOrientation.orTwist / 10.0);
678 if (rotation > 180.0)
684 <<
"Packet #" << i <<
'/' << packetCount <<
"button:" << packet.pkButtons
685 << globalPosF << z <<
"to:" << target << localPos <<
"(packet" << packet.pkX
686 << packet.pkY <<
") dev:" << m_currentDevice->type() <<
"pointer:"
687 << m_currentDevice->pointerType() <<
"P:" << pressureNew <<
"tilt:" << tiltX <<
','
688 << tiltY <<
"tanP:" << tangentialPressure <<
"rotation:" << rotation
689 <<
" target=" << target;
692 m_eventTime = packet.pkTime;
693 QWindowSystemInterface::handleTabletEvent(target, packet.pkTime,
694 m_currentDevice.data(),
695 QPointF(localPos), globalPosF,
696 buttons, pressureNew, tiltX, tiltY,
697 tangentialPressure, rotation, z,
\inmodule QtCore\reentrant
QWinTabPointingDevice(const DeviceDataPtr &data, const QString &name, qint64 systemId, QInputDevice::DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &seatName=QString(), QPointingDeviceUniqueId uniqueId=QPointingDeviceUniqueId(), QObject *parent=nullptr)
Singleton container for all relevant information.
QPlatformKeyMapper * keyMapper() const
static QWindowsContext * instance()
Tablet support for Windows.
QString description() const
bool translateTabletPacketEvent()
bool translateTabletProximityEvent(WPARAM wParam, LPARAM lParam)
static QWindowsTabletSupport * create()
Qt::MouseButtons convertTabletButtons(DWORD btnNew, const QWindowsTabletDeviceData &tdd)
static QPointingDevice::PointerType pointerType(unsigned currentCursor)
QWinTabPointingDevice * createInputDevice(const QSharedPointer< QWindowsTabletDeviceData > &d, QInputDevice::DeviceType devType, QPointingDevice::PointerType pointerType)
Qt::MouseButton buttonValueToEnum(DWORD button, const QWindowsTabletDeviceData &tdd)
static QInputDevice::DeviceType deviceType(const UINT cursorType)
static void formatOptions(Stream &str, unsigned options)
QPointF scaleCoordinates(int coordX, int coordY, const QRect &targetArea) const
Functions from wintabl32.dll shipped with WACOM tablets used by QWindowsTabletSupport.