Qt 4.8
qmultitouch_mac.mm
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include <private/qmultitouch_mac_p.h>
43 #include <qcursor.h>
44 
45 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
46 
48 
49 #ifdef QT_MAC_USE_COCOA
50 
51 QHash<qint64, QCocoaTouch*> QCocoaTouch::_currentTouches;
52 QPointF QCocoaTouch::_screenReferencePos;
53 QPointF QCocoaTouch::_trackpadReferencePos;
54 int QCocoaTouch::_idAssignmentCount = 0;
55 int QCocoaTouch::_touchCount = 0;
56 bool QCocoaTouch::_updateInternalStateOnly = true;
57 
58 QCocoaTouch::QCocoaTouch(NSTouch *nstouch)
59 {
60  if (_currentTouches.size() == 0)
61  _idAssignmentCount = 0;
62 
63  _touchPoint.setId(_idAssignmentCount++);
64  _touchPoint.setPressure(1.0);
65  _identity = qint64([nstouch identity]);
66  _currentTouches.insert(_identity, this);
67  updateTouchData(nstouch, NSTouchPhaseBegan);
68 }
69 
70 QCocoaTouch::~QCocoaTouch()
71 {
72  _currentTouches.remove(_identity);
73 }
74 
75 void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase)
76 {
77  if (_touchCount == 1)
78  _touchPoint.setState(toTouchPointState(phase) | Qt::TouchPointPrimary);
79  else
80  _touchPoint.setState(toTouchPointState(phase));
81 
82  // From the normalized position on the trackpad, calculate
83  // where on screen the touchpoint should be according to the
84  // reference position:
85  NSPoint npos = [nstouch normalizedPosition];
86  QPointF qnpos = QPointF(npos.x, 1 - npos.y);
87  _touchPoint.setNormalizedPos(qnpos);
88 
89  if (_touchPoint.id() == 0 && phase == NSTouchPhaseBegan) {
90  _trackpadReferencePos = qnpos;
91  _screenReferencePos = QCursor::pos();
92  }
93 
94  NSSize dsize = [nstouch deviceSize];
95  float ppiX = (qnpos.x() - _trackpadReferencePos.x()) * dsize.width;
96  float ppiY = (qnpos.y() - _trackpadReferencePos.y()) * dsize.height;
97  QPointF relativePos = _trackpadReferencePos - QPointF(ppiX, ppiY);
98  _touchPoint.setScreenPos(_screenReferencePos - relativePos);
99 }
100 
101 QCocoaTouch *QCocoaTouch::findQCocoaTouch(NSTouch *nstouch)
102 {
103  qint64 identity = qint64([nstouch identity]);
104  if (_currentTouches.contains(identity))
105  return _currentTouches.value(identity);
106  return 0;
107 }
108 
109 Qt::TouchPointState QCocoaTouch::toTouchPointState(NSTouchPhase nsState)
110 {
112  switch (nsState) {
113  case NSTouchPhaseBegan:
114  qtState = Qt::TouchPointPressed;
115  break;
116  case NSTouchPhaseMoved:
117  qtState = Qt::TouchPointMoved;
118  break;
119  case NSTouchPhaseStationary:
120  qtState = Qt::TouchPointStationary;
121  break;
122  case NSTouchPhaseEnded:
123  case NSTouchPhaseCancelled:
124  qtState = Qt::TouchPointReleased;
125  break;
126  default:
127  break;
128  }
129  return qtState;
130 }
131 
133 QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch)
134 {
136  NSSet *ended = [event touchesMatchingPhase:NSTouchPhaseEnded | NSTouchPhaseCancelled inView:nil];
137  NSSet *active = [event
138  touchesMatchingPhase:NSTouchPhaseBegan | NSTouchPhaseMoved | NSTouchPhaseStationary
139  inView:nil];
140  _touchCount = [active count];
141 
142  // First: remove touches that were ended by the user. If we are
143  // currently not accepting single touches, a corresponding 'begin'
144  // has never been send to the app for these events.
145  // So should therefore not send the following removes either.
146 
147  for (int i=0; i<int([ended count]); ++i) {
148  NSTouch *touch = [[ended allObjects] objectAtIndex:i];
149  QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch);
150  if (qcocoaTouch) {
151  qcocoaTouch->updateTouchData(touch, [touch phase]);
152  if (!_updateInternalStateOnly)
153  touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint);
154  delete qcocoaTouch;
155  }
156  }
157 
158  bool wasUpdateInternalStateOnly = _updateInternalStateOnly;
159  _updateInternalStateOnly = !acceptSingleTouch && _touchCount < 2;
160 
161  // Next: update, or create, existing touches.
162  // We always keep track of all touch points, even
163  // when not accepting single touches.
164 
165  for (int i=0; i<int([active count]); ++i) {
166  NSTouch *touch = [[active allObjects] objectAtIndex:i];
167  QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch);
168  if (!qcocoaTouch)
169  qcocoaTouch = new QCocoaTouch(touch);
170  else
171  qcocoaTouch->updateTouchData(touch, wasUpdateInternalStateOnly ? NSTouchPhaseBegan : [touch phase]);
172  if (!_updateInternalStateOnly)
173  touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint);
174  }
175 
176  // Next: sadly, we need to check that our touch hash is in
177  // sync with cocoa. This is typically not the case after a system
178  // gesture happend (like a four-finger-swipe to show expose).
179 
180  if (_touchCount != _currentTouches.size()) {
181  // Remove all instances, and basically start from scratch:
182  touchPoints.clear();
183  foreach (QCocoaTouch *qcocoaTouch, _currentTouches.values()) {
184  if (!_updateInternalStateOnly) {
185  qcocoaTouch->_touchPoint.setState(Qt::TouchPointReleased);
186  touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint);
187  }
188  delete qcocoaTouch;
189  }
190  _currentTouches.clear();
191  _updateInternalStateOnly = !acceptSingleTouch;
192  return touchPoints.values();
193  }
194 
195  // Finally: If this call _started_ to reject single
196  // touches, we need to fake a relase for the remaining
197  // touch now (and refake a begin for it later, if needed).
198 
199  if (_updateInternalStateOnly && !wasUpdateInternalStateOnly && !_currentTouches.isEmpty()) {
200  QCocoaTouch *qcocoaTouch = _currentTouches.values().first();
201  qcocoaTouch->_touchPoint.setState(Qt::TouchPointReleased);
202  touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint);
203  // Since this last touch also will end up beeing the first
204  // touch (if the user adds a second finger without lifting
205  // the first), we promote it to be the primary touch:
206  qcocoaTouch->_touchPoint.setId(0);
207  _idAssignmentCount = 1;
208  }
209 
210  return touchPoints.values();
211 }
212 
213 #endif
214 
216 
217 #endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
218 
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
EventRef event
QList< T > values() const
Returns a list containing all the values in the map, in ascending order of their keys.
Definition: qmap.h:863
The QPointF class defines a point in the plane using floating point precision.
Definition: qpoint.h:214
The QHash class is a template class that provides a hash-table-based dictionary.
Definition: qdatastream.h:66
qreal x() const
Returns the x-coordinate of this point.
Definition: qpoint.h:282
void setState(Qt::TouchPointStates state)
Definition: qevent.cpp:4671
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
__int64 qint64
Definition: qglobal.h:942
T & first()
Returns a reference to the first item in the list.
Definition: qlist.h:282
iterator insert(const Key &key, const T &value)
Inserts a new item with the key key and a value of value.
Definition: qmap.h:559
struct CGPoint NSPoint
TouchPointState
This enum represents the state of a touch point at the time the QTouchEvent occurred.
Definition: qnamespace.h:1738
qreal y() const
Returns the y-coordinate of this point.
Definition: qpoint.h:287
void clear()
Removes all items from the map.
Definition: qmap.h:444
static QPoint pos()
Returns the position of the cursor (hot spot) in global screen coordinates.
Definition: qcursor_mac.mm:310