Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qnsview_tablet.mm
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// Qt-Security score:significant reason:default
4
5// This file is included from qnsview.mm, and only used to organize the code
6
7#ifndef QT_NO_TABLETEVENT
8
9#include <QtGui/qpointingdevice.h>
10#include <QtCore/private/qflatmap_p.h>
11
12using QCocoaTabletDeviceMap = QFlatMap<qint64, const QPointingDevice*>;
13Q_GLOBAL_STATIC(QCocoaTabletDeviceMap, devicesInProximity)
14
15@implementation QNSView (Tablet)
16
17- (bool)handleTabletEvent:(NSEvent *)theEvent
18{
19 static bool ignoreButtonMapping = qEnvironmentVariableIsSet("QT_MAC_TABLET_IGNORE_BUTTON_MAPPING");
20
21 if (!m_platformWindow)
22 return false;
23
24 NSEventType eventType = [theEvent type];
25 if (eventType != NSEventTypeTabletPoint && [theEvent subtype] != NSEventSubtypeTabletPoint)
26 return false; // Not a tablet event.
27
28 ulong timestamp = [theEvent timestamp] * 1000;
29
30 QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
31 nativeDrag->setLastInputEvent(theEvent, self);
32
33 QPointF windowPoint;
34 QPointF screenPoint;
35 [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint: &windowPoint andScreenPoint: &screenPoint];
36
37 // We use devicesInProximity because deviceID is typically 0,
38 // so QInputDevicePrivate::fromId() won't work.
39 const auto deviceId = theEvent.deviceID;
40 const auto *device = devicesInProximity->value(deviceId);
41 if (!device && deviceId == 0) {
42 // Application started up with stylus in proximity already, so we missed the proximity event?
43 // Create a generic tablet device for now.
44 device = tabletToolInstance(theEvent);
45 devicesInProximity->insert(deviceId, device);
46 }
47
48 if (Q_UNLIKELY(!device))
49 return false;
50
51 bool down = (eventType != NSEventTypeMouseMoved);
52
53 qreal pressure;
54 if (down) {
55 pressure = [theEvent pressure];
56 } else {
57 pressure = 0.0;
58 }
59
60 NSPoint tilt = [theEvent tilt];
61 int xTilt = qRound(tilt.x * 60.0);
62 int yTilt = qRound(tilt.y * -60.0);
63 qreal tangentialPressure = 0;
64 qreal rotation = 0;
65 int z = 0;
66 if (device->hasCapability(QInputDevice::Capability::ZPosition))
67 z = [theEvent absoluteZ];
68
69 if (device->hasCapability(QInputDevice::Capability::TangentialPressure))
70 tangentialPressure = ([theEvent tangentialPressure] * 2.0) - 1.0;
71
72 rotation = 360.0 - [theEvent rotation];
73 if (rotation > 180.0)
74 rotation -= 360.0;
75
76 Qt::KeyboardModifiers keyboardModifiers = QAppleKeyMapper::fromCocoaModifiers(theEvent.modifierFlags);
77 Qt::MouseButtons buttons = ignoreButtonMapping ? static_cast<Qt::MouseButtons>(static_cast<uint>([theEvent buttonMask])) : m_buttons;
78
79 QWindowSystemInterface::handleTabletEvent(m_platformWindow->window(), timestamp, device, windowPoint, screenPoint,
80 buttons, pressure, xTilt, yTilt, tangentialPressure, rotation, z, keyboardModifiers);
81 return true;
82}
83
84- (void)tabletPoint:(NSEvent *)theEvent
85{
86 if ([self isTransparentForUserInput])
87 return [super tabletPoint:theEvent];
88
89 [self handleTabletEvent: theEvent];
90}
91
92/*!
93 \internal
94 Find the existing QPointingDevice instance representing a particular tablet or stylus;
95 or create and register a new instance if it was not found.
96
97 An instance can be uniquely identified by various properties taken from \a theEvent.
98*/
99static const QPointingDevice *tabletToolInstance(NSEvent *theEvent)
100{
101 qint64 uid = theEvent.uniqueID;
102 uint bits = theEvent.vendorPointingDeviceType;
103 if (bits == 0 && uid != 0) {
104 // Fallback. It seems that the driver doesn't always include all the information.
105 // High-End Wacom devices store their "type" in the uper bits of the Unique ID.
106 // I'm not sure how to handle it for consumer devices, but I'll test that in a bit.
107 bits = uid >> 32;
108 }
109
110 // Defined in the "EN0056-NxtGenImpGuideX"
111 // on Wacom's Developer Website (www.wacomeng.com)
112 static const quint32 CursorTypeBitMask = 0x0F06;
113 quint32 toolId = bits & CursorTypeBitMask;
114 QInputDevice::Capabilities caps = QInputDevice::Capability::Position |
115 QInputDevice::Capability::Pressure | QInputDevice::Capability::Hover;
116 QInputDevice::DeviceType device;
117 int buttonCount = 3; // the tip, plus two barrel buttons
118 if (((bits & 0x0006) == 0x0002) && (toolId != 0x0902)) {
119 device = QInputDevice::DeviceType::Stylus;
120 } else {
121 switch (toolId) {
122 // TODO same cases as in qxcbconnection_xi2.cpp? then we could share this function
123 case 0x0802:
124 device = QInputDevice::DeviceType::Stylus;
125 break;
126 case 0x0902:
127 device = QInputDevice::DeviceType::Airbrush;
128 caps.setFlag(QInputDevice::Capability::TangentialPressure);
129 buttonCount = 2;
130 break;
131 case 0x0004:
132 device = QInputDevice::DeviceType::Mouse;
133 caps.setFlag(QInputDevice::Capability::Scroll);
134 break;
135 case 0x0006:
136 device = QInputDevice::DeviceType::Puck;
137 break;
138 case 0x0804:
139 device = QInputDevice::DeviceType::Stylus; // Art Pen
140 caps.setFlag(QInputDevice::Capability::Rotation);
141 buttonCount = 1;
142 break;
143 default:
144 device = QInputDevice::DeviceType::Unknown;
145 }
146 }
147
148 uint capabilityMask = theEvent.capabilityMask;
149 if (capabilityMask & 0x0200)
150 caps.setFlag(QInputDevice::Capability::ZPosition);
151 if (capabilityMask & 0x0800)
152 Q_ASSERT(caps.testFlag(QInputDevice::Capability::TangentialPressure));
153
154 QPointingDevice::PointerType pointerType = QPointingDevice::PointerType::Unknown;
155 switch (theEvent.pointingDeviceType) {
156 case NSPointingDeviceTypeUnknown:
157 default:
158 break;
159 case NSPointingDeviceTypePen:
160 pointerType = QPointingDevice::PointerType::Pen;
161 break;
162 case NSPointingDeviceTypeCursor:
163 pointerType = QPointingDevice::PointerType::Cursor;
164 break;
165 case NSPointingDeviceTypeEraser:
166 pointerType = QPointingDevice::PointerType::Eraser;
167 break;
168 }
169
170 const auto uniqueID = QPointingDeviceUniqueId::fromNumericId(uid);
171 auto windowSystemId = theEvent.deviceID;
172 const QPointingDevice *ret = QPointingDevicePrivate::queryTabletDevice(device, pointerType, uniqueID, caps, windowSystemId);
173 if (!ret) {
174 // TODO get the device name? (first argument)
175 // TODO associate each stylus with a "master" device representing the tablet itself
176 ret = new QPointingDevice(QString(), windowSystemId, device, pointerType,
177 caps, 1, buttonCount, QString(), uniqueID, QCocoaIntegration::instance());
178 QWindowSystemInterface::registerInputDevice(ret);
179 }
180 QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(ret));
181 devPriv->toolId = toolId;
182 return ret;
183}
184
185- (void)tabletProximity:(NSEvent *)theEvent
186{
187 if ([self isTransparentForUserInput])
188 return [super tabletProximity:theEvent];
189
190 const ulong timestamp = theEvent.timestamp * 1000;
191 const qint64 windowSystemId = theEvent.deviceID;
192 const QPointingDevice *device = tabletToolInstance(theEvent);
193 // TODO which window?
194 QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(nullptr, timestamp, device, theEvent.isEnteringProximity);
195 // The windowSystemId starts at 0, but is "unique" while in proximity
196 if (theEvent.isEnteringProximity)
197 devicesInProximity->insert_or_assign(windowSystemId, device);
198 else
199 devicesInProximity->remove(windowSystemId);
200}
201@end
202
203#endif