5#include <AppKit/AppKit.h>
10#include <private/qpointingdevice_p.h>
14using namespace Qt::StringLiterals;
16Q_CONSTINIT QHash<qint64, QCocoaTouch*> QCocoaTouch::_currentTouches;
17Q_CONSTINIT QHash<quint64, QPointingDevice*> QCocoaTouch::_touchDevices;
18Q_CONSTINIT QPointF QCocoaTouch::_screenReferencePos;
19Q_CONSTINIT QPointF QCocoaTouch::_trackpadReferencePos;
20int QCocoaTouch::_idAssignmentCount = 0;
21int QCocoaTouch::_touchCount = 0;
22bool QCocoaTouch::_updateInternalStateOnly =
true;
24QCocoaTouch::QCocoaTouch(NSTouch *nstouch)
26 if (_currentTouches.size() == 0)
27 _idAssignmentCount = 0;
29 _touchPoint.id = _idAssignmentCount++;
30 _touchPoint.pressure = 1.0;
31 _identity = qint64([nstouch identity]);
32 _currentTouches.insert(_identity,
this);
33 updateTouchData(nstouch, NSTouchPhaseBegan);
36QCocoaTouch::~QCocoaTouch()
38 _currentTouches.remove(_identity);
41void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase)
43 _touchPoint.state = toTouchPointState(phase);
48 NSPoint npos = [nstouch normalizedPosition];
49 QPointF qnpos = QPointF(npos.x, 1 - npos.y);
50 _touchPoint.normalPosition = qnpos;
52 if (_touchPoint.id == 0 && phase == NSTouchPhaseBegan) {
53 _trackpadReferencePos = qnpos;
54 _screenReferencePos = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
57 QPointF screenPos = _screenReferencePos;
59 NSSize dsize = [nstouch deviceSize];
60 float ppiX = (qnpos.x() - _trackpadReferencePos.x()) * dsize.width;
61 float ppiY = (qnpos.y() - _trackpadReferencePos.y()) * dsize.height;
62 QPointF relativePos = _trackpadReferencePos - QPointF(ppiX, ppiY);
63 screenPos -= relativePos;
67 screenPos.rx() -= 0.5;
68 screenPos.ry() -= 0.5;
69 _touchPoint.area = QRectF(screenPos, QSize(1, 1));
72QCocoaTouch *QCocoaTouch::findQCocoaTouch(NSTouch *nstouch)
74 qint64 identity = qint64([nstouch identity]);
75 if (_currentTouches.contains(identity))
76 return _currentTouches.value(identity);
80QEventPoint::State QCocoaTouch::toTouchPointState(NSTouchPhase nsState)
82 QEventPoint::State qtState = QEventPoint::State::Released;
84 case NSTouchPhaseBegan:
85 qtState = QEventPoint::State::Pressed;
87 case NSTouchPhaseMoved:
88 qtState = QEventPoint::State::Updated;
90 case NSTouchPhaseStationary:
91 qtState = QEventPoint::State::Stationary;
93 case NSTouchPhaseEnded:
94 case NSTouchPhaseCancelled:
95 qtState = QEventPoint::State::Released;
103QList<QWindowSystemInterface::TouchPoint>
104QCocoaTouch::getCurrentTouchPointList(NSEvent *event,
bool acceptSingleTouch)
106 QMap<
int, QWindowSystemInterface::TouchPoint> touchPoints;
107 NSSet *ended = [event touchesMatchingPhase:NSTouchPhaseEnded | NSTouchPhaseCancelled inView:nil];
108 NSSet *active = [event
109 touchesMatchingPhase:NSTouchPhaseBegan | NSTouchPhaseMoved | NSTouchPhaseStationary
111 _touchCount = [active count];
118 for (
int i=0; i<
int([ended count]); ++i) {
119 NSTouch *touch = [[ended allObjects] objectAtIndex:i];
120 QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch);
122 qcocoaTouch->updateTouchData(touch, [touch phase]);
123 if (!_updateInternalStateOnly)
124 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
129 bool wasUpdateInternalStateOnly = _updateInternalStateOnly;
130 _updateInternalStateOnly = !acceptSingleTouch && _touchCount < 2;
136 for (
int i=0; i<
int([active count]); ++i) {
137 NSTouch *touch = [[active allObjects] objectAtIndex:i];
138 QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch);
140 qcocoaTouch =
new QCocoaTouch(touch);
142 qcocoaTouch->updateTouchData(touch, wasUpdateInternalStateOnly ? NSTouchPhaseBegan : [touch phase]);
143 if (!_updateInternalStateOnly)
144 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
151 if (_touchCount != _currentTouches.size()) {
156 const auto currentTouchesSnapshot = _currentTouches;
157 for (QCocoaTouch *qcocoaTouch : currentTouchesSnapshot) {
158 if (!_updateInternalStateOnly) {
159 qcocoaTouch->_touchPoint.state = QEventPoint::State::Released;
160 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
164 _currentTouches.clear();
165 _updateInternalStateOnly = !acceptSingleTouch;
166 return touchPoints.values();
173 if (_updateInternalStateOnly && !wasUpdateInternalStateOnly && !_currentTouches.isEmpty()) {
174 QCocoaTouch *qcocoaTouch = _currentTouches.cbegin().value();
175 qcocoaTouch->_touchPoint.state = QEventPoint::State::Released;
176 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
180 qcocoaTouch->_touchPoint.id = 0;
181 _idAssignmentCount = 1;
184 return touchPoints.values();
187QPointingDevice *QCocoaTouch::getTouchDevice(QInputDevice::DeviceType type, quint64 id)
189 QPointingDevice *ret = _touchDevices.value(id);
191 ret =
new QPointingDevice(type == QInputDevice::DeviceType::TouchScreen ?
"touchscreen"_L1 :
"trackpad"_L1,
192 id, type, QPointingDevice::PointerType::Finger,
193 QInputDevice::Capability::Position |
194 QInputDevice::Capability::NormalizedPosition |
195 QInputDevice::Capability::MouseEmulation,
197 QWindowSystemInterface::registerInputDevice(ret);
198 _touchDevices.insert(id, ret);