1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the plugins of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qmultitouch_mac_p.h"
43 #include "qcocoahelpers.h"
45 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
49 QHash<qint64, QCocoaTouch*> QCocoaTouch::_currentTouches;
50 QPointF QCocoaTouch::_screenReferencePos;
51 QPointF QCocoaTouch::_trackpadReferencePos;
52 int QCocoaTouch::_idAssignmentCount = 0;
53 int QCocoaTouch::_touchCount = 0;
54 bool QCocoaTouch::_updateInternalStateOnly = true;
56 QCocoaTouch::QCocoaTouch(NSTouch *nstouch)
58 if (_currentTouches.size() == 0)
59 _idAssignmentCount = 0;
61 _touchPoint.id = _idAssignmentCount++;
62 _touchPoint.pressure = 1.0;
63 _identity = qint64([nstouch identity]);
64 _currentTouches.insert(_identity, this);
65 updateTouchData(nstouch, NSTouchPhaseBegan);
68 QCocoaTouch::~QCocoaTouch()
70 _currentTouches.remove(_identity);
73 void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase)
75 _touchPoint.state = toTouchPointState(phase);
76 _touchPoint.isPrimary = (_touchCount == 1);
78 // From the normalized position on the trackpad, calculate
79 // where on screen the touchpoint should be according to the
80 // reference position:
81 NSPoint npos = [nstouch normalizedPosition];
82 QPointF qnpos = QPointF(npos.x, 1 - npos.y);
83 _touchPoint.normalPosition = qnpos;
85 if (_touchPoint.id == 0 && phase == NSTouchPhaseBegan) {
86 _trackpadReferencePos = qnpos;
87 _screenReferencePos = qt_mac_flipPoint([NSEvent mouseLocation]);
90 QPointF screenPos = _screenReferencePos;
92 NSSize dsize = [nstouch deviceSize];
93 float ppiX = (qnpos.x() - _trackpadReferencePos.x()) * dsize.width;
94 float ppiY = (qnpos.y() - _trackpadReferencePos.y()) * dsize.height;
95 QPointF relativePos = _trackpadReferencePos - QPointF(ppiX, ppiY);
96 screenPos -= relativePos;
97 // Mac does not support area touch, only points, hence set width/height to 1.
98 // The touch point is supposed to be in the center of '_touchPoint.area', and
99 // since width/height is 1 it means we must subtract 0.5 from x and y.
100 screenPos.rx() -= 0.5;
101 screenPos.ry() -= 0.5;
102 _touchPoint.area = QRectF(screenPos, QSize(1, 1));
105 QCocoaTouch *QCocoaTouch::findQCocoaTouch(NSTouch *nstouch)
107 qint64 identity = qint64([nstouch identity]);
108 if (_currentTouches.contains(identity))
109 return _currentTouches.value(identity);
113 Qt::TouchPointState QCocoaTouch::toTouchPointState(NSTouchPhase nsState)
115 Qt::TouchPointState qtState = Qt::TouchPointReleased;
117 case NSTouchPhaseBegan:
118 qtState = Qt::TouchPointPressed;
120 case NSTouchPhaseMoved:
121 qtState = Qt::TouchPointMoved;
123 case NSTouchPhaseStationary:
124 qtState = Qt::TouchPointStationary;
126 case NSTouchPhaseEnded:
127 case NSTouchPhaseCancelled:
128 qtState = Qt::TouchPointReleased;
136 QList<QWindowSystemInterface::TouchPoint>
137 QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch)
139 QMap<int, QWindowSystemInterface::TouchPoint> touchPoints;
140 NSSet *ended = [event touchesMatchingPhase:NSTouchPhaseEnded | NSTouchPhaseCancelled inView:nil];
141 NSSet *active = [event
142 touchesMatchingPhase:NSTouchPhaseBegan | NSTouchPhaseMoved | NSTouchPhaseStationary
144 _touchCount = [active count];
146 // First: remove touches that were ended by the user. If we are
147 // currently not accepting single touches, a corresponding 'begin'
148 // has never been send to the app for these events.
149 // So should therefore not send the following removes either.
151 for (int i=0; i<int([ended count]); ++i) {
152 NSTouch *touch = [[ended allObjects] objectAtIndex:i];
153 QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch);
155 qcocoaTouch->updateTouchData(touch, [touch phase]);
156 if (!_updateInternalStateOnly)
157 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
162 bool wasUpdateInternalStateOnly = _updateInternalStateOnly;
163 _updateInternalStateOnly = !acceptSingleTouch && _touchCount < 2;
165 // Next: update, or create, existing touches.
166 // We always keep track of all touch points, even
167 // when not accepting single touches.
169 for (int i=0; i<int([active count]); ++i) {
170 NSTouch *touch = [[active allObjects] objectAtIndex:i];
171 QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch);
173 qcocoaTouch = new QCocoaTouch(touch);
175 qcocoaTouch->updateTouchData(touch, wasUpdateInternalStateOnly ? NSTouchPhaseBegan : [touch phase]);
176 if (!_updateInternalStateOnly)
177 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
180 // Next: sadly, we need to check that our touch hash is in
181 // sync with cocoa. This is typically not the case after a system
182 // gesture happend (like a four-finger-swipe to show expose).
184 if (_touchCount != _currentTouches.size()) {
185 // Remove all instances, and basically start from scratch:
187 foreach (QCocoaTouch *qcocoaTouch, _currentTouches.values()) {
188 if (!_updateInternalStateOnly) {
189 qcocoaTouch->_touchPoint.state = Qt::TouchPointReleased;
190 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
194 _currentTouches.clear();
195 _updateInternalStateOnly = !acceptSingleTouch;
196 return touchPoints.values();
199 // Finally: If this call _started_ to reject single
200 // touches, we need to fake a relase for the remaining
201 // touch now (and refake a begin for it later, if needed).
203 if (_updateInternalStateOnly && !wasUpdateInternalStateOnly && !_currentTouches.isEmpty()) {
204 QCocoaTouch *qcocoaTouch = _currentTouches.values().first();
205 qcocoaTouch->_touchPoint.state = Qt::TouchPointReleased;
206 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
207 // Since this last touch also will end up beeing the first
208 // touch (if the user adds a second finger without lifting
209 // the first), we promote it to be the primary touch:
210 qcocoaTouch->_touchPoint.id = 0;
211 _idAssignmentCount = 1;
214 return touchPoints.values();
219 #endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6