Reduce CC of TouchEventProcessor::ProcessTouchEvent
[platform/core/uifw/dali-core.git] / dali / internal / event / events / touch-event-processor.cpp
1 /*
2  * Copyright (c) 2024 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::Internal
41 {
42 namespace
43 {
44 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
45 #if defined(DEBUG_ENABLED)
46 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TOUCH_PROCESSOR");
47 #endif // defined(DEBUG_ENABLED)
48
49 /**
50  * Structure for Variables used in the ProcessTouchEvent method.
51  */
52 struct ProcessTouchEventVariables
53 {
54   ProcessTouchEventVariables(TouchEventProcessor& eventProcessor, bool geometry)
55   : processor(eventProcessor),
56     isGeometry(geometry)
57   {
58   }
59
60   TouchEventProcessor& processor;                              ///< A handle to the touch-event-processor.
61   const bool           isGeometry;                             ///< Whether it's a geometry or not.
62   Actor*               lastPrimaryHitActor{nullptr};           ///< The last primary hit-actor.
63   Actor*               lastConsumedActor{nullptr};             ///< The last consuming actor.
64   TouchEventPtr        touchEventImpl;                         ///< The current touch-event-impl.
65   Dali::TouchEvent     touchEventHandle;                       ///< The handle to the touch-event-impl.
66   RenderTaskPtr        currentRenderTask;                      ///< The current render-task.
67   Dali::Actor          consumedActor;                          ///< The actor that consumed the event.
68   Dali::Actor          primaryHitActor;                        ///< The actor that has been hit by the primary point.
69   Integration::Point*  primaryPoint{nullptr};                  ///< The primary point of the hit.
70   PointState::Type     primaryPointState{PointState::STARTED}; ///< The state of the primary point.
71 };
72
73 const char* TOUCH_POINT_STATE[6] =
74   {
75     "DOWN",
76     "UP",
77     "MOTION",
78     "LEAVE",
79     "STATIONARY",
80     "INTERRUPTED",
81 };
82
83 bool ShouldEmitInterceptTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event)
84 {
85   PointState::Type state = event.GetState(0);
86   return actorImpl.GetInterceptTouchRequired() && (state != PointState::MOTION || actorImpl.IsDispatchTouchMotion());
87 }
88
89 bool ShouldEmitTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event)
90 {
91   PointState::Type state = event.GetState(0);
92   return actorImpl.GetTouchRequired() && (state != PointState::MOTION || actorImpl.IsDispatchTouchMotion());
93 }
94
95 // child -> parent
96 Dali::Actor EmitInterceptTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
97 {
98   Dali::Actor interceptedActor;
99
100   if(actor)
101   {
102     Dali::Actor parent = actor.GetParent();
103     if(parent)
104     {
105       // Recursively deliver events to the actor and its parents for intercept touch event.
106       interceptedActor = EmitInterceptTouchSignals(parent, touchEvent);
107     }
108
109     if(!interceptedActor)
110     {
111       bool   intercepted = false;
112       Actor& actorImpl(GetImplementation(actor));
113       if(ShouldEmitInterceptTouchEvent(actorImpl, touchEvent))
114       {
115         DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
116         intercepted = actorImpl.EmitInterceptTouchEventSignal(touchEvent);
117         if(intercepted)
118         {
119           interceptedActor = Dali::Actor(&actorImpl);
120         }
121       }
122     }
123   }
124   return interceptedActor;
125 }
126
127 // geometry
128 // child -> below
129 Dali::Actor EmitGeoInterceptTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, std::list<Dali::Internal::Actor*>& interceptActorList, const Dali::TouchEvent& touchEvent, ActorObserver& lastConsumedActor)
130 {
131   interceptActorList.clear();
132   Dali::Actor interceptedActor;
133   for(auto&& actor : actorLists)
134   {
135     // If there is a consumed actor, the intercept is sent only up to the moment before the consumed actor.
136     if(lastConsumedActor.GetActor() == actor)
137     {
138       break;
139     }
140     interceptActorList.push_back(actor);
141     if(ShouldEmitInterceptTouchEvent(*actor, touchEvent))
142     {
143       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
144       if(actor->EmitInterceptTouchEventSignal(touchEvent))
145       {
146         interceptedActor = Dali::Actor(actor);
147         break;
148       }
149     }
150   }
151   return interceptedActor;
152 }
153
154 /**
155  *  Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
156  */
157 Dali::Actor EmitTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
158 {
159   Dali::Actor consumedActor;
160
161   if(actor)
162   {
163     Dali::Actor oldParent(actor.GetParent());
164
165     Actor& actorImpl(GetImplementation(actor));
166
167     bool consumed(false);
168
169     // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
170     if(ShouldEmitTouchEvent(actorImpl, touchEvent))
171     {
172       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
173       consumed = actorImpl.EmitTouchEventSignal(touchEvent);
174     }
175
176     if(consumed)
177     {
178       // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
179       consumedActor = Dali::Actor(&actorImpl);
180     }
181     else
182     {
183       // The actor may have been removed/reparented during the signal callbacks.
184       Dali::Actor parent = actor.GetParent();
185
186       if(parent &&
187          (parent == oldParent))
188       {
189         // One of the actor's parents may consumed the event and they should be set as the consumed actor.
190         consumedActor = EmitTouchSignals(parent, touchEvent);
191       }
192     }
193   }
194
195   return consumedActor;
196 }
197
198 /**
199  *  Recursively deliver events to the actor and its below actor, until the event is consumed or the stage is reached.
200  */
201 Dali::Actor EmitGeoTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, const Dali::TouchEvent& touchEvent)
202 {
203   Dali::Actor consumedActor;
204
205   std::list<Dali::Internal::Actor*>::reverse_iterator rIter = actorLists.rbegin();
206   for(; rIter != actorLists.rend(); rIter++)
207   {
208     Actor* actorImpl(*rIter);
209     // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
210     if(ShouldEmitTouchEvent(*actorImpl, touchEvent))
211     {
212       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
213       if(actorImpl->EmitTouchEventSignal(touchEvent))
214       {
215         // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
216         consumedActor = Dali::Actor(actorImpl);
217         break;
218       }
219     }
220   }
221   return consumedActor;
222 }
223
224 Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point, RenderTaskPtr renderTask)
225 {
226   TouchEventPtr    touchEvent(new TouchEvent(time));
227   Dali::TouchEvent touchEventHandle(touchEvent.Get());
228
229   touchEvent->AddPoint(point);
230   touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
231
232   return EmitTouchSignals(actor, touchEventHandle);
233 }
234
235 Dali::Actor GeoAllocAndEmitTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, unsigned long time, const Integration::Point& point, RenderTaskPtr renderTask)
236 {
237   TouchEventPtr    touchEvent(new TouchEvent(time));
238   Dali::TouchEvent touchEventHandle(touchEvent.Get());
239
240   touchEvent->AddPoint(point);
241   touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
242
243   return EmitGeoTouchSignals(actorLists, touchEventHandle);
244 }
245
246 /**
247  * Changes the state of the primary point to leave and emits the touch signals
248  */
249 Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state, bool isGeometry)
250 {
251   Dali::Actor consumingActor;
252
253   if(actor)
254   {
255     TouchEventPtr touchEventImpl = TouchEvent::Clone(*originalTouchEvent.Get());
256     touchEventImpl->SetRenderTask(Dali::RenderTask(&renderTask));
257
258     Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
259
260     const Vector2& screenPosition = primaryPoint.GetScreenPosition();
261     Vector2        localPosition;
262     actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
263
264     primaryPoint.SetLocalPosition(localPosition);
265     primaryPoint.SetHitActor(Dali::Actor(actor));
266     primaryPoint.SetState(state);
267
268     if(isGeometry)
269     {
270       std::list<Dali::Internal::Actor*> actorLists;
271       actorLists.push_back(actor);
272       consumingActor = EmitGeoTouchSignals(actorLists, Dali::TouchEvent(touchEventImpl.Get()));
273     }
274     else
275     {
276       consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get()));
277     }
278   }
279
280   return consumingActor;
281 }
282
283 /**
284  * @brief Parses the primary touch point by performing a hit-test if necessary
285  *
286  * @param[out] hitTestResults The hit test results are put into this variable
287  * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member
288  * @param[in] lastRenderTask The last render task member
289  * @param[in] currentPoint The current point information
290  * @param[in] scene The scene that this touch is related to
291  * @param[in] actorLists The list of actors that can be touched, from leaf actor to root.
292  */
293 void ParsePrimaryTouchPoint(
294   HitTestAlgorithm::Results&         hitTestResults,
295   ActorObserver&                     capturingTouchActorObserver,
296   ActorObserver&                     ownTouchActorObserver,
297   const RenderTaskPtr&               lastRenderTask,
298   const Integration::Point&          currentPoint,
299   const Internal::Scene&             scene,
300   std::list<Dali::Internal::Actor*>& actorLists)
301 {
302   Actor* capturingTouchActor = capturingTouchActorObserver.GetActor();
303
304   // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it
305   if(capturingTouchActor && lastRenderTask)
306   {
307     hitTestResults.actor          = Dali::Actor(capturingTouchActor);
308     hitTestResults.renderTask     = lastRenderTask;
309     const Vector2& screenPosition = currentPoint.GetScreenPosition();
310     capturingTouchActor->ScreenToLocal(*lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
311   }
312   else
313   {
314     Actor* ownTouchActor = ownTouchActorObserver.GetActor();
315     HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor, scene.IsGeometryHittestEnabled());
316
317     if(currentPoint.GetState() == PointState::STARTED && hitTestResults.actor)
318     {
319       bool isGeometry = scene.IsGeometryHittestEnabled();
320       // If we've just started touch, then check whether the actor has requested to capture all touch events
321       Actor* hitActor = &GetImplementation(hitTestResults.actor);
322       if(hitActor->CapturesAllTouchAfterStart() || isGeometry)
323       {
324         capturingTouchActorObserver.SetActor(hitActor);
325       }
326       if(hitActor->IsAllowedOnlyOwnTouch() || isGeometry)
327       {
328         ownTouchActorObserver.SetActor(hitActor);
329       }
330       if(isGeometry)
331       {
332         actorLists = hitTestResults.actorLists;
333       }
334     }
335   }
336 }
337
338 } // unnamed namespace
339
340 struct TouchEventProcessor::Impl
341 {
342   /**
343    * Emits an interrupted event.
344    * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
345    * @param[in] event The touch event that has occurred
346    */
347   static inline void EmitInterruptedEvent(ProcessTouchEventVariables& localVars, const Integration::TouchEvent& event)
348   {
349     Dali::Actor        consumingActor;
350     Integration::Point currentPoint(event.points[0]);
351
352     if(localVars.isGeometry)
353     {
354       // Since the geometry way only receives touch events from the consumed actor,
355       // it first searches for whether there is a consumed actor and then sends the event
356       Actor* touchConsumedActor(localVars.processor.mLastConsumedActor.GetActor());
357       Actor* touchDownConsumedActor(localVars.processor.mTouchDownConsumedActor.GetActor());
358       Actor* lastPrimaryHitActor(localVars.processor.mLastPrimaryHitActor.GetActor());
359       if(touchConsumedActor)
360       {
361         Dali::Actor touchConsumedActorHandle(touchConsumedActor);
362         currentPoint.SetHitActor(touchConsumedActorHandle);
363         std::list<Dali::Internal::Actor*> actorLists;
364         actorLists.push_back(touchConsumedActor);
365         GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, localVars.processor.mLastRenderTask);
366       }
367       else if(touchDownConsumedActor)
368       {
369         Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
370         currentPoint.SetHitActor(touchDownConsumedActorHandle);
371         std::list<Dali::Internal::Actor*> actorLists;
372         actorLists.push_back(touchDownConsumedActor);
373         GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, localVars.processor.mLastRenderTask);
374       }
375       else if(lastPrimaryHitActor)
376       {
377         Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
378         currentPoint.SetHitActor(lastPrimaryHitActorHandle);
379
380         GeoAllocAndEmitTouchSignals(localVars.processor.mCandidateActorLists, event.time, currentPoint, localVars.processor.mLastRenderTask);
381       }
382     }
383     else
384     {
385       Actor* lastPrimaryHitActor(localVars.processor.mLastPrimaryHitActor.GetActor());
386       if(lastPrimaryHitActor)
387       {
388         Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
389         currentPoint.SetHitActor(lastPrimaryHitActorHandle);
390
391         consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint, localVars.processor.mLastRenderTask);
392       }
393
394       // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
395       Actor* lastConsumedActor(localVars.processor.mLastConsumedActor.GetActor());
396       if(lastConsumedActor &&
397          lastConsumedActor != lastPrimaryHitActor &&
398          lastConsumedActor != consumingActor)
399       {
400         Dali::Actor lastConsumedActorHandle(lastConsumedActor);
401         currentPoint.SetHitActor(lastConsumedActorHandle);
402         AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint, localVars.processor.mLastRenderTask);
403       }
404
405       // Tell the touch-down consuming actor as well, if required
406       Actor* touchDownConsumedActor(localVars.processor.mTouchDownConsumedActor.GetActor());
407       if(touchDownConsumedActor &&
408          touchDownConsumedActor != lastPrimaryHitActor &&
409          touchDownConsumedActor != lastConsumedActor &&
410          touchDownConsumedActor != consumingActor)
411       {
412         Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
413
414         currentPoint.SetHitActor(touchDownConsumedActorHandle);
415         AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, localVars.processor.mLastRenderTask);
416       }
417     }
418
419     localVars.processor.Clear();
420     localVars.processor.mTouchDownConsumedActor.SetActor(nullptr);
421
422     currentPoint.SetHitActor(Dali::Actor());
423
424     TouchEventPtr    touchEventImpl(new TouchEvent(event.time));
425     Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
426
427     touchEventImpl->AddPoint(currentPoint);
428
429     localVars.processor.mScene.EmitTouchedSignal(touchEventHandle);
430   }
431
432   /**
433    * Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
434    * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
435    * @return True if consumed, false otherwise.
436    */
437   static inline bool DeliverEventsToActorAndParents(ProcessTouchEventVariables& localVars)
438   {
439     bool                 consumed  = false;
440     TouchEventProcessor& processor = localVars.processor;
441     if(localVars.currentRenderTask)
442     {
443       if(localVars.isGeometry)
444       {
445         Actor* interceptedTouchActor(processor.mInterceptedTouchActor.GetActor());
446         if(interceptedTouchActor)
447         {
448           Actor* touchConsumedActor(processor.mLastConsumedActor.GetActor());
449           if(touchConsumedActor) // If there is a consumed actor, send events only to the consumed actor.
450           {
451             RenderTask& currentRenderTaskImpl = *localVars.currentRenderTask.Get();
452             localVars.consumedActor           = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, localVars.touchEventImpl, localVars.primaryPointState, localVars.isGeometry);
453           }
454           else // If there is an intercepted actor, send a touch event starting from the intercepted actor.
455           {
456             Dali::Actor                       interceptedTouchActorHandle(interceptedTouchActor);
457             std::list<Dali::Internal::Actor*> interceptActorLists = localVars.processor.mInterceptedActorLists;
458             localVars.consumedActor                               = EmitGeoTouchSignals(interceptActorLists, localVars.touchEventHandle);
459           }
460         }
461         else
462         {
463           Dali::Actor interceptedActor;
464           // Let's find out if there is an intercept actor.
465           interceptedActor = EmitGeoInterceptTouchSignals(processor.mCandidateActorLists, processor.mInterceptedActorLists, localVars.touchEventHandle, processor.mLastConsumedActor);
466           if(interceptedActor)
467           {
468             processor.mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
469
470             // If there is an interception, send an interrupted event to the last consumed actor or to the actor that hit previously.
471             if(processor.mLastConsumedActor.GetActor() &&
472                processor.mLastConsumedActor.GetActor() != interceptedActor &&
473                processor.mLastRenderTask &&
474                processor.mLastPrimaryPointState != PointState::FINISHED)
475             {
476               EmitTouchSignals(processor.mLastConsumedActor.GetActor(), *processor.mLastRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
477               processor.mTouchDownConsumedActor.SetActor(nullptr);
478               processor.mLastConsumedActor.SetActor(nullptr);
479             }
480             else if(processor.mLastPrimaryHitActor.GetActor() &&
481                     processor.mLastPrimaryHitActor.GetActor() != interceptedActor &&
482                     processor.mLastRenderTask &&
483                     processor.mLastPrimaryPointState != PointState::FINISHED)
484             {
485               std::list<Dali::Internal::Actor*>::reverse_iterator rIter = processor.mCandidateActorLists.rbegin();
486               for(; rIter != processor.mCandidateActorLists.rend(); rIter++)
487               {
488                 Actor* actorImpl(*rIter);
489                 EmitTouchSignals(actorImpl, *processor.mLastRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
490               }
491             }
492           }
493
494           Actor* touchConsumedActor(processor.mLastConsumedActor.GetActor());
495           if(touchConsumedActor) // If there is a consumed actor, send events only to the consumed actor.
496           {
497             RenderTask& currentRenderTaskImpl = *localVars.currentRenderTask.Get();
498             localVars.consumedActor           = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, localVars.touchEventImpl, localVars.primaryPointState, localVars.isGeometry);
499           }
500           else
501           {
502             // Let's propagate touch events from the intercepted actor or start propagating touch events from the leaf actor.
503             localVars.consumedActor = EmitGeoTouchSignals(interceptedActor ? processor.mInterceptedActorLists : processor.mCandidateActorLists, localVars.touchEventHandle);
504           }
505         }
506       }
507       else
508       {
509         Actor* interceptedTouchActor(processor.mInterceptedTouchActor.GetActor());
510         if(interceptedTouchActor)
511         {
512           Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
513           localVars.consumedActor = EmitTouchSignals(interceptedTouchActorHandle, localVars.touchEventHandle);
514         }
515         else
516         {
517           // Emit the intercept touch signal
518           Dali::Actor interceptedActor = EmitInterceptTouchSignals(localVars.primaryHitActor, localVars.touchEventHandle);
519           if(interceptedActor)
520           {
521             processor.mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
522             // If the child is being touched, INTERRUPTED is sent.
523             if(processor.mLastPrimaryHitActor.GetActor() &&
524                processor.mLastPrimaryHitActor.GetActor() != interceptedActor &&
525                processor.mLastRenderTask &&
526                processor.mLastPrimaryPointState != PointState::FINISHED)
527             {
528               EmitTouchSignals(processor.mLastPrimaryHitActor.GetActor(), *processor.mLastRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
529               processor.mTouchDownConsumedActor.SetActor(nullptr);
530             }
531             localVars.consumedActor = EmitTouchSignals(interceptedActor, localVars.touchEventHandle);
532           }
533           else
534           {
535             localVars.consumedActor = EmitTouchSignals(localVars.primaryHitActor, localVars.touchEventHandle);
536           }
537         }
538       }
539
540       consumed = localVars.consumedActor ? true : false;
541
542       if(localVars.primaryPointState == PointState::MOTION)
543       {
544         DALI_LOG_INFO(gLogFilter,
545                       Debug::Concise,
546                       "PrimaryHitActor: (%p) id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n",
547                       localVars.primaryHitActor ? reinterpret_cast<void*>(&localVars.primaryHitActor.GetBaseObject()) : NULL,
548                       localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
549                       localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
550                       TOUCH_POINT_STATE[localVars.primaryPointState],
551                       localVars.primaryPoint->GetScreenPosition().x,
552                       localVars.primaryPoint->GetScreenPosition().y,
553                       localVars.isGeometry);
554         DALI_LOG_INFO(gLogFilter,
555                       Debug::Concise,
556                       "ConsumedActor:   (%p) id(%d), name(%s), state(%s), isGeo : %d \n",
557                       localVars.consumedActor ? reinterpret_cast<void*>(&localVars.consumedActor.GetBaseObject()) : NULL,
558                       localVars.consumedActor ? localVars.consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
559                       localVars.consumedActor ? localVars.consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
560                       TOUCH_POINT_STATE[localVars.primaryPointState],
561                       localVars.isGeometry);
562       }
563       else
564       {
565         DALI_LOG_RELEASE_INFO("PrimaryHitActor:(%p), id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n",
566                               localVars.primaryHitActor ? reinterpret_cast<void*>(&localVars.primaryHitActor.GetBaseObject()) : NULL,
567                               localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
568                               localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
569                               TOUCH_POINT_STATE[localVars.primaryPointState],
570                               localVars.primaryPoint->GetScreenPosition().x,
571                               localVars.primaryPoint->GetScreenPosition().y,
572                               localVars.isGeometry);
573         DALI_LOG_RELEASE_INFO("ConsumedActor:  (%p), id(%d), name(%s), state(%s), isGeo : %d \n",
574                               localVars.consumedActor ? reinterpret_cast<void*>(&localVars.consumedActor.GetBaseObject()) : NULL,
575                               localVars.consumedActor ? localVars.consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
576                               localVars.consumedActor ? localVars.consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
577                               TOUCH_POINT_STATE[localVars.primaryPointState],
578                               localVars.isGeometry);
579       }
580     }
581
582     if((localVars.primaryPointState == PointState::DOWN) &&
583        (localVars.touchEventImpl->GetPointCount() == 1) &&
584        (localVars.consumedActor && localVars.consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
585     {
586       processor.mTouchDownConsumedActor.SetActor(&GetImplementation(localVars.consumedActor));
587     }
588
589     return consumed;
590   }
591
592   /**
593    * Deliver Leave event to last hit or consuming actor if required.
594    * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
595    * @return True if consumed, false otherwise.
596    */
597   static inline bool DeliverLeaveEvent(ProcessTouchEventVariables& localVars)
598   {
599     bool                 consumed = false;
600     TouchEventProcessor& processor(localVars.processor);
601     if((localVars.primaryPointState == PointState::MOTION) || (localVars.primaryPointState == PointState::UP) || (localVars.primaryPointState == PointState::STATIONARY))
602     {
603       if(processor.mLastRenderTask)
604       {
605         Dali::Actor leaveEventConsumer;
606         RenderTask& lastRenderTaskImpl = *processor.mLastRenderTask.Get();
607
608         if(localVars.isGeometry)
609         {
610           if(localVars.lastPrimaryHitActor)
611           {
612             if(!localVars.lastPrimaryHitActor->IsHittable() || !IsActuallySensitive(localVars.lastPrimaryHitActor))
613             {
614               // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
615               // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
616               DALI_LOG_RELEASE_INFO("InterruptedActor(Hit):     (%p) %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetName().data());
617               leaveEventConsumer = EmitTouchSignals(localVars.lastPrimaryHitActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
618             }
619           }
620
621           consumed = leaveEventConsumer ? true : false;
622
623           // Check if the motion event has been consumed by another actor's listener.  In this case, the previously
624           // consumed actor's listeners may need to be informed (through a leave event).
625           // Further checks here to ensure we do not signal the same actor twice for the same event.
626           if(localVars.lastConsumedActor &&
627              localVars.lastConsumedActor != localVars.lastPrimaryHitActor &&
628              localVars.lastConsumedActor != leaveEventConsumer)
629           {
630             if(!localVars.lastConsumedActor->IsHittable() || !IsActuallySensitive(localVars.lastConsumedActor))
631             {
632               // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
633               // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
634               DALI_LOG_RELEASE_INFO("InterruptedActor(Consume):     (%p) %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetName().data());
635               EmitTouchSignals(processor.mLastConsumedActor.GetActor(), lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
636             }
637           }
638         }
639         else
640         {
641           if(localVars.lastPrimaryHitActor &&
642              localVars.lastPrimaryHitActor != localVars.primaryHitActor &&
643              localVars.lastPrimaryHitActor != localVars.consumedActor)
644           {
645             if(localVars.lastPrimaryHitActor->IsHittable() && IsActuallySensitive(localVars.lastPrimaryHitActor))
646             {
647               if(localVars.lastPrimaryHitActor->GetLeaveRequired())
648               {
649                 DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetId(), localVars.lastPrimaryHitActor->GetName().data());
650                 leaveEventConsumer = EmitTouchSignals(localVars.lastPrimaryHitActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::LEAVE, localVars.isGeometry);
651               }
652             }
653             else
654             {
655               // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
656               // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
657               DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetId(), localVars.lastPrimaryHitActor->GetName().data());
658               leaveEventConsumer = EmitTouchSignals(localVars.lastPrimaryHitActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
659             }
660           }
661
662           consumed |= leaveEventConsumer ? true : false;
663
664           // Check if the motion event has been consumed by another actor's listener.  In this case, the previously
665           // consumed actor's listeners may need to be informed (through a leave event).
666           // Further checks here to ensure we do not signal the same actor twice for the same event.
667           if(localVars.lastConsumedActor &&
668              localVars.lastConsumedActor != localVars.consumedActor &&
669              localVars.lastConsumedActor != localVars.lastPrimaryHitActor &&
670              localVars.lastConsumedActor != localVars.primaryHitActor &&
671              localVars.lastConsumedActor != leaveEventConsumer)
672           {
673             if(localVars.lastConsumedActor->IsHittable() && IsActuallySensitive(localVars.lastConsumedActor))
674             {
675               if(localVars.lastConsumedActor->GetLeaveRequired())
676               {
677                 DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetId(), localVars.lastConsumedActor->GetName().data());
678                 EmitTouchSignals(localVars.lastConsumedActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::LEAVE, localVars.isGeometry);
679               }
680             }
681             else
682             {
683               // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
684               // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
685               DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetId(), localVars.lastConsumedActor->GetName().data());
686               EmitTouchSignals(processor.mLastConsumedActor.GetActor(), lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
687             }
688           }
689         }
690       }
691     }
692     return consumed;
693   }
694
695   /**
696    * Update the processor member appropriately by handling the final up event, and setting the last hit/consumed events etc.
697    * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
698    */
699   static inline void UpdateMembersWithCurrentHitInformation(ProcessTouchEventVariables& localVars)
700   {
701     // If our primary point is an Up event, then the primary point (in multi-touch) will change next
702     // time so set our last primary actor to NULL.  Do the same to the last consumed actor as well.
703
704     TouchEventProcessor& processor(localVars.processor);
705     if(localVars.primaryPointState == PointState::UP)
706     {
707       processor.Clear();
708     }
709     else
710     {
711       // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
712       if(localVars.primaryHitActor && GetImplementation(localVars.primaryHitActor).OnScene())
713       {
714         processor.mLastPrimaryHitActor.SetActor(&GetImplementation(localVars.primaryHitActor));
715
716         // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
717         if(localVars.consumedActor && GetImplementation(localVars.consumedActor).OnScene())
718         {
719           processor.mLastConsumedActor.SetActor(&GetImplementation(localVars.consumedActor));
720         }
721         else
722         {
723           if(localVars.isGeometry)
724           {
725             if(localVars.lastConsumedActor && !localVars.lastConsumedActor->OnScene())
726             {
727               processor.mLastConsumedActor.SetActor(nullptr);
728             }
729           }
730           else
731           {
732             processor.mLastConsumedActor.SetActor(nullptr);
733           }
734         }
735
736         processor.mLastRenderTask        = localVars.currentRenderTask;
737         processor.mLastPrimaryPointState = localVars.primaryPointState;
738       }
739       else
740       {
741         processor.Clear();
742       }
743     }
744   }
745
746   /**
747    * Deliver an event to the touch-down actor and emit from the scene if required.
748    * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
749    * @param[in] event The touch event that has occurred
750    */
751   static inline void DeliverEventToTouchDownActorAndScene(ProcessTouchEventVariables& localVars, const Integration::TouchEvent& event)
752   {
753     TouchEventProcessor& processor(localVars.processor);
754     if(localVars.touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
755     {
756       switch(localVars.primaryPointState)
757       {
758         case PointState::UP:
759         {
760           Actor* touchDownConsumedActor(processor.mTouchDownConsumedActor.GetActor());
761           if(touchDownConsumedActor &&
762              touchDownConsumedActor != localVars.consumedActor &&
763              touchDownConsumedActor != localVars.lastPrimaryHitActor &&
764              touchDownConsumedActor != localVars.lastConsumedActor)
765           {
766             Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
767
768             Integration::Point currentPoint = localVars.touchEventImpl->GetPoint(0);
769             currentPoint.SetHitActor(touchDownConsumedActorHandle);
770             currentPoint.SetState(PointState::INTERRUPTED);
771
772             if(localVars.isGeometry)
773             {
774               std::list<Dali::Internal::Actor*> actorLists;
775               actorLists.push_back(touchDownConsumedActor);
776               GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, nullptr /* Not Required for this use case */);
777             }
778             else
779             {
780               AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, nullptr /* Not Required for this use case */);
781             }
782           }
783
784           processor.mTouchDownConsumedActor.SetActor(nullptr);
785           processor.mInterceptedTouchActor.SetActor(nullptr);
786
787           DALI_FALLTHROUGH;
788         }
789
790         case PointState::DOWN:
791         {
792           processor.mScene.EmitTouchedSignal(localVars.touchEventHandle);
793           break;
794         }
795
796         case PointState::MOTION:
797         case PointState::LEAVE:
798         case PointState::STATIONARY:
799         case PointState::INTERRUPTED:
800         {
801           // Ignore
802           break;
803         }
804       }
805     }
806   }
807 };
808
809 TouchEventProcessor::TouchEventProcessor(Scene& scene)
810 : mScene(scene),
811   mLastPrimaryHitActor(MakeCallback(this, &TouchEventProcessor::OnObservedActorDisconnected)),
812   mLastConsumedActor(),
813   mCapturingTouchActor(),
814   mOwnTouchActor(),
815   mTouchDownConsumedActor(),
816   mInterceptedTouchActor(),
817   mLastRenderTask(),
818   mLastPrimaryPointState(PointState::FINISHED)
819 {
820   DALI_LOG_TRACE_METHOD(gLogFilter);
821 }
822
823 TouchEventProcessor::~TouchEventProcessor()
824 {
825   DALI_LOG_TRACE_METHOD(gLogFilter);
826 }
827
828 bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event)
829 {
830   DALI_LOG_TRACE_METHOD(gLogFilter);
831   DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty TouchEvent sent from Integration\n");
832
833   PRINT_HIERARCHY(gLogFilter);
834
835   DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT");
836
837   ProcessTouchEventVariables localVars(*this, mScene.IsGeometryHittestEnabled());
838
839   // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
840   //    and emit the stage signal as well.
841
842   if(event.points[0].GetState() == PointState::INTERRUPTED)
843   {
844     Impl::EmitInterruptedEvent(localVars, event);
845     return false; // No need for hit testing & already an interrupted event so just return false
846   }
847
848   // 2) Hit Testing.
849   localVars.touchEventImpl   = new TouchEvent(event.time);
850   localVars.touchEventHandle = Dali::TouchEvent(localVars.touchEventImpl.Get());
851
852   DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
853   DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
854
855   bool firstPointParsed = false;
856   for(auto&& currentPoint : event.points)
857   {
858     HitTestAlgorithm::Results hitTestResults;
859     hitTestResults.point     = currentPoint;
860     hitTestResults.eventTime = event.time;
861     if(!firstPointParsed)
862     {
863       firstPointParsed = true;
864       ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene, mCandidateActorLists);
865
866       // Only set the currentRenderTask for the primary hit actor.
867       localVars.currentRenderTask = hitTestResults.renderTask;
868       localVars.touchEventImpl->SetRenderTask(Dali::RenderTask(localVars.currentRenderTask.Get()));
869     }
870     else
871     {
872       HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, nullptr, localVars.isGeometry);
873     }
874
875     Integration::Point newPoint(currentPoint);
876     newPoint.SetHitActor(hitTestResults.actor);
877     newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
878
879     localVars.touchEventImpl->AddPoint(newPoint);
880
881     DALI_LOG_INFO(gLogFilter,
882                   Debug::General,
883                   "  State(%s), Screen(%.0f, %.0f), HitActor(%p, %s), Local(%.2f, %.2f)\n",
884                   TOUCH_POINT_STATE[currentPoint.GetState()],
885                   currentPoint.GetScreenPosition().x,
886                   currentPoint.GetScreenPosition().y,
887                   (hitTestResults.actor ? reinterpret_cast<void*>(&hitTestResults.actor.GetBaseObject()) : NULL),
888                   (hitTestResults.actor ? hitTestResults.actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : ""),
889                   hitTestResults.actorCoordinates.x,
890                   hitTestResults.actorCoordinates.y);
891   }
892
893   // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
894   localVars.primaryPoint      = &localVars.touchEventImpl->GetPoint(0);
895   localVars.primaryHitActor   = localVars.primaryPoint->GetHitActor();
896   localVars.primaryPointState = localVars.primaryPoint->GetState();
897
898   bool consumed = Impl::DeliverEventsToActorAndParents(localVars);
899
900   // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
901   //    hit actor.  Also process the last consumed actor in the same manner.
902   localVars.lastPrimaryHitActor = mInterceptedTouchActor.GetActor() ? mInterceptedTouchActor.GetActor() : mLastPrimaryHitActor.GetActor();
903   localVars.lastConsumedActor   = mLastConsumedActor.GetActor();
904   consumed |= Impl::DeliverLeaveEvent(localVars);
905
906   // 5) Update the processor member appropriately.
907   Impl::UpdateMembersWithCurrentHitInformation(localVars);
908
909   // 6) Emit an interrupted event to the touch-down actor if it hasn't consumed the up and
910   //    emit the stage touched event if required.
911   Impl::DeliverEventToTouchDownActorAndScene(localVars, event);
912
913   return consumed;
914 }
915
916 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
917 {
918   if(mScene.IsGeometryHittestEnabled() && (actor == mLastConsumedActor.GetActor() || actor == mLastPrimaryHitActor.GetActor()))
919   {
920     Dali::Actor        actorHandle(actor);
921     Integration::Point point;
922     point.SetState(PointState::INTERRUPTED);
923     point.SetHitActor(actorHandle);
924     if(actor == mLastConsumedActor.GetActor())
925     {
926       std::list<Dali::Internal::Actor*> actorLists;
927       actorLists.push_back(mLastConsumedActor.GetActor());
928       GeoAllocAndEmitTouchSignals(actorLists, 0, point, mLastRenderTask);
929     }
930     else if(!mLastConsumedActor.GetActor())
931     {
932       GeoAllocAndEmitTouchSignals(mCandidateActorLists, 0, point, mLastRenderTask);
933     }
934     // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
935     mLastConsumedActor.SetActor(nullptr);
936     mLastRenderTask.Reset();
937     mLastPrimaryPointState = PointState::FINISHED;
938   }
939   else
940   {
941     if(actor == mLastPrimaryHitActor.GetActor())
942     {
943       Dali::Actor        actorHandle(actor);
944       Integration::Point point;
945       point.SetState(PointState::INTERRUPTED);
946       point.SetHitActor(actorHandle);
947
948       TouchEventPtr touchEventImpl(new TouchEvent);
949       touchEventImpl->AddPoint(point);
950       Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
951
952       Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
953       if(mLastConsumedActor.GetActor() != eventConsumer)
954       {
955         EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
956       }
957
958       // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
959       mLastConsumedActor.SetActor(nullptr);
960       mLastRenderTask.Reset();
961       mLastPrimaryPointState = PointState::FINISHED;
962     }
963   }
964 }
965
966 void TouchEventProcessor::Clear()
967 {
968   mLastPrimaryHitActor.SetActor(nullptr);
969   mLastConsumedActor.SetActor(nullptr);
970   mCapturingTouchActor.SetActor(nullptr);
971   mOwnTouchActor.SetActor(nullptr);
972   mInterceptedTouchActor.SetActor(nullptr);
973   mLastRenderTask.Reset();
974   mLastPrimaryPointState = PointState::FINISHED;
975 }
976
977 } // namespace Dali::Internal