12#include <qpa/qwindowsysteminterface.h>
14#include <QtGui/qevent.h>
15#include <QtGui/qscreen.h>
16#include <QtGui/qguiapplication.h>
17#include <QtGui/qwindow.h>
18#include <QtCore/qdebug.h>
19#include <QtCore/qvarlengtharray.h>
20#include <QtCore/qmath.h>
22#include <private/qguiapplication_p.h>
23#include <QtCore/private/qsystemlibrary_p.h>
26#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_Z | PK_TIME)
38LRESULT QT_WIN_CALLBACK qWindowsTabletSupportWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
42 if (QWindowsContext::instance()->tabletSupport()->translateTabletProximityEvent(wParam, lParam))
46 if (QWindowsContext::instance()->tabletSupport()->translateTabletPacketEvent())
50 return DefWindowProc(hwnd, message, wParam, lParam);
56static inline int sign(
int x)
58 return x >= 0 ? 1 : -1;
63 const int targetX = targetArea.x();
64 const int targetY = targetArea.y();
65 const int targetWidth = targetArea.width();
66 const int targetHeight = targetArea.height();
69 ((coordX -
minX) * qAbs(targetWidth) / qAbs(qreal(
maxX -
minX))) + targetX :
70 ((qAbs(
maxX) - (coordX -
minX)) * qAbs(targetWidth) / qAbs(qreal(
maxX -
minX))) + targetX;
73 ((coordY -
minY) * qAbs(targetHeight) / qAbs(qreal(
maxY -
minY))) + targetY :
74 ((qAbs(
maxY) - (coordY -
minY)) * qAbs(targetHeight) / qAbs(qreal(
maxY -
minY))) + targetY;
79template <
class Stream>
82 if (options & CXO_SYSTEM)
84 if (options & CXO_PEN)
86 if (options & CXO_MESSAGES)
87 str <<
" CXO_MESSAGES";
88 if (options & CXO_MARGIN)
90 if (options & CXO_MGNINSIDE)
91 str <<
" CXO_MGNINSIDE";
92 if (options & CXO_CSRMESSAGES)
93 str <<
" CXO_CSRMESSAGES";
96#ifndef QT_NO_DEBUG_STREAM
99 QDebugStateSaver saver(d);
101 d <<
"TabletDevice id:" << t.systemId <<
" pressure: " << t
.minPressure
108QDebug operator<<(QDebug d,
const LOGCONTEXT &lc)
110 QDebugStateSaver saver(d);
112 d <<
"LOGCONTEXT(\"" << QString::fromWCharArray(lc.lcName) <<
"\", options=0x"
113 << Qt::hex << lc.lcOptions << Qt::dec;
114 formatOptions(d, lc.lcOptions);
115 d <<
", status=0x" << Qt::hex << lc.lcStatus <<
", device=0x" << lc.lcDevice
116 << Qt::dec <<
", PktRate=" << lc.lcPktRate
117 <<
", PktData=" << lc.lcPktData <<
", PktMode=" << lc.lcPktMode
118 <<
", MoveMask=0x" << Qt::hex << lc.lcMoveMask <<
", BtnDnMask=0x" << lc.lcBtnDnMask
119 <<
", BtnUpMask=0x" << lc.lcBtnUpMask << Qt::dec <<
", SysMode=" << lc.lcSysMode
120 <<
", InOrg=(" << lc.lcInOrgX <<
", " << lc.lcInOrgY <<
", " << lc.lcInOrgZ
121 <<
"), InExt=(" << lc.lcInExtX <<
", " << lc.lcInExtY <<
", " << lc.lcInExtZ
122 <<
") OutOrg=(" << lc.lcOutOrgX <<
", " << lc.lcOutOrgY <<
", "
123 << lc.lcOutOrgZ <<
"), OutExt=(" << lc.lcOutExtX <<
", " << lc.lcOutExtY
124 <<
", " << lc.lcOutExtZ
125 <<
"), Sens=(" << lc.lcSensX <<
", " << lc.lcSensX <<
", " << lc.lcSensZ
126 <<
") SysOrg=(" << lc.lcSysOrgX <<
", " << lc.lcSysOrgY
127 <<
"), SysExt=(" << lc.lcSysExtX <<
", " << lc.lcSysExtY
128 <<
"), SysSens=(" << lc.lcSysSensX <<
", " << lc.lcSysSensY <<
"))";
134 QInputDevice::DeviceType devType,
135 QPointingDevice::PointerType pointerType)
137 const qint64 uniqueId = d->systemId | (qint64(devType) << 32)
138 | (qint64(pointerType) << 48L);
139 QInputDevice::Capabilities caps(QInputDevice::Capability::Position
140 | QInputDevice::Capability::Pressure
141 | QInputDevice::Capability::MouseEmulation
142 | QInputDevice::Capability::Hover);
144 caps |= QInputDevice::Capability::ZPosition;
145 if (d->tiltCapability) {
146 caps |= QInputDevice::Capability::XTilt
147 | QInputDevice::Capability::YTilt
148 | QInputDevice::Capability::Rotation
149 | QInputDevice::Capability::TangentialPressure;
152 auto result =
new QWinTabPointingDevice(d, QStringLiteral(
"wintab"), d->systemId,
153 devType, pointerType, caps, 1,
154 d->buttonsMap.size(), QString(),
155 QPointingDeviceUniqueId::fromNumericId(uniqueId));
156 QWindowSystemInterface::registerInputDevice(result);
161 const QString &name, qint64 systemId,
162 QInputDevice::DeviceType devType,
163 QPointingDevice::PointerType pType,
164 QInputDevice::Capabilities caps,
165 int maxPoints,
int buttonCount,
166 const QString &seatName,
167 QPointingDeviceUniqueId uniqueId,
178
179
180
181
182
188 QSystemLibrary library(QStringLiteral(
"wintab32"));
191 wTOpen = (PtrWTOpen)library.resolve(
"WTOpenW");
192 wTClose = (PtrWTClose)library.resolve(
"WTClose");
193 wTInfo = (PtrWTInfo)library.resolve(
"WTInfoW");
194 wTEnable = (PtrWTEnable)library.resolve(
"WTEnable");
195 wTOverlap = (PtrWTEnable)library.resolve(
"WTOverlap");
196 wTPacketsGet = (PtrWTPacketsGet)library.resolve(
"WTPacketsGet");
197 wTGet = (PtrWTGet)library.resolve(
"WTGetW");
198 wTQueueSizeGet = (PtrWTQueueSizeGet)library.resolve(
"WTQueueSizeGet");
199 wTQueueSizeSet = (PtrWTQueueSizeSet)library.resolve(
"WTQueueSizeSet");
200 return wTOpen && wTClose && wTInfo && wTEnable && wTOverlap && wTPacketsGet && wTQueueSizeGet && wTQueueSizeSet;
204
205
206
207
208
209
210
211
212
213
224 m_tiltSupport = orientation[0].axResolution && orientation[1].axResolution;
230 DestroyWindow(m_window);
238 L"TabletDummyWindow",
239 qWindowsTabletSupportWndProc);
241 qCWarning(lcQpaTablet) <<
__FUNCTION__ <<
"Unable to create window for tablet.";
247 qCDebug(lcQpaTablet) <<
"Default: " << lcMine;
249 lcMine.lcOptions |= CXO_MESSAGES | CXO_CSRMESSAGES;
250 lcMine.lcPktData = lcMine.lcMoveMask =
PACKETDATA;
252 lcMine.lcOutOrgX = 0;
253 lcMine.lcOutExtX = lcMine.lcInExtX;
254 lcMine.lcOutOrgY = 0;
255 lcMine.lcOutExtY = -lcMine.lcInExtY;
256 qCDebug(lcQpaTablet) <<
"Requesting: " << lcMine;
259 qCDebug(lcQpaTablet) <<
__FUNCTION__ <<
"Unable to open tablet.";
260 DestroyWindow(window);
269 qWarning(
"Unable to set queue size on tablet. The tablet will not work.");
271 DestroyWindow(window);
276 qCDebug(lcQpaTablet) <<
"Opened tablet context " << context <<
" on window "
277 << window <<
"changed packet queue size " << currentQueueSize
285 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_CTXOPTIONS, &result);
291 const unsigned size = m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_WINTABID,
nullptr);
294 QVarLengthArray<TCHAR> winTabId(size + 1);
295 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_WINTABID, winTabId.data());
296 WORD implementationVersion = 0;
297 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_IMPLVERSION, &implementationVersion);
298 WORD specificationVersion = 0;
299 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_SPECVERSION, &specificationVersion);
300 const unsigned opts = options();
302 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_NDEVICES, &devices);
304 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_NCURSORS, &cursors);
306 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_NEXTENSIONS, &extensions);
308 QTextStream str(&result);
309 str <<
'"' << QString::fromWCharArray(winTabId.data())
310 <<
"\" specification: v" << (specificationVersion >> 8)
311 <<
'.' << (specificationVersion & 0xFF) <<
" implementation: v"
312 << (implementationVersion >> 8) <<
'.' << (implementationVersion & 0xFF)
313 <<
' ' << devices <<
" device(s), " << cursors <<
" cursor(s), "
314 << extensions <<
" extensions" <<
", options: 0x" << Qt::hex << opts << Qt::dec;
315 formatOptions(str, opts);
326 qCDebug(lcQpaTablet) <<
__FUNCTION__ << result;
331 if (((cursorType & 0x0006) == 0x0002) && ((cursorType & CursorTypeBitMask) != 0x0902))
332 return QInputDevice::DeviceType::Stylus;
333 if (cursorType == 0x4020)
334 return QInputDevice::DeviceType::Stylus;
337 return QInputDevice::DeviceType::Stylus;
339 return QInputDevice::DeviceType::Airbrush;
341 return QInputDevice::DeviceType::Mouse;
343 return QInputDevice::DeviceType::Puck;
345 return QInputDevice::DeviceType::Stylus;
349 return QInputDevice::DeviceType::Unknown;
354 switch (currentCursor % 3) {
356 return QPointingDevice::PointerType::Cursor;
358 return QPointingDevice::PointerType::Pen;
360 return QPointingDevice::PointerType::Eraser;
364 return QPointingDevice::PointerType::Unknown;
369 enterLeaveProximity(
true, time, window);
374 enterLeaveProximity(
false, time, window);
379 Q_ASSERT(!m_currentDevice.isNull());
384 QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, m_eventTime,
385 m_currentDevice.data(),
391 for (
const auto &d : m_devices) {
392 if (d->deviceData()->systemId == systemId)
399 QInputDevice::DeviceType deviceType,
400 QPointingDevice::PointerType pointerType)
const
402 for (
const auto &d : m_devices) {
403 if (d->deviceData()->systemId == systemId && d->type() == deviceType
404 && d->pointerType() == pointerType) {
413 QInputDevice::DeviceType deviceType,
414 QPointingDevice::PointerType pointerType)
416 auto similar = findDevice(systemId);
417 if (similar.isNull())
419 DevicePtr result(createInputDevice(similar->deviceData(), deviceType, pointerType));
420 m_devices.append(result);
440 LOGCONTEXT defaultLc;
443 data
->maxX =
int(defaultLc.lcInExtX) -
int(defaultLc.lcInOrgX);
444 data
->maxY =
int(defaultLc.lcInExtY) -
int(defaultLc.lcInOrgY);
445 data
->maxZ =
int(defaultLc.lcInExtZ) -
int(defaultLc.lcInOrgZ);
460 BYTE logicalButtons[32];
461 memset(logicalButtons, 0, 32);
462 m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_SYSBTNMAP, &logicalButtons);
463 data->buttonsMap.clear();
464 data->buttonsMap[0x1] = logicalButtons[0];
465 data->buttonsMap[0x2] = logicalButtons[1];
466 data->buttonsMap[0x4] = logicalButtons[2];
471 PACKET proximityBuffer[1];
472 const int totalPacks =
QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, 1, proximityBuffer);
474 if (!LOWORD(lParam)) {
475 if (m_currentDevice.isNull())
477 qCDebug(lcQpaTablet) <<
"leave proximity for device #" << m_currentDevice.data();
479 leaveProximity(totalPacks > 0 ? proximityBuffer[0].pkTime : 0u);
486 const UINT currentCursor = proximityBuffer[0].pkCursor;
487 UINT physicalCursorId;
488 QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_PHYSID, &physicalCursorId);
489 const qint64 systemId = physicalCursorId;
493 const QInputDevice::DeviceType currentType = deviceType(cursorType);
494 const QPointingDevice::PointerType currentPointerType = pointerType(currentCursor);
498 m_currentDevice = findDevice(systemId, currentType, currentPointerType);
499 if (m_currentDevice.isNull())
500 m_currentDevice = clonePhysicalDevice(systemId, currentType, currentPointerType);
501 if (m_currentDevice.isNull()) {
502 QWinTabPointingDevice::DeviceDataPtr data(
new QWindowsTabletDeviceData);
503 data->systemId = systemId;
504 data->tiltCapability = m_tiltSupport;
505 data->zCapability = (cursorType == 0x0004);
506 updateButtons(currentCursor, data.data());
507 m_currentDevice.reset(createInputDevice(data, currentType, currentPointerType));
508 m_devices.append(m_currentDevice);
514 updateData(m_currentDevice->deviceData().data());
517 qCDebug(lcQpaTablet) <<
"enter proximity for device #"
518 << m_currentDevice.data();
519 enterProximity(proximityBuffer[0].pkTime);
527 leftButtonValue = 0x1,
528 middleButtonValue = 0x2,
529 rightButtonValue = 0x4,
530 doubleClickButtonValue = 0x7
533 button = tdd.buttonsMap.value(button);
535 return button == leftButtonValue ? Qt::LeftButton :
536 button == rightButtonValue ? Qt::RightButton :
537 button == doubleClickButtonValue ? Qt::MiddleButton :
538 button == middleButtonValue ? Qt::MiddleButton :
539 button ? Qt::LeftButton :
546 Qt::MouseButtons buttons = Qt::NoButton;
547 for (
unsigned int i = 0; i < 3; i++) {
548 unsigned int btn = 0x1 << i;
551 Qt::MouseButton convertedButton =
552 buttonValueToEnum(btn, tdd);
554 buttons |= convertedButton;
557
558
559
560
561
562
563
573 if (!packetCount || m_currentDevice.isNull())
591 const QRect virtualDesktopArea =
592 QWindowsScreen::virtualGeometry(QGuiApplication::primaryScreen()->handle());
595 qCDebug(lcQpaTablet) <<
__FUNCTION__ <<
"processing" << packetCount
596 <<
"mode=" << m_mode;
600 const Qt::KeyboardModifiers keyboardModifiers = keyMapper->queryKeyboardModifiers();
602 for (
int i = 0; i < packetCount ; ++i) {
603 const PACKET &packet = localPacketBuf[i];
605 const int z = current
.zCapability ?
int(packet.pkZ) : 0;
607 const auto packetPointerType = pointerType(packet.pkCursor);
609 const Qt::MouseButtons buttons =
610 convertTabletButtons(packet.pkButtons, current);
612 if (buttons == Qt::NoButton && packetPointerType != m_currentDevice->pointerType()) {
613 leaveProximity(packet.pkTime);
614 Q_ASSERT(!m_currentDevice.isNull());
616 const qint64 systemId = m_currentDevice->systemId();
617 const QInputDevice::DeviceType type = m_currentDevice->type();
618 m_currentDevice = findDevice(systemId, type, packetPointerType);
619 if (m_currentDevice.isNull())
620 m_currentDevice = clonePhysicalDevice(systemId, type, packetPointerType);
621 Q_ASSERT(!m_currentDevice.isNull());
622 enterProximity(packet.pkTime);
626 current.scaleCoordinates(packet.pkX, packet.pkY, virtualDesktopArea);
629 QWindow *target = QGuiApplicationPrivate::tabletDevicePoint(m_currentDevice->uniqueId().numericId()).target;
632 const QPoint mouseLocation = QWindowsCursor::mousePosition();
635 m_mode = (mouseLocation - globalPosF).manhattanLength() > m_absoluteRange
637 qCDebug(lcQpaTablet) <<
__FUNCTION__ <<
"mode=" << m_mode <<
"pen:"
638 << globalPosF <<
"mouse:" << mouseLocation;
641 globalPosF = mouseLocation;
642 const QPoint globalPos = globalPosF.toPoint();
649 const QPlatformWindow *platformWindow = target->handle();
650 Q_ASSERT(platformWindow);
651 const QPoint localPos = platformWindow->mapFromGlobal(globalPos);
653 const qreal pressureNew = packet.pkButtons
654 && (m_currentDevice->pointerType() == QPointingDevice::PointerType::Pen
655 || m_currentDevice->pointerType() == QPointingDevice::PointerType::Eraser)
656 ? current.scalePressure(packet.pkNormalPressure) : qreal(0);
657 const qreal tangentialPressure = m_currentDevice->type() == QInputDevice::DeviceType::Airbrush
658 ? current.scaleTangentialPressure(packet.pkTangentPressure) : qreal(0);
671 const double radAzim = qDegreesToRadians(packet.pkOrientation.orAzimuth / 10.0);
672 const double tanAlt = std::tan(qDegreesToRadians(std::abs(packet.pkOrientation.orAltitude / 10.0)));
674 const double radX = std::atan(std::sin(radAzim) / tanAlt);
675 const double radY = std::atan(std::cos(radAzim) / tanAlt);
676 tiltX = qRadiansToDegrees(radX);
677 tiltY = qRadiansToDegrees(-radY);
678 rotation = 360.0 - (packet.pkOrientation.orTwist / 10.0);
679 if (rotation > 180.0)
685 <<
"Packet #" << i <<
'/' << packetCount <<
"button:" << packet.pkButtons
686 << globalPosF << z <<
"to:" << target << localPos <<
"(packet" << packet.pkX
687 << packet.pkY <<
") dev:" << m_currentDevice->type() <<
"pointer:"
688 << m_currentDevice->pointerType() <<
"P:" << pressureNew <<
"tilt:" << tiltX <<
','
689 << tiltY <<
"tanP:" << tangentialPressure <<
"rotation:" << rotation
690 <<
" target=" << target;
693 m_eventTime = packet.pkTime;
694 QWindowSystemInterface::handleTabletEvent(target, packet.pkTime,
695 m_currentDevice.data(),
696 QPointF(localPos), globalPosF,
697 buttons, pressureNew, tiltX, tiltY,
698 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()
static QWindow * windowAt(const QPoint &point, unsigned flags)
Tablet support for Windows.
QString description() const
bool translateTabletPacketEvent()
bool translateTabletProximityEvent(WPARAM wParam, LPARAM lParam)
static QWindowsTabletSupport * create()
Combined button and popup list for selecting options.
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.