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