Add trace log for touch, wheel and gesture
[platform/core/uifw/dali-core.git] / dali / internal / event / events / touch-event-processor.cpp
1 /*
2  * Copyright (c) 2023 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/internal/event/events/touch-event-processor.h>
20
21 #if defined(DEBUG_ENABLED)
22 #include <sstream>
23 #endif
24
25 // INTERNAL INCLUDES
26 #include <dali/integration-api/debug.h>
27 #include <dali/integration-api/events/touch-event-integ.h>
28 #include <dali/integration-api/trace.h>
29 #include <dali/internal/event/actors/actor-impl.h>
30 #include <dali/internal/event/actors/layer-impl.h>
31 #include <dali/internal/event/common/scene-impl.h>
32 #include <dali/internal/event/events/hit-test-algorithm-impl.h>
33 #include <dali/internal/event/events/multi-point-event-util.h>
34 #include <dali/internal/event/events/touch-event-impl.h>
35 #include <dali/internal/event/render-tasks/render-task-impl.h>
36 #include <dali/public-api/events/touch-event.h>
37 #include <dali/public-api/math/vector2.h>
38 #include <dali/public-api/signals/callback.h>
39
40 namespace Dali
41 {
42 namespace Internal
43 {
44 namespace
45 {
46 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TOUCH_PROCESSOR");
49
50 const char* TOUCH_POINT_STATE[6] =
51   {
52     "DOWN",
53     "UP",
54     "MOTION",
55     "LEAVE",
56     "STATIONARY",
57     "INTERRUPTED",
58 };
59
60 #endif // defined(DEBUG_ENABLED)
61
62 Dali::Actor EmitInterceptTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
63 {
64   Dali::Actor interceptedActor;
65
66   if(actor)
67   {
68     Dali::Actor parent = actor.GetParent();
69     if(parent)
70     {
71       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
72       // Recursively deliver events to the actor and its parents for intercept touch event.
73       interceptedActor = EmitInterceptTouchSignals(parent, touchEvent);
74     }
75
76     if(!interceptedActor)
77     {
78       bool   intercepted = false;
79       Actor& actorImpl(GetImplementation(actor));
80       if(actorImpl.GetInterceptTouchRequired())
81       {
82         intercepted = actorImpl.EmitInterceptTouchEventSignal(touchEvent);
83         if(intercepted)
84         {
85           interceptedActor = Dali::Actor(&actorImpl);
86         }
87       }
88     }
89   }
90
91   return interceptedActor;
92 }
93
94 /**
95  *  Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
96  */
97 Dali::Actor EmitTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
98 {
99   Dali::Actor consumedActor;
100
101   if(actor)
102   {
103     Dali::Actor oldParent(actor.GetParent());
104
105     Actor& actorImpl(GetImplementation(actor));
106
107     bool consumed(false);
108
109     // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
110     if(actorImpl.GetTouchRequired())
111     {
112       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
113       consumed = actorImpl.EmitTouchEventSignal(touchEvent);
114     }
115
116     if(consumed)
117     {
118       // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
119       consumedActor = Dali::Actor(&actorImpl);
120     }
121     else
122     {
123       // The actor may have been removed/reparented during the signal callbacks.
124       Dali::Actor parent = actor.GetParent();
125
126       if(parent &&
127          (parent == oldParent))
128       {
129         // One of the actor's parents may consumed the event and they should be set as the consumed actor.
130         consumedActor = EmitTouchSignals(parent, touchEvent);
131       }
132     }
133   }
134
135   return consumedActor;
136 }
137
138 Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point)
139 {
140   TouchEventPtr    touchEvent(new TouchEvent(time));
141   Dali::TouchEvent touchEventHandle(touchEvent.Get());
142
143   touchEvent->AddPoint(point);
144
145   return EmitTouchSignals(actor, touchEventHandle);
146 }
147
148 /**
149  * Changes the state of the primary point to leave and emits the touch signals
150  */
151 Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state)
152 {
153   Dali::Actor consumingActor;
154
155   if(actor)
156   {
157     TouchEventPtr touchEventImpl = TouchEvent::Clone(*originalTouchEvent.Get());
158
159     Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
160
161     const Vector2& screenPosition = primaryPoint.GetScreenPosition();
162     Vector2        localPosition;
163     actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
164
165     primaryPoint.SetLocalPosition(localPosition);
166     primaryPoint.SetHitActor(Dali::Actor(actor));
167     primaryPoint.SetState(state);
168
169     consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get()));
170   }
171
172   return consumingActor;
173 }
174
175 /**
176  * @brief Parses the primary touch point by performing a hit-test if necessary
177  *
178  * @param[out] hitTestResults The hit test results are put into this variable
179  * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member
180  * @param[in] lastRenderTask The last render task member
181  * @param[in] currentPoint The current point information
182  * @param[in] scene The scene that this touch is related to
183  */
184 void ParsePrimaryTouchPoint(
185   HitTestAlgorithm::Results& hitTestResults,
186   ActorObserver&             capturingTouchActorObserver,
187   ActorObserver&             ownTouchActorObserver,
188   const RenderTaskPtr&       lastRenderTask,
189   const Integration::Point&  currentPoint,
190   const Internal::Scene&     scene)
191 {
192   Actor* capturingTouchActor = capturingTouchActorObserver.GetActor();
193
194   // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it
195   if(capturingTouchActor && lastRenderTask)
196   {
197     hitTestResults.actor          = Dali::Actor(capturingTouchActor);
198     hitTestResults.renderTask     = lastRenderTask;
199     const Vector2& screenPosition = currentPoint.GetScreenPosition();
200     capturingTouchActor->ScreenToLocal(*lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
201   }
202   else
203   {
204     Actor* ownTouchActor = ownTouchActorObserver.GetActor();
205     HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor);
206
207     if(currentPoint.GetState() == PointState::STARTED && hitTestResults.actor)
208     {
209       // If we've just started touch, then check whether the actor has requested to capture all touch events
210       Actor* hitActor = &GetImplementation(hitTestResults.actor);
211       if(hitActor->CapturesAllTouchAfterStart())
212       {
213         capturingTouchActorObserver.SetActor(hitActor);
214       }
215       if(hitActor->IsAllowedOnlyOwnTouch())
216       {
217         ownTouchActorObserver.SetActor(hitActor);
218       }
219     }
220   }
221 }
222
223 } // unnamed namespace
224
225 TouchEventProcessor::TouchEventProcessor(Scene& scene)
226 : mScene(scene),
227   mLastPrimaryHitActor(MakeCallback(this, &TouchEventProcessor::OnObservedActorDisconnected)),
228   mLastConsumedActor(),
229   mCapturingTouchActor(),
230   mOwnTouchActor(),
231   mTouchDownConsumedActor(),
232   mLastRenderTask(),
233   mLastPrimaryPointState(PointState::FINISHED)
234 {
235   DALI_LOG_TRACE_METHOD(gLogFilter);
236 }
237
238 TouchEventProcessor::~TouchEventProcessor()
239 {
240   DALI_LOG_TRACE_METHOD(gLogFilter);
241 }
242
243 void TouchEventProcessor::Clear()
244 {
245   mLastPrimaryHitActor.SetActor(nullptr);
246   mLastConsumedActor.SetActor(nullptr);
247   mCapturingTouchActor.SetActor(nullptr);
248   mOwnTouchActor.SetActor(nullptr);
249   mLastRenderTask.Reset();
250   mLastPrimaryPointState = PointState::FINISHED;
251 }
252
253 bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event)
254 {
255   DALI_LOG_TRACE_METHOD(gLogFilter);
256   DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty TouchEvent sent from Integration\n");
257
258   PRINT_HIERARCHY(gLogFilter);
259
260   DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT");
261
262   // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
263   //    and emit the stage signal as well.
264
265   if(event.points[0].GetState() == PointState::INTERRUPTED)
266   {
267     Dali::Actor        consumingActor;
268     Integration::Point currentPoint(event.points[0]);
269
270     Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
271     if(lastPrimaryHitActor)
272     {
273       Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
274       currentPoint.SetHitActor(lastPrimaryHitActorHandle);
275
276       consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint);
277     }
278
279     // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
280     Actor* lastConsumedActor(mLastConsumedActor.GetActor());
281     if(lastConsumedActor &&
282        lastConsumedActor != lastPrimaryHitActor &&
283        lastConsumedActor != consumingActor)
284     {
285       Dali::Actor lastConsumedActorHandle(lastConsumedActor);
286       currentPoint.SetHitActor(lastConsumedActorHandle);
287       AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint);
288     }
289
290     // Tell the touch-down consuming actor as well, if required
291     Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
292     if(touchDownConsumedActor &&
293        touchDownConsumedActor != lastPrimaryHitActor &&
294        touchDownConsumedActor != lastConsumedActor &&
295        touchDownConsumedActor != consumingActor)
296     {
297       Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
298
299       currentPoint.SetHitActor(touchDownConsumedActorHandle);
300       AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint);
301     }
302
303     Clear();
304     mTouchDownConsumedActor.SetActor(nullptr);
305
306     currentPoint.SetHitActor(Dali::Actor());
307
308     TouchEventPtr    touchEventImpl(new TouchEvent(event.time));
309     Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
310
311     touchEventImpl->AddPoint(currentPoint);
312
313     mScene.EmitTouchedSignal(touchEventHandle);
314     return false; // No need for hit testing & already an interrupted event so just return false
315   }
316
317   // 2) Hit Testing.
318   TouchEventPtr    touchEventImpl(new TouchEvent(event.time));
319   Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
320
321   DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
322   DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
323
324   RenderTaskPtr currentRenderTask;
325   bool          firstPointParsed = false;
326
327   for(auto&& currentPoint : event.points)
328   {
329     HitTestAlgorithm::Results hitTestResults;
330     hitTestResults.point     = currentPoint;
331     hitTestResults.eventTime = event.time;
332     if(!firstPointParsed)
333     {
334       firstPointParsed = true;
335       ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene);
336
337       // Only set the currentRenderTask for the primary hit actor.
338       currentRenderTask = hitTestResults.renderTask;
339     }
340     else
341     {
342       HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults);
343     }
344
345     Integration::Point newPoint(currentPoint);
346     newPoint.SetHitActor(hitTestResults.actor);
347     newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
348
349     touchEventImpl->AddPoint(newPoint);
350
351     DALI_LOG_INFO(gLogFilter, Debug::General, "  State(%s), Screen(%.0f, %.0f), HitActor(%p, %s), Local(%.2f, %.2f)\n", TOUCH_POINT_STATE[currentPoint.GetState()], currentPoint.GetScreenPosition().x, currentPoint.GetScreenPosition().y, (hitTestResults.actor ? reinterpret_cast<void*>(&hitTestResults.actor.GetBaseObject()) : NULL), (hitTestResults.actor ? hitTestResults.actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : ""), hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y);
352   }
353
354   // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
355
356   bool consumed = false;
357
358   // Emit the touch signal
359   Dali::Actor consumedActor;
360
361   Integration::Point& primaryPoint      = touchEventImpl->GetPoint(0);
362   Dali::Actor         primaryHitActor   = primaryPoint.GetHitActor();
363   PointState::Type    primaryPointState = primaryPoint.GetState();
364
365   if(currentRenderTask)
366   {
367     // Emit the intercept touch signal
368     Dali::Actor interceptedActor = EmitInterceptTouchSignals(primaryHitActor, touchEventHandle);
369     if(interceptedActor)
370     {
371       // If the child is being touched, INTERRUPTED is sent.
372       if(mLastPrimaryHitActor.GetActor() &&
373          mLastPrimaryHitActor.GetActor() != interceptedActor &&
374          mLastRenderTask &&
375          mLastPrimaryPointState != PointState::FINISHED)
376       {
377         EmitTouchSignals(mLastPrimaryHitActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED);
378       }
379
380       consumedActor = EmitTouchSignals(interceptedActor, touchEventHandle);
381     }
382     else
383     {
384       consumedActor = EmitTouchSignals(primaryHitActor, touchEventHandle);
385     }
386     consumed = consumedActor ? true : false;
387   }
388
389   DALI_LOG_INFO(gLogFilter, Debug::Concise, "PrimaryHitActor:     (%p) %s\n", primaryHitActor ? reinterpret_cast<void*>(&primaryHitActor.GetBaseObject()) : NULL, primaryHitActor ? primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "");
390   DALI_LOG_INFO(gLogFilter, Debug::Concise, "ConsumedActor:       (%p) %s\n", consumedActor ? reinterpret_cast<void*>(&consumedActor.GetBaseObject()) : NULL, consumedActor ? consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "");
391
392   if((primaryPointState == PointState::DOWN) &&
393      (touchEventImpl->GetPointCount() == 1) &&
394      (consumedActor && consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
395   {
396     mTouchDownConsumedActor.SetActor(&GetImplementation(consumedActor));
397   }
398
399   // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
400   //    hit actor.  Also process the last consumed actor in the same manner.
401
402   Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
403   Actor* lastConsumedActor(mLastConsumedActor.GetActor());
404   if((primaryPointState == PointState::MOTION) || (primaryPointState == PointState::UP) || (primaryPointState == PointState::STATIONARY))
405   {
406     if(mLastRenderTask)
407     {
408       Dali::Actor leaveEventConsumer;
409       RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
410
411       if(lastPrimaryHitActor &&
412          lastPrimaryHitActor != primaryHitActor &&
413          lastPrimaryHitActor != consumedActor)
414       {
415         if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
416         {
417           if(lastPrimaryHitActor->GetLeaveRequired())
418           {
419             DALI_LOG_INFO(gLogFilter, Debug::Concise, "LeaveActor(Hit):     (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
420             leaveEventConsumer = EmitTouchSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::LEAVE);
421           }
422         }
423         else
424         {
425           // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
426           // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
427           DALI_LOG_INFO(gLogFilter, Debug::Concise, "InterruptedActor(Hit):     (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
428           leaveEventConsumer = EmitTouchSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED);
429         }
430       }
431
432       consumed |= leaveEventConsumer ? true : false;
433
434       // Check if the motion event has been consumed by another actor's listener.  In this case, the previously
435       // consumed actor's listeners may need to be informed (through a leave event).
436       // Further checks here to ensure we do not signal the same actor twice for the same event.
437       if(lastConsumedActor &&
438          lastConsumedActor != consumedActor &&
439          lastConsumedActor != lastPrimaryHitActor &&
440          lastConsumedActor != primaryHitActor &&
441          lastConsumedActor != leaveEventConsumer)
442       {
443         if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
444         {
445           if(lastConsumedActor->GetLeaveRequired())
446           {
447             DALI_LOG_INFO(gLogFilter, Debug::Concise, "LeaveActor(Consume): (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
448             EmitTouchSignals(lastConsumedActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE);
449           }
450         }
451         else
452         {
453           // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
454           // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
455           DALI_LOG_INFO(gLogFilter, Debug::Concise, "InterruptedActor(Consume):     (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
456           EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED);
457         }
458       }
459     }
460   }
461
462   // 5) If our primary point is an Up event, then the primary point (in multi-touch) will change next
463   //    time so set our last primary actor to NULL.  Do the same to the last consumed actor as well.
464   if(primaryPointState == PointState::UP)
465   {
466     Clear();
467   }
468   else
469   {
470     // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
471     if(primaryHitActor && GetImplementation(primaryHitActor).OnScene())
472     {
473       mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
474
475       // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
476       if(consumedActor && GetImplementation(consumedActor).OnScene())
477       {
478         mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
479       }
480       else
481       {
482         mLastConsumedActor.SetActor(nullptr);
483       }
484
485       mLastRenderTask        = currentRenderTask;
486       mLastPrimaryPointState = primaryPointState;
487     }
488     else
489     {
490       Clear();
491     }
492   }
493
494   // 6) Emit an interrupted event to the touch-down actor if it hasn't consumed the up and
495   //    emit the stage touched event if required.
496
497   if(touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
498   {
499     switch(primaryPointState)
500     {
501       case PointState::UP:
502       {
503         Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
504         if(touchDownConsumedActor &&
505            touchDownConsumedActor != consumedActor &&
506            touchDownConsumedActor != lastPrimaryHitActor &&
507            touchDownConsumedActor != lastConsumedActor)
508         {
509           Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
510
511           Integration::Point currentPoint = touchEventImpl->GetPoint(0);
512           currentPoint.SetHitActor(touchDownConsumedActorHandle);
513           currentPoint.SetState(PointState::INTERRUPTED);
514
515           AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint);
516         }
517
518         mTouchDownConsumedActor.SetActor(nullptr);
519
520         DALI_FALLTHROUGH;
521       }
522
523       case PointState::DOWN:
524       {
525         mScene.EmitTouchedSignal(touchEventHandle);
526         break;
527       }
528
529       case PointState::MOTION:
530       case PointState::LEAVE:
531       case PointState::STATIONARY:
532       case PointState::INTERRUPTED:
533       {
534         // Ignore
535         break;
536       }
537     }
538   }
539
540   return consumed;
541 }
542
543 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
544 {
545   if(actor == mLastPrimaryHitActor.GetActor())
546   {
547     Dali::Actor actorHandle(actor);
548
549     Integration::Point point;
550     point.SetState(PointState::INTERRUPTED);
551     point.SetHitActor(actorHandle);
552
553     TouchEventPtr touchEventImpl(new TouchEvent);
554     touchEventImpl->AddPoint(point);
555     Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
556
557     Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
558
559     if(mLastConsumedActor.GetActor() != eventConsumer)
560     {
561       EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
562     }
563
564     // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
565
566     mLastConsumedActor.SetActor(nullptr);
567     mLastRenderTask.Reset();
568     mLastPrimaryPointState = PointState::FINISHED;
569   }
570 }
571
572 } // namespace Internal
573
574 } // namespace Dali