Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwindowstabletsupport.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include "qwindowscontext.h"
7#include "qwindowskeymapper.h"
8#include "qwindowswindow.h"
9#include "qwindowsscreen.h"
10
11#include <qpa/qwindowsysteminterface.h>
12
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>
20
21#include <private/qguiapplication_p.h>
22#include <QtCore/private/qsystemlibrary_p.h>
23
24// Note: The definition of the PACKET structure in pktdef.h depends on this define.
25#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_Z | PK_TIME)
26#include <pktdef.h>
27
29
30enum {
33 DeviceIdMask = 0xFF6, // device type mask && device color mask
34 CursorTypeBitMask = 0x0F06 // bitmask to find the specific cursor type (see Wacom FAQ)
35};
36
37extern "C" LRESULT QT_WIN_CALLBACK qWindowsTabletSupportWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
38{
39 switch (message) {
40 case WT_PROXIMITY:
41 if (QWindowsContext::instance()->tabletSupport()->translateTabletProximityEvent(wParam, lParam))
42 return 0;
43 break;
44 case WT_PACKET:
45 if (QWindowsContext::instance()->tabletSupport()->translateTabletPacketEvent())
46 return 0;
47 break;
48 }
49 return DefWindowProc(hwnd, message, wParam, lParam);
50}
51
52
53// Scale tablet coordinates to screen coordinates.
54
55static inline int sign(int x)
56{
57 return x >= 0 ? 1 : -1;
58}
59
60inline QPointF QWindowsTabletDeviceData::scaleCoordinates(int coordX, int coordY, const QRect &targetArea) const
61{
62 const int targetX = targetArea.x();
63 const int targetY = targetArea.y();
64 const int targetWidth = targetArea.width();
65 const int targetHeight = targetArea.height();
66
67 const qreal x = sign(targetWidth) == sign(maxX) ?
68 ((coordX - minX) * qAbs(targetWidth) / qAbs(qreal(maxX - minX))) + targetX :
69 ((qAbs(maxX) - (coordX - minX)) * qAbs(targetWidth) / qAbs(qreal(maxX - minX))) + targetX;
70
71 const qreal y = sign(targetHeight) == sign(maxY) ?
72 ((coordY - minY) * qAbs(targetHeight) / qAbs(qreal(maxY - minY))) + targetY :
73 ((qAbs(maxY) - (coordY - minY)) * qAbs(targetHeight) / qAbs(qreal(maxY - minY))) + targetY;
74
75 return {x, y};
76}
77
78template <class Stream>
79static void formatOptions(Stream &str, unsigned options)
80{
81 if (options & CXO_SYSTEM)
82 str << " CXO_SYSTEM";
83 if (options & CXO_PEN)
84 str << " CXO_PEN";
85 if (options & CXO_MESSAGES)
86 str << " CXO_MESSAGES";
87 if (options & CXO_MARGIN)
88 str << " CXO_MARGIN";
89 if (options & CXO_MGNINSIDE)
90 str << " CXO_MGNINSIDE";
91 if (options & CXO_CSRMESSAGES)
92 str << " CXO_CSRMESSAGES";
93}
94
95#ifndef QT_NO_DEBUG_STREAM
97{
98 QDebugStateSaver saver(d);
99 d.nospace();
100 d << "TabletDevice id:" << t.systemId << " pressure: " << t.minPressure
101 << ".." << t.maxPressure << " tan pressure: " << t.minTanPressure << ".."
102 << t.maxTanPressure << " area: (" << t.minX << ',' << t.minY << ',' << t.minZ
103 << ")..(" << t.maxX << ',' << t.maxY << ',' << t.maxZ << ')';
104 return d;
105}
106
107QDebug operator<<(QDebug d, const LOGCONTEXT &lc)
108{
109 QDebugStateSaver saver(d);
110 d.nospace();
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 << "))";
128 return d;
129}
130#endif // !QT_NO_DEBUG_STREAM
131
132QWinTabPointingDevice *createInputDevice(const QSharedPointer<QWindowsTabletDeviceData> &d,
135{
136 const qint64 uniqueId = d->systemId | (qint64(devType) << 32)
137 | (qint64(pointerType) << 48L);
138 QInputDevice::Capabilities caps(QInputDevice::Capability::Position
142 if (d->zCapability)
144 if (d->tiltCapability) {
149 }
150
151 auto result = new QWinTabPointingDevice(d, QStringLiteral("wintab"), d->systemId,
152 devType, pointerType, caps, 1,
153 d->buttonsMap.size(), QString(),
156 return result;
157}
158
160 const QString &name, qint64 systemId,
163 QInputDevice::Capabilities caps,
164 int maxPoints, int buttonCount,
165 const QString &seatName,
167 QObject *parent)
168 : QPointingDevice(name, systemId, devType, pType, caps, maxPoints, buttonCount,
169 seatName, uniqueId, parent),
170 m_deviceData(data)
171{
172}
173
174QWindowsWinTab32DLL QWindowsTabletSupport::m_winTab32DLL;
175
184{
185 if (wTInfo)
186 return true;
187 QSystemLibrary library(QStringLiteral("wintab32"));
188 if (!library.load())
189 return false;
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");
200}
201
214int QWindowsTabletSupport::m_absoluteRange = 20;
215
216QWindowsTabletSupport::QWindowsTabletSupport(HWND window, HCTX context)
217 : m_window(window)
218 , m_context(context)
219{
220 AXIS orientation[3];
221 // Some tablets don't support tilt, check if it is possible,
222 if (QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES, DVC_ORIENTATION, &orientation))
223 m_tiltSupport = orientation[0].axResolution && orientation[1].axResolution;
224}
225
227{
228 QWindowsTabletSupport::m_winTab32DLL.wTClose(m_context);
229 DestroyWindow(m_window);
230}
231
233{
234 if (!m_winTab32DLL.init())
235 return nullptr;
236 const HWND window = QWindowsContext::instance()->createDummyWindow(QStringLiteral("TabletDummyWindow"),
237 L"TabletDummyWindow",
239 if (!window) {
240 qCWarning(lcQpaTablet) << __FUNCTION__ << "Unable to create window for tablet.";
241 return nullptr;
242 }
243 LOGCONTEXT lcMine;
244 // build our context from the default context
245 QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEFSYSCTX, 0, &lcMine);
246 qCDebug(lcQpaTablet) << "Default: " << lcMine;
247 // Go for the raw coordinates, the tablet event will return good stuff
248 lcMine.lcOptions |= CXO_MESSAGES | CXO_CSRMESSAGES;
249 lcMine.lcPktData = lcMine.lcMoveMask = PACKETDATA;
250 lcMine.lcPktMode = PacketMode;
251 lcMine.lcOutOrgX = 0;
252 lcMine.lcOutExtX = lcMine.lcInExtX;
253 lcMine.lcOutOrgY = 0;
254 lcMine.lcOutExtY = -lcMine.lcInExtY;
255 qCDebug(lcQpaTablet) << "Requesting: " << lcMine;
256 const HCTX context = QWindowsTabletSupport::m_winTab32DLL.wTOpen(window, &lcMine, true);
257 if (!context) {
258 qCDebug(lcQpaTablet) << __FUNCTION__ << "Unable to open tablet.";
259 DestroyWindow(window);
260 return nullptr;
261
262 }
263 // Set the size of the Packet Queue to the correct size
264 const int currentQueueSize = QWindowsTabletSupport::m_winTab32DLL.wTQueueSizeGet(context);
265 if (currentQueueSize != TabletPacketQSize) {
266 if (!QWindowsTabletSupport::m_winTab32DLL.wTQueueSizeSet(context, TabletPacketQSize)) {
267 if (!QWindowsTabletSupport::m_winTab32DLL.wTQueueSizeSet(context, currentQueueSize)) {
268 qWarning("Unable to set queue size on tablet. The tablet will not work.");
269 QWindowsTabletSupport::m_winTab32DLL.wTClose(context);
270 DestroyWindow(window);
271 return nullptr;
272 } // cannot restore old size
273 } // cannot set
274 } // mismatch
275 qCDebug(lcQpaTablet) << "Opened tablet context " << context << " on window "
276 << window << "changed packet queue size " << currentQueueSize
277 << "->" << TabletPacketQSize << "\nobtained: " << lcMine;
279}
280
281unsigned QWindowsTabletSupport::options() const
282{
283 UINT result = 0;
284 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_CTXOPTIONS, &result);
285 return result;
286}
287
289{
290 const unsigned size = m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_WINTABID, nullptr);
291 if (!size)
292 return QString();
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();
300 WORD devices = 0;
301 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_NDEVICES, &devices);
302 WORD cursors = 0;
303 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_NCURSORS, &cursors);
304 WORD extensions = 0;
305 m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_NEXTENSIONS, &extensions);
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);
315 if (m_tiltSupport)
316 str << " tilt";
317 return result;
318}
319
321{
322 // Cooperate with other tablet applications, but when we get focus, I want to use the tablet.
323 const bool result = QWindowsTabletSupport::m_winTab32DLL.wTEnable(m_context, true)
324 && QWindowsTabletSupport::m_winTab32DLL.wTOverlap(m_context, true);
325 qCDebug(lcQpaTablet) << __FUNCTION__ << result;
326}
327
328static inline QInputDevice::DeviceType deviceType(const UINT cursorType)
329{
330 if (((cursorType & 0x0006) == 0x0002) && ((cursorType & CursorTypeBitMask) != 0x0902))
332 if (cursorType == 0x4020) // Surface Pro 2 tablet device
334 switch (cursorType & CursorTypeBitMask) {
335 case 0x0802:
337 case 0x0902:
339 case 0x0004:
341 case 0x0006:
343 case 0x0804:
345 default:
346 break;
347 }
349}
350
351static inline QPointingDevice::PointerType pointerType(unsigned currentCursor)
352{
353 switch (currentCursor % 3) { // %3 for dual track
354 case 0:
356 case 1:
358 case 2:
360 default:
361 break;
362 }
364}
365
366inline void QWindowsTabletSupport::enterProximity(ulong time, QWindow *window)
367{
368 enterLeaveProximity(true, time, window);
369}
370
371inline void QWindowsTabletSupport::leaveProximity(ulong time, QWindow *window)
372{
373 enterLeaveProximity(false, time, window);
374}
375
376void QWindowsTabletSupport::enterLeaveProximity(bool enter, ulong time, QWindow *window)
377{
378 Q_ASSERT(!m_currentDevice.isNull());
379 if (time == 0) // Some leave events do not have a time associated
380 ++m_eventTime;
381 else
382 m_eventTime = time;
384 m_currentDevice.data(),
385 enter);
386}
387
388QWindowsTabletSupport::DevicePtr QWindowsTabletSupport::findDevice(qint64 systemId) const
389{
390 for (const auto &d : m_devices) {
391 if (d->deviceData()->systemId == systemId)
392 return d;
393 }
394 return {};
395}
396
397QWindowsTabletSupport::DevicePtr QWindowsTabletSupport::findDevice(qint64 systemId,
400{
401 for (const auto &d : m_devices) {
402 if (d->deviceData()->systemId == systemId && d->type() == deviceType
403 && d->pointerType() == pointerType) {
404 return d;
405 }
406 }
407 return {};
408}
409
410// Clone a device for a new pointer type.
411QWindowsTabletSupport::DevicePtr QWindowsTabletSupport::clonePhysicalDevice(qint64 systemId,
414{
415 auto similar = findDevice(systemId);
416 if (similar.isNull())
417 return {};
418 DevicePtr result(createInputDevice(similar->deviceData(), deviceType, pointerType));
419 m_devices.append(result);
420 return result;
421}
422
423void QWindowsTabletSupport::updateData(QWindowsTabletDeviceData *data) const
424{
425 /* browse WinTab's many info items to discover pressure handling. */
426 AXIS axis;
427 LOGCONTEXT lc;
428 /* get the current context for its device variable. */
429 QWindowsTabletSupport::m_winTab32DLL.wTGet(m_context, &lc);
430 /* get the size of the pressure axis. */
431 QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES + lc.lcDevice, DVC_NPRESSURE, &axis);
432 data->minPressure = int(axis.axMin);
433 data->maxPressure = int(axis.axMax);
434
435 QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES + lc.lcDevice, DVC_TPRESSURE, &axis);
436 data->minTanPressure = int(axis.axMin);
437 data->maxTanPressure = int(axis.axMax);
438
439 LOGCONTEXT defaultLc;
440 /* get default region */
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);
445}
446
447void QWindowsTabletSupport::updateButtons(unsigned currentCursor, QWindowsTabletDeviceData *data) const
448{
449 // We should check button map for changes on every proximity event, not
450 // only during initialization phase.
451 // WARNING: in 2016 there were some Wacom tablet drivers, which could mess up
452 // button mapping if the remapped button was pressed, while the
453 // application **didn't have input focus**. This bug is somehow
454 // related to the fact that Wacom drivers allow user to configure
455 // per-application button-mappings. If the bug shows up again,
456 // just move this button-map fetching into initialization block.
457 //
458 // See https://bugs.kde.org/show_bug.cgi?id=359561
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];
466}
467
468bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, LPARAM lParam)
469{
470 PACKET proximityBuffer[1]; // we are only interested in the first packet in this case
471 const int totalPacks = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, 1, proximityBuffer);
472
473 if (!LOWORD(lParam)) {
474 if (m_currentDevice.isNull()) // QTBUG-65120, spurious leave observed
475 return false;
476 qCDebug(lcQpaTablet) << "leave proximity for device #" << m_currentDevice.data();
477 m_state = PenUp;
478 leaveProximity(totalPacks > 0 ? proximityBuffer[0].pkTime : 0u);
479 return true;
480 }
481
482 if (!totalPacks)
483 return false;
484
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;
489 UINT cursorType;
490 QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_TYPE, &cursorType);
491
492 const QInputDevice::DeviceType currentType = deviceType(cursorType);
493 const QPointingDevice::PointerType currentPointerType = pointerType(currentCursor);
494 // initializing and updating the cursor should be done in response to
495 // WT_CSRCHANGE. We do it in WT_PROXIMITY because some wintab never send
496 // the event WT_CSRCHANGE even if asked with CXO_CSRMESSAGES
497 m_currentDevice = findDevice(systemId, currentType, currentPointerType);
498 if (m_currentDevice.isNull())
499 m_currentDevice = clonePhysicalDevice(systemId, currentType, currentPointerType);
500 if (m_currentDevice.isNull()) {
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);
508 }
509
510 // The user can switch pressure sensitivity level in the driver,which
511 // will make our saved values invalid (this option is provided by Wacom
512 // drivers for compatibility reasons, and it can be adjusted on the fly)
513 updateData(m_currentDevice->deviceData().data());
514
515 m_state = PenProximity;
516 qCDebug(lcQpaTablet) << "enter proximity for device #"
517 << m_currentDevice.data();
518 enterProximity(proximityBuffer[0].pkTime);
519 return true;
520}
521
523 const QWindowsTabletDeviceData &tdd) {
524
525 enum : unsigned {
526 leftButtonValue = 0x1,
527 middleButtonValue = 0x2,
528 rightButtonValue = 0x4,
529 doubleClickButtonValue = 0x7
530 };
531
532 button = tdd.buttonsMap.value(button);
533
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 /* fallback item */ :
540}
541
542Qt::MouseButtons convertTabletButtons(DWORD btnNew,
543 const QWindowsTabletDeviceData &tdd) {
544
545 Qt::MouseButtons buttons = Qt::NoButton;
546 for (unsigned int i = 0; i < 3; i++) {
547 unsigned int btn = 0x1 << i;
548
549 if (btn & btnNew) {
550 Qt::MouseButton convertedButton =
552
553 buttons |= convertedButton;
554
563 }
564 }
565 return buttons;
566}
567
569{
570 static PACKET localPacketBuf[TabletPacketQSize]; // our own tablet packet queue.
571 const int packetCount = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, TabletPacketQSize, &localPacketBuf);
572 if (!packetCount || m_currentDevice.isNull())
573 return false;
574
575 const QWindowsTabletDeviceData &current = *m_currentDevice->deviceData();
576
577 // The tablet can be used in 2 different modes (reflected in enum Mode),
578 // depending on its settings:
579 // 1) Absolute (pen) mode:
580 // The coordinates are scaled to the virtual desktop (by default). The user
581 // can also choose to scale to the monitor or a region of the screen.
582 // When entering proximity, the tablet driver snaps the mouse pointer to the
583 // tablet position scaled to that area and keeps it in sync.
584 // 2) Relative (mouse) mode:
585 // The pen follows the mouse. The constant 'absoluteRange' specifies the
586 // manhattanLength difference for detecting if a tablet input device is in this mode,
587 // in which case we snap the position to the mouse position.
588 // It seems there is no way to find out the mode programmatically, the LOGCONTEXT orgX/Y/Ext
589 // area is always the virtual desktop.
590 const QRect virtualDesktopArea =
592
593 if (QWindowsContext::verbose > 1) {
594 qCDebug(lcQpaTablet) << __FUNCTION__ << "processing" << packetCount
595 << "mode=" << m_mode;
596 }
597
598 const auto *keyMapper = QWindowsContext::instance()->keyMapper();
599 const Qt::KeyboardModifiers keyboardModifiers = keyMapper->queryKeyboardModifiers();
600
601 for (int i = 0; i < packetCount ; ++i) {
602 const PACKET &packet = localPacketBuf[i];
603
604 const int z = current.zCapability ? int(packet.pkZ) : 0;
605
606 const auto packetPointerType = pointerType(packet.pkCursor);
607
608 const Qt::MouseButtons buttons =
609 convertTabletButtons(packet.pkButtons, current);
610
611 if (buttons == Qt::NoButton && packetPointerType != m_currentDevice->pointerType()) {
612 leaveProximity(packet.pkTime);
613 Q_ASSERT(!m_currentDevice.isNull());
614 // Pointer type changed, find or clone a new device for this physical cursor.
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);
622 }
623
624 QPointF globalPosF =
625 current.scaleCoordinates(packet.pkX, packet.pkY, virtualDesktopArea);
626
627 // Pass to window that grabbed it.
629
630 // Get Mouse Position and compare to tablet info
631 const QPoint mouseLocation = QWindowsCursor::mousePosition();
632 if (m_state == PenProximity) {
633 m_state = PenDown;
634 m_mode = (mouseLocation - globalPosF).manhattanLength() > m_absoluteRange
635 ? MouseMode : PenMode;
636 qCDebug(lcQpaTablet) << __FUNCTION__ << "mode=" << m_mode << "pen:"
637 << globalPosF << "mouse:" << mouseLocation;
638 }
639 if (m_mode == MouseMode)
640 globalPosF = mouseLocation;
641 const QPoint globalPos = globalPosF.toPoint();
642
643 if (!target)
644 target = QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT);
645 if (!target)
646 continue;
647
648 const QPlatformWindow *platformWindow = target->handle();
649 Q_ASSERT(platformWindow);
650 const QPoint localPos = platformWindow->mapFromGlobal(globalPos);
651
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);
658
659 int tiltX = 0;
660 int tiltY = 0;
661 qreal rotation = 0;
662 if (m_tiltSupport) {
663 // Convert from azimuth and altitude to x tilt and y tilt. What
664 // follows is the optimized version. Here are the equations used:
665 // X = sin(azimuth) * cos(altitude)
666 // Y = cos(azimuth) * cos(altitude)
667 // Z = sin(altitude)
668 // X Tilt = arctan(X / Z)
669 // Y Tilt = arctan(Y / Z)
670 const double radAzim = qDegreesToRadians(packet.pkOrientation.orAzimuth / 10.0);
671 const double tanAlt = std::tan(qDegreesToRadians(std::abs(packet.pkOrientation.orAltitude / 10.0)));
672
673 const double radX = std::atan(std::sin(radAzim) / tanAlt);
674 const double radY = std::atan(std::cos(radAzim) / tanAlt);
675 tiltX = int(qRadiansToDegrees(radX));
676 tiltY = int(qRadiansToDegrees(-radY));
677 rotation = 360.0 - (packet.pkOrientation.orTwist / 10.0);
678 if (rotation > 180.0)
679 rotation -= 360.0;
680 }
681
682 if (QWindowsContext::verbose > 1) {
683 qCDebug(lcQpaTablet)
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;
690 }
691
692 m_eventTime = packet.pkTime;
694 m_currentDevice.data(),
695 QPointF(localPos), globalPosF,
696 buttons, pressureNew, tiltX, tiltY,
697 tangentialPressure, rotation, z,
698 keyboardModifiers);
699 }
700 return true;
701}
702
\inmodule QtCore
\inmodule QtCore
static TabletPointData & tabletDevicePoint(qint64 deviceId)
QScreen * primaryScreen
the primary (or default) screen of the application.
DeviceType
This enum represents the type of device that generated a QPointerEvent.
DeviceType type
qint64 systemId
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qobject.h:103
The QPlatformWindow class provides an abstraction for top-level windows.
virtual QPoint mapFromGlobal(const QPoint &pos) const
Translates the global screen coordinate pos to window coordinates using native methods.
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
\inmodule QtCore\reentrant
Definition qpoint.h:25
QPointingDeviceUniqueId identifies a unique object, such as a tagged token or stylus,...
qint64 numericId
the numeric unique ID of the token represented by a touchpoint
static QPointingDeviceUniqueId fromNumericId(qint64 id)
Constructs a unique pointer ID from numeric ID id.
The QPointingDevice class describes a device from which mouse, touch or tablet events originate.
PointerType pointerType
PointerType
This enum represents what is interacting with the pointing device.
QPointingDeviceUniqueId uniqueId
\inmodule QtCore\reentrant
Definition qrect.h:30
bool isNull() const noexcept
Returns true if this object refers to \nullptr.
T * data() const noexcept
Returns the value of the pointer referenced by this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
\inmodule QtCore
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)
const DeviceDataPtr & deviceData() const
static bool handleTabletEvent(QWindow *window, ulong timestamp, const QPointingDevice *device, const QPointF &local, const QPointF &global, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, Qt::KeyboardModifiers modifiers=Qt::NoModifier)
static void registerInputDevice(const QInputDevice *device)
static bool handleTabletEnterLeaveProximityEvent(QWindow *window, ulong timestamp, const QPointingDevice *device, bool inProximity, const QPointF &local=QPointF(), const QPointF &global=QPointF(), Qt::MouseButtons buttons={}, int xTilt=0, int yTilt=0, qreal tangentialPressure=0, qreal rotation=0, int z=0, Qt::KeyboardModifiers modifiers=Qt::NoModifier)
\inmodule QtGui
Definition qwindow.h:63
static QWindowsContext * instance()
static QPoint mousePosition()
static QWindow * windowAt(const QPoint &point, unsigned flags)
static QRect virtualGeometry(const QPlatformScreen *screen)
Tablet support for Windows.
bool translateTabletProximityEvent(WPARAM wParam, LPARAM lParam)
QSharedPointer< QWinTabPointingDevice > DevicePtr
static QWindowsTabletSupport * create()
QString str
[2]
QPushButton * button
[2]
const QStyleOptionButton * btn
[3]
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
MouseButton
Definition qnamespace.h:56
@ LeftButton
Definition qnamespace.h:58
@ RightButton
Definition qnamespace.h:59
@ MiddleButton
Definition qnamespace.h:60
@ NoButton
Definition qnamespace.h:57
QTextStream & dec(QTextStream &stream)
Calls QTextStream::setIntegerBase(10) on stream and returns stream.
static void * context
EGLDeviceEXT * devices
#define qWarning
Definition qlogging.h:166
#define qCWarning(category,...)
#define qCDebug(category,...)
constexpr float qRadiansToDegrees(float radians)
Definition qmath.h:281
constexpr float qDegreesToRadians(float degrees)
Definition qmath.h:260
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLuint64 GLenum void * handle
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat GLfloat GLfloat maxY
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLfloat minY
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
GLenum target
GLuint GLsizei const GLchar * message
GLuint name
GLint y
GLfloat GLfloat GLfloat GLfloat maxX
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QStringLiteral(str)
unsigned long ulong
Definition qtypes.h:35
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
Qt::MouseButtons convertTabletButtons(DWORD btnNew, const QWindowsTabletDeviceData &tdd)
static QPointingDevice::PointerType pointerType(unsigned currentCursor)
#define PACKETDATA
QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t)
LRESULT QT_WIN_CALLBACK qWindowsTabletSupportWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
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)
@ TabletPacketQSize
@ CursorTypeBitMask
static int sign(int x)
static void formatOptions(Stream &str, unsigned options)
aWidget window() -> setWindowTitle("New Window Title")
[2]
QPointF scaleCoordinates(int coordX, int coordY, const QRect &targetArea) const
qreal scalePressure(qreal p) const
qreal scaleTangentialPressure(qreal p) const
Functions from wintabl32.dll shipped with WACOM tablets used by QWindowsTabletSupport.
PtrWTQueueSizeSet wTQueueSizeSet
int(API * PtrWTQueueSizeGet)(HCTX)
BOOL(API * PtrWTGet)(HCTX, LPLOGCONTEXT)
UINT(API * PtrWTInfo)(UINT, UINT, LPVOID)
BOOL(API * PtrWTEnable)(HCTX, BOOL)
BOOL(API * PtrWTClose)(HCTX)
BOOL(API * PtrWTQueueSizeSet)(HCTX, int)
int(API * PtrWTPacketsGet)(HCTX, int, LPVOID)
PtrWTQueueSizeGet wTQueueSizeGet
HCTX(API * PtrWTOpen)(HWND, LPLOGCONTEXT, BOOL)