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
qmultitouch_mac.mm
Go to the documentation of this file.
1// Copyright (C) 2016 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
4#include <AppKit/AppKit.h>
5
6#include "qmultitouch_mac_p.h"
7#include "qcocoahelpers.h"
8#include "qcocoascreen.h"
9#include <private/qpointingdevice_p.h>
10
12
13using namespace Qt::StringLiterals;
14
15Q_CONSTINIT QHash<qint64, QCocoaTouch*> QCocoaTouch::_currentTouches;
16Q_CONSTINIT QHash<quint64, QPointingDevice*> QCocoaTouch::_touchDevices;
17Q_CONSTINIT QPointF QCocoaTouch::_screenReferencePos;
18Q_CONSTINIT QPointF QCocoaTouch::_trackpadReferencePos;
19int QCocoaTouch::_idAssignmentCount = 0;
20int QCocoaTouch::_touchCount = 0;
21bool QCocoaTouch::_updateInternalStateOnly = true;
22
23QCocoaTouch::QCocoaTouch(NSTouch *nstouch)
24{
25 if (_currentTouches.size() == 0)
26 _idAssignmentCount = 0;
27
28 _touchPoint.id = _idAssignmentCount++;
29 _touchPoint.pressure = 1.0;
30 _identity = qint64([nstouch identity]);
31 _currentTouches.insert(_identity, this);
32 updateTouchData(nstouch, NSTouchPhaseBegan);
33}
34
35QCocoaTouch::~QCocoaTouch()
36{
37 _currentTouches.remove(_identity);
38}
39
40void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase)
41{
42 _touchPoint.state = toTouchPointState(phase);
43
44 // From the normalized position on the trackpad, calculate
45 // where on screen the touchpoint should be according to the
46 // reference position:
47 NSPoint npos = [nstouch normalizedPosition];
48 QPointF qnpos = QPointF(npos.x, 1 - npos.y);
49 _touchPoint.normalPosition = qnpos;
50
51 if (_touchPoint.id == 0 && phase == NSTouchPhaseBegan) {
52 _trackpadReferencePos = qnpos;
53 _screenReferencePos = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
54 }
55
56 QPointF screenPos = _screenReferencePos;
57
58 NSSize dsize = [nstouch deviceSize];
59 float ppiX = (qnpos.x() - _trackpadReferencePos.x()) * dsize.width;
60 float ppiY = (qnpos.y() - _trackpadReferencePos.y()) * dsize.height;
61 QPointF relativePos = _trackpadReferencePos - QPointF(ppiX, ppiY);
62 screenPos -= relativePos;
63 // Mac does not support area touch, only points, hence set width/height to 1.
64 // The touch point is supposed to be in the center of '_touchPoint.area', and
65 // since width/height is 1 it means we must subtract 0.5 from x and y.
66 screenPos.rx() -= 0.5;
67 screenPos.ry() -= 0.5;
68 _touchPoint.area = QRectF(screenPos, QSize(1, 1));
69}
70
71QCocoaTouch *QCocoaTouch::findQCocoaTouch(NSTouch *nstouch)
72{
73 qint64 identity = qint64([nstouch identity]);
74 if (_currentTouches.contains(identity))
75 return _currentTouches.value(identity);
76 return nullptr;
77}
78
79QEventPoint::State QCocoaTouch::toTouchPointState(NSTouchPhase nsState)
80{
82 switch (nsState) {
83 case NSTouchPhaseBegan:
85 break;
86 case NSTouchPhaseMoved:
88 break;
89 case NSTouchPhaseStationary:
91 break;
92 case NSTouchPhaseEnded:
93 case NSTouchPhaseCancelled:
95 break;
96 default:
97 break;
98 }
99 return qtState;
100}
101
102QList<QWindowSystemInterface::TouchPoint>
103QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch)
104{
105 QMap<int, QWindowSystemInterface::TouchPoint> touchPoints;
106 NSSet *ended = [event touchesMatchingPhase:NSTouchPhaseEnded | NSTouchPhaseCancelled inView:nil];
107 NSSet *active = [event
108 touchesMatchingPhase:NSTouchPhaseBegan | NSTouchPhaseMoved | NSTouchPhaseStationary
109 inView:nil];
110 _touchCount = [active count];
111
112 // First: remove touches that were ended by the user. If we are
113 // currently not accepting single touches, a corresponding 'begin'
114 // has never been send to the app for these events.
115 // So should therefore not send the following removes either.
116
117 for (int i=0; i<int([ended count]); ++i) {
118 NSTouch *touch = [[ended allObjects] objectAtIndex:i];
119 QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch);
120 if (qcocoaTouch) {
121 qcocoaTouch->updateTouchData(touch, [touch phase]);
122 if (!_updateInternalStateOnly)
123 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
124 delete qcocoaTouch;
125 }
126 }
127
128 bool wasUpdateInternalStateOnly = _updateInternalStateOnly;
129 _updateInternalStateOnly = !acceptSingleTouch && _touchCount < 2;
130
131 // Next: update, or create, existing touches.
132 // We always keep track of all touch points, even
133 // when not accepting single touches.
134
135 for (int i=0; i<int([active count]); ++i) {
136 NSTouch *touch = [[active allObjects] objectAtIndex:i];
137 QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch);
138 if (!qcocoaTouch)
139 qcocoaTouch = new QCocoaTouch(touch);
140 else
141 qcocoaTouch->updateTouchData(touch, wasUpdateInternalStateOnly ? NSTouchPhaseBegan : [touch phase]);
142 if (!_updateInternalStateOnly)
143 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
144 }
145
146 // Next: sadly, we need to check that our touch hash is in
147 // sync with cocoa. This is typically not the case after a system
148 // gesture happened (like a four-finger-swipe to show expose).
149
150 if (_touchCount != _currentTouches.size()) {
151 // Remove all instances, and basically start from scratch:
152 touchPoints.clear();
153 // Deleting touch points will remove them from current touches,
154 // so we make a copy of the touches before iterating them.
155 const auto currentTouchesSnapshot = _currentTouches;
156 for (QCocoaTouch *qcocoaTouch : currentTouchesSnapshot) {
157 if (!_updateInternalStateOnly) {
158 qcocoaTouch->_touchPoint.state = QEventPoint::State::Released;
159 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
160 }
161 delete qcocoaTouch;
162 }
163 _currentTouches.clear();
164 _updateInternalStateOnly = !acceptSingleTouch;
165 return touchPoints.values();
166 }
167
168 // Finally: If this call _started_ to reject single
169 // touches, we need to fake a release for the remaining
170 // touch now (and refake a begin for it later, if needed).
171
172 if (_updateInternalStateOnly && !wasUpdateInternalStateOnly && !_currentTouches.isEmpty()) {
173 QCocoaTouch *qcocoaTouch = _currentTouches.cbegin().value();
174 qcocoaTouch->_touchPoint.state = QEventPoint::State::Released;
175 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
176 // Since this last touch also will end up being the first
177 // touch (if the user adds a second finger without lifting
178 // the first), we promote it to be the primary touch:
179 qcocoaTouch->_touchPoint.id = 0;
180 _idAssignmentCount = 1;
181 }
182
183 return touchPoints.values();
184}
185
201
static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen=QCocoaScreen::primaryScreen())
static QList< QWindowSystemInterface::TouchPoint > getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch)
static QPointingDevice * getTouchDevice(QInputDevice::DeviceType type, quint64 id)
State
Specifies the state of this event point.
Definition qeventpoint.h:48
DeviceType
This enum represents the type of device that generated a QPointerEvent.
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal & ry() noexcept
Returns a reference to the y coordinate of this point.
Definition qpoint.h:368
constexpr qreal & rx() noexcept
Returns a reference to the x coordinate of this point.
Definition qpoint.h:363
The QPointingDevice class describes a device from which mouse, touch or tablet events originate.
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore
Definition qsize.h:25
static void registerInputDevice(const QInputDevice *device)
Combined button and popup list for selecting options.
return ret
GLenum GLuint id
[7]
GLenum GLenum GLsizei count
GLenum type
struct _cl_event * event
unsigned long long quint64
Definition qtypes.h:61
long long qint64
Definition qtypes.h:60