1/*
2 * Copyright (C) 2013 Samsung Electronics. All rights reserved.
3 * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies)
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "GestureRecognizer.h"
29
30#include "EwkView.h"
31#include "WKSharedAPICast.h"
32
33#if ENABLE(TOUCH_EVENTS)
34
35using namespace WebCore;
36
37namespace WebKit {
38
39const double GestureRecognizer::s_doubleTapTimeoutInSeconds = 0.4;
40const double GestureRecognizer::s_tapAndHoldTimeoutInSeconds = 1.0;
41const int GestureRecognizer::s_squaredDoubleTapThreshold = 10000;
42const int GestureRecognizer::s_squaredPanThreshold = 100;
43
44GestureRecognizer::GestureRecognizer(GestureRecognizerClient* client)
45 : m_recognizerFunction(&GestureRecognizer::noGesture)
46 , m_client(client)
47 , m_tapAndHoldTimer(0)
48 , m_doubleTapTimer(0)
49{
50}
51
52void GestureRecognizer::processTouchEvent(WKTouchEventRef eventRef)
53{
54 WKEventType type = WKTouchEventGetType(eventRef);
55 if (type == kWKEventTypeTouchCancel) {
56 reset();
57 return;
58 }
59
60 (this->*m_recognizerFunction)(type, WKTouchEventGetTouchPoints(eventRef));
61}
62
63Eina_Bool GestureRecognizer::doubleTapTimerCallback(void* data)
64{
65 GestureRecognizer* gestureRecognizer = static_cast<GestureRecognizer*>(data);
66 gestureRecognizer->m_doubleTapTimer = 0;
67
68 // If doubleTapTimer is fired we should not process double tap,
69 // so process single tap here if touched point is already released
70 // or do nothing for processing single tap when touch is released.
71 if (gestureRecognizer->m_recognizerFunction == &GestureRecognizer::doubleTapGesture) {
72 gestureRecognizer->m_client->handleSingleTap(gestureRecognizer->m_firstPressedPoint);
73 gestureRecognizer->m_recognizerFunction = &GestureRecognizer::noGesture;
74 }
75
76 return ECORE_CALLBACK_CANCEL;
77}
78
79Eina_Bool GestureRecognizer::tapAndHoldTimerCallback(void* data)
80{
81 GestureRecognizer* gestureRecognizer = static_cast<GestureRecognizer*>(data);
82 gestureRecognizer->m_tapAndHoldTimer = 0;
83 gestureRecognizer->m_client->handleTapAndHold(gestureRecognizer->m_firstPressedPoint);
84 gestureRecognizer->m_recognizerFunction = &GestureRecognizer::noGesture;
85
86 return ECORE_CALLBACK_CANCEL;
87}
88
89static inline WKPoint getPointAtIndex(WKArrayRef array, size_t index)
90{
91 WKTouchPointRef pointRef = static_cast<WKTouchPointRef>(WKArrayGetItemAtIndex(array, index));
92 ASSERT(pointRef);
93
94 return WKTouchPointGetPosition(pointRef);
95}
96
97static inline Vector<IntPoint>* createVectorWithWKArray(WKArrayRef array, size_t size)
98{
99 Vector<IntPoint>* points = new Vector<IntPoint>(size);
100 for (size_t i = 0; i < size; ++i)
101 points->uncheckedAppend(toIntPoint(getPointAtIndex(array, i)));
102
103 return points;
104}
105
106void GestureRecognizer::noGesture(WKEventType type, WKArrayRef touchPoints)
107{
108 switch (type) {
109 case kWKEventTypeTouchStart:
110 m_recognizerFunction = &GestureRecognizer::singleTapGesture;
111 m_firstPressedPoint = toIntPoint(getPointAtIndex(touchPoints, 0));
112 ASSERT(m_tapAndHoldTimer);
113 m_tapAndHoldTimer = ecore_timer_add(s_tapAndHoldTimeoutInSeconds, tapAndHoldTimerCallback, this);
114 m_doubleTapTimer = ecore_timer_add(s_doubleTapTimeoutInSeconds, doubleTapTimerCallback, this);
115 break;
116 default:
117 break;
118 }
119}
120
121void GestureRecognizer::singleTapGesture(WKEventType type, WKArrayRef touchPoints)
122{
123 switch (type) {
124 case kWKEventTypeTouchStart:
125 stopTapTimers();
126 m_recognizerFunction = &GestureRecognizer::pinchGesture;
127 m_client->handlePinchStarted(*adoptPtr(createVectorWithWKArray(touchPoints, 2)).get());
128 break;
129 case kWKEventTypeTouchMove: {
130 IntPoint currentPoint = toIntPoint(getPointAtIndex(touchPoints, 0));
131 if (m_firstPressedPoint.distanceSquaredToPoint(currentPoint) > s_squaredPanThreshold) {
132 stopTapTimers();
133 m_recognizerFunction = &GestureRecognizer::panGesture;
134 m_client->handlePanStarted(currentPoint);
135 }
136 break;
137 }
138 case kWKEventTypeTouchEnd:
139 if (m_tapAndHoldTimer) {
140 ecore_timer_del(m_tapAndHoldTimer);
141 m_tapAndHoldTimer = 0;
142 }
143
144 if (m_doubleTapTimer)
145 m_recognizerFunction = &GestureRecognizer::doubleTapGesture;
146 else {
147 m_client->handleSingleTap(m_firstPressedPoint);
148 m_recognizerFunction = &GestureRecognizer::noGesture;
149 }
150 break;
151 default:
152 break;
153 }
154}
155
156void GestureRecognizer::doubleTapGesture(WKEventType type, WKArrayRef touchPoints)
157{
158 switch (type) {
159 case kWKEventTypeTouchStart: {
160 if (m_doubleTapTimer) {
161 ecore_timer_del(m_doubleTapTimer);
162 m_doubleTapTimer = 0;
163 }
164
165 size_t numberOfTouchPoints = WKArrayGetSize(touchPoints);
166 if (numberOfTouchPoints == 1) {
167 if (m_firstPressedPoint.distanceSquaredToPoint(toIntPoint(getPointAtIndex(touchPoints, 0))) > s_squaredDoubleTapThreshold)
168 m_recognizerFunction = &GestureRecognizer::singleTapGesture;
169 } else {
170 m_recognizerFunction = &GestureRecognizer::pinchGesture;
171 m_client->handlePinchStarted(*adoptPtr(createVectorWithWKArray(touchPoints, 2)).get());
172 }
173 break;
174 }
175 case kWKEventTypeTouchMove: {
176 IntPoint currentPoint = toIntPoint(getPointAtIndex(touchPoints, 0));
177 if (m_firstPressedPoint.distanceSquaredToPoint(currentPoint) > s_squaredPanThreshold) {
178 m_recognizerFunction = &GestureRecognizer::panGesture;
179 m_client->handlePanStarted(currentPoint);
180 }
181 break;
182 }
183 case kWKEventTypeTouchEnd:
184 m_client->handleDoubleTap(m_firstPressedPoint);
185 m_recognizerFunction = &GestureRecognizer::noGesture;
186 break;
187 default:
188 break;
189 }
190}
191
192void GestureRecognizer::panGesture(WKEventType type, WKArrayRef touchPoints)
193{
194 switch (type) {
195 case kWKEventTypeTouchStart:
196 m_recognizerFunction = &GestureRecognizer::pinchGesture;
197 m_client->handlePinchStarted(*adoptPtr(createVectorWithWKArray(touchPoints, 2)).get());
198 break;
199 case kWKEventTypeTouchMove:
200 m_client->handlePan(toIntPoint(getPointAtIndex(touchPoints, 0)));
201 break;
202 case kWKEventTypeTouchEnd:
203 m_client->handlePanFinished();
204 m_recognizerFunction = &GestureRecognizer::noGesture;
205 break;
206 default:
207 break;
208 }
209}
210
211void GestureRecognizer::pinchGesture(WKEventType type, WKArrayRef touchPoints)
212{
213 size_t numberOfTouchPoints = WKArrayGetSize(touchPoints);
214 ASSERT(numberOfTouchPoints >= 2);
215
216 switch (type) {
217 case kWKEventTypeTouchMove: {
218 m_client->handlePinch(*adoptPtr(createVectorWithWKArray(touchPoints, 2)).get());
219 break;
220 }
221 case kWKEventTypeTouchEnd:
222 if (numberOfTouchPoints == 2) {
223 m_client->handlePinchFinished();
224 m_recognizerFunction = &GestureRecognizer::panGesture;
225 WKTouchPointRef pointRef;
226 for (size_t i = 0; i < numberOfTouchPoints; ++i) {
227 pointRef = static_cast<WKTouchPointRef>(WKArrayGetItemAtIndex(touchPoints, i));
228 WKTouchPointState state = WKTouchPointGetState(pointRef);
229 if (state != kWKTouchPointStateTouchReleased && state != kWKTouchPointStateTouchCancelled)
230 break;
231 }
232 ASSERT(pointRef);
233 m_client->handlePanStarted(toIntPoint(WKTouchPointGetPosition(pointRef)));
234 }
235 break;
236 default:
237 break;
238 }
239}
240
241void GestureRecognizer::reset()
242{
243 stopTapTimers();
244
245 m_recognizerFunction = &GestureRecognizer::noGesture;
246}
247
248void GestureRecognizer::stopTapTimers()
249{
250 if (m_doubleTapTimer) {
251 ecore_timer_del(m_doubleTapTimer);
252 m_doubleTapTimer = 0;
253 }
254
255 if (m_tapAndHoldTimer) {
256 ecore_timer_del(m_tapAndHoldTimer);
257 m_tapAndHoldTimer = 0;
258 }
259}
260
261} // namespace WebKit
262
263#endif // ENABLE(TOUCH_EVENTS)