Clean up the code to build successfully on macOS
[platform/core/uifw/dali-core.git] / dali / integration-api / events / touch-event-combiner.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/integration-api/events/touch-event-combiner.h>
20
21 // EXTERNAL INCLUDES
22 #include <algorithm>
23 #include <cmath> // abs<float>
24
25 // INTERNAL INCLUDES
26 #include <dali/integration-api/events/hover-event-integ.h>
27 #include <dali/integration-api/events/touch-event-integ.h>
28 #include <dali/public-api/common/dali-common.h>
29
30 namespace Dali
31 {
32 namespace Integration
33 {
34 namespace
35 {
36 const unsigned long DEFAULT_MINIMUM_MOTION_TIME(1u);
37 const Vector2       DEFAULT_MINIMUM_MOTION_DISTANCE(1.0f, 1.0f);
38 } // unnamed namespace
39
40 struct TouchEventCombiner::PointInfo
41 {
42   // Construction
43
44   /**
45    * Constructor
46    * @param[in]  touchPoint  The point to add.
47    * @param[in]  pointTime   The time of the point event.
48    */
49   PointInfo(const Point& touchPoint, uint32_t pointTime)
50   : point(touchPoint),
51     time(pointTime)
52   {
53   }
54
55   // Data
56
57   Point    point; ///< The point.
58   uint32_t time;  ///< The time the point event took place.
59 };
60
61 TouchEventCombiner::TouchEventCombiner()
62 : mMinMotionTime(DEFAULT_MINIMUM_MOTION_TIME),
63   mMinMotionDistance(DEFAULT_MINIMUM_MOTION_DISTANCE)
64 {
65 }
66
67 TouchEventCombiner::TouchEventCombiner(uint32_t minMotionTime, float minMotionXDistance, float minMotionYDistance)
68 : mMinMotionTime(minMotionTime),
69   mMinMotionDistance(minMotionXDistance, minMotionYDistance)
70 {
71   DALI_ASSERT_ALWAYS(minMotionXDistance >= 0.0f && minMotionYDistance >= 0.0f && "Negative values not allowed\n");
72 }
73
74 TouchEventCombiner::TouchEventCombiner(uint32_t minMotionTime, Vector2 minMotionDistance)
75 : mMinMotionTime(minMotionTime),
76   mMinMotionDistance(minMotionDistance)
77 {
78   DALI_ASSERT_ALWAYS(minMotionDistance.x >= 0.0f && minMotionDistance.y >= 0.0f && "Negative values not allowed\n");
79 }
80
81 TouchEventCombiner::~TouchEventCombiner()
82 {
83 }
84
85 TouchEventCombiner::EventDispatchType TouchEventCombiner::GetNextTouchEvent(const Point& point, uint32_t time, TouchEvent& touchEvent, HoverEvent& hoverEvent)
86 {
87   TouchEventCombiner::EventDispatchType dispatchEvent(TouchEventCombiner::DISPATCH_NONE);
88   const PointState::Type                state    = point.GetState();
89   const int                             deviceId = point.GetDeviceId();
90
91   switch(state)
92   {
93     case PointState::STARTED:
94     {
95       touchEvent.time = time;
96       bool addToContainer(true);
97
98       // Iterate through already stored touch points and add to TouchEvent
99       for(PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter)
100       {
101         if(iter->point.GetDeviceId() != deviceId)
102         {
103           iter->point.SetState(PointState::STATIONARY);
104         }
105         else
106         {
107           // System has sent us two down points for the same point ID, update our stored data to latest.
108           // We do not want to emit another down event for this Point Device ID.
109
110           addToContainer = false;
111           iter->point    = point;
112           iter->time     = time;
113         }
114         touchEvent.AddPoint(iter->point);
115       }
116
117       // Add new touch point to the list and to the TouchEvent
118       if(addToContainer)
119       {
120         mPressedPoints.push_back(PointInfo(point, time));
121         touchEvent.AddPoint(point);
122         dispatchEvent = TouchEventCombiner::DISPATCH_TOUCH; // Only dispatch touch event if just added to container
123
124         // Check whether hover event was dispatched previously
125         if(!mHoveredPoints.empty())
126         {
127           hoverEvent.time = time;
128
129           PointInfoContainer::iterator match(mHoveredPoints.end());
130           for(PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter)
131           {
132             if(deviceId == iter->point.GetDeviceId())
133             {
134               match = iter;
135               // Add new point to the HoverEvent
136               iter->point.SetState(PointState::FINISHED);
137               hoverEvent.AddPoint(iter->point);
138             }
139             else
140             {
141               iter->point.SetState(PointState::STATIONARY);
142               hoverEvent.AddPoint(iter->point);
143             }
144           }
145
146           if(match != mHoveredPoints.end())
147           {
148             mHoveredPoints.erase(match);
149             dispatchEvent = TouchEventCombiner::DISPATCH_BOTH; // We should only dispatch hover events if the point was actually hovered in this window
150           }
151         }
152       }
153
154       break;
155     }
156
157     case PointState::FINISHED:
158     {
159       touchEvent.time = time;
160
161       // Find pressed touch point in local list (while also adding the stored points to the touchEvent)
162       PointInfoContainer::iterator match(mPressedPoints.end());
163       for(PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter)
164       {
165         if(deviceId == iter->point.GetDeviceId())
166         {
167           match = iter;
168
169           // Add new point to the TouchEvent
170           touchEvent.AddPoint(point);
171         }
172         else
173         {
174           iter->point.SetState(PointState::STATIONARY);
175           touchEvent.AddPoint(iter->point);
176         }
177       }
178
179       if(match != mPressedPoints.end())
180       {
181         mPressedPoints.erase(match);
182         dispatchEvent = TouchEventCombiner::DISPATCH_TOUCH; // We should only dispatch touch events if the point was actually pressed in this window
183
184         // Iterate through already stored touch points for HoverEvent and delete them
185         for(PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter)
186         {
187           if(iter->point.GetDeviceId() == deviceId)
188           {
189             iter = mHoveredPoints.erase(iter);
190           }
191         }
192       }
193       break;
194     }
195
196     case PointState::MOTION:
197     {
198       bool fromNewDeviceId = false;
199
200       if(!mPressedPoints.empty())
201       {
202         touchEvent.time = time;
203
204         bool                         ignore              = false;
205         PointInfoContainer::iterator match               = mPressedPoints.end();
206         const Vector2&               pointScreenPosition = point.GetScreenPosition();
207         for(PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter)
208         {
209           if(deviceId == iter->point.GetDeviceId())
210           {
211             uint32_t timeDiff(time - iter->time);
212
213             if(timeDiff < mMinMotionTime)
214             {
215               // Motion event sent too soon after previous event so ignore
216               ignore = true;
217               break;
218             }
219
220             const Vector2& currentScreenPosition = iter->point.GetScreenPosition();
221             if((std::abs(pointScreenPosition.x - currentScreenPosition.x) < mMinMotionDistance.x) &&
222                (std::abs(pointScreenPosition.y - currentScreenPosition.y) < mMinMotionDistance.y))
223             {
224               // Not enough positional change from last event so ignore
225               ignore = true;
226               break;
227             }
228
229             match = iter;
230
231             // Add new touch point to the TouchEvent
232             touchEvent.AddPoint(point);
233           }
234           else
235           {
236             iter->point.SetState(PointState::STATIONARY);
237             touchEvent.AddPoint(iter->point);
238           }
239         }
240
241         if(match != mPressedPoints.end())
242         {
243           PointInfo matchedPoint(point, time);
244           std::swap(*match, matchedPoint);
245
246           dispatchEvent = TouchEventCombiner::DISPATCH_TOUCH; // Dispatch touch event
247         }
248         else if(!ignore)
249         {
250           fromNewDeviceId = true;
251         }
252       }
253
254       // Dispatch hover event if no previous down event received or the motion event comes from a new device ID
255       if(mPressedPoints.empty() || fromNewDeviceId)
256       {
257         hoverEvent.time = time;
258
259         // Iterate through already stored touch points and add to HoverEvent
260         bool                         ignore              = false;
261         PointInfoContainer::iterator match               = mHoveredPoints.end();
262         const Vector2&               pointScreenPosition = point.GetScreenPosition();
263         for(PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter)
264         {
265           if(iter->point.GetDeviceId() == deviceId)
266           {
267             uint32_t timeDiff(time - iter->time);
268
269             if(timeDiff < mMinMotionTime)
270             {
271               // Motion event sent too soon after previous event so ignore
272               ignore = true;
273               break;
274             }
275
276             const Vector2& currentScreenPosition = iter->point.GetScreenPosition();
277             if((std::abs(pointScreenPosition.x - currentScreenPosition.x) < mMinMotionDistance.x) &&
278                (std::abs(pointScreenPosition.y - currentScreenPosition.y) < mMinMotionDistance.y))
279             {
280               // Not enough positional change from last event so ignore
281               ignore = true;
282               break;
283             }
284
285             match = iter;
286
287             // Add new touch point to the HoverEvent
288             hoverEvent.AddPoint(point);
289           }
290           else
291           {
292             iter->point.SetState(PointState::STATIONARY);
293             hoverEvent.AddPoint(iter->point);
294           }
295         }
296
297         // Add new hover point to the list and to the HoverEvent
298         if(!ignore) // Only dispatch hover event when it should not be ignored
299         {
300           if(match == mHoveredPoints.end())
301           {
302             Point hoverPoint(point);
303             hoverPoint.SetState(PointState::STARTED); // The first hover event received
304             mHoveredPoints.push_back(PointInfo(hoverPoint, time));
305             hoverEvent.AddPoint(hoverPoint);
306           }
307           else
308           {
309             PointInfo matchedPoint(point, time);
310             std::swap(*match, matchedPoint);
311           }
312
313           if(dispatchEvent == TouchEventCombiner::DISPATCH_TOUCH)
314           {
315             dispatchEvent = TouchEventCombiner::DISPATCH_BOTH;
316           }
317           else
318           {
319             dispatchEvent = TouchEventCombiner::DISPATCH_HOVER;
320           }
321         }
322       }
323       break;
324     }
325
326     case PointState::INTERRUPTED:
327     {
328       Reset();
329
330       // We should still tell core about the interruption.
331       touchEvent.AddPoint(point);
332       hoverEvent.AddPoint(point);
333       dispatchEvent = TouchEventCombiner::DISPATCH_BOTH;
334       break;
335     }
336
337     default:
338       break;
339   }
340
341   return dispatchEvent;
342 }
343
344 void TouchEventCombiner::SetMinimumMotionTimeThreshold(uint32_t minTime)
345 {
346   mMinMotionTime = minTime;
347 }
348
349 void TouchEventCombiner::SetMinimumMotionDistanceThreshold(float minDistance)
350 {
351   DALI_ASSERT_ALWAYS(minDistance >= 0.0f && "Negative values not allowed\n");
352
353   mMinMotionDistance.x = mMinMotionDistance.y = minDistance;
354 }
355
356 void TouchEventCombiner::SetMinimumMotionDistanceThreshold(float minXDistance, float minYDistance)
357 {
358   DALI_ASSERT_ALWAYS(minXDistance >= 0.0f && minYDistance >= 0.0f && "Negative values not allowed\n");
359
360   mMinMotionDistance.x = minXDistance;
361   mMinMotionDistance.y = minYDistance;
362 }
363
364 void TouchEventCombiner::SetMinimumMotionDistanceThreshold(Vector2 minDistance)
365 {
366   DALI_ASSERT_ALWAYS(minDistance.x >= 0.0f && minDistance.y >= 0.0f && "Negative values not allowed\n");
367
368   mMinMotionDistance = minDistance;
369 }
370
371 unsigned long TouchEventCombiner::GetMinimumMotionTimeThreshold() const
372 {
373   return mMinMotionTime;
374 }
375
376 Vector2 TouchEventCombiner::GetMinimumMotionDistanceThreshold() const
377 {
378   return mMinMotionDistance;
379 }
380
381 void TouchEventCombiner::Reset()
382 {
383   mPressedPoints.clear();
384   mHoveredPoints.clear();
385 }
386
387 } // namespace Integration
388
389 } // namespace Dali