• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/manager/post_event/post_event_manager.h"
17 
18 #include "core/common/stylus/stylus_detector_mgr.h"
19 #include "core/pipeline_ng/pipeline_context.h"
20 
21 namespace OHOS::Ace::NG {
22 namespace {
23 constexpr int32_t PASS_THROUGH_EVENT_ID = 100000;
24 }
25 
PostEvent(const RefPtr<NG::UINode> & uiNode,TouchEvent & touchEvent)26 bool PostEventManager::PostEvent(const RefPtr<NG::UINode>& uiNode, TouchEvent& touchEvent)
27 {
28     if (!CheckPointValidity(touchEvent)) {
29         TAG_LOGW(AceLogTag::ACE_INPUTKEYFLOW,
30             "PostEvent event is invalid, possible reason is event timeStamp is the same as the previous event");
31         return false;
32     }
33     CHECK_NULL_RETURN(uiNode, false);
34     touchEvent.postEventNodeId = uiNode->GetId();
35     auto result = false;
36     switch (touchEvent.type) {
37         case TouchType::DOWN:
38             result = PostDownEvent(uiNode, touchEvent);
39             break;
40         case TouchType::MOVE:
41             result = PostMoveEvent(uiNode, touchEvent);
42             break;
43         case TouchType::UP:
44         case TouchType::CANCEL:
45             result = PostUpEvent(uiNode, touchEvent);
46             break;
47         default:
48             TAG_LOGE(AceLogTag::ACE_GESTURE, "dispatchEvent touchEvent type unkown");
49             break;
50     }
51     return result;
52 }
53 
PostTouchEvent(const RefPtr<NG::UINode> & uiNode,TouchEvent && touchEvent)54 bool PostEventManager::PostTouchEvent(const RefPtr<NG::UINode>& uiNode, TouchEvent&& touchEvent)
55 {
56     CHECK_NULL_RETURN(uiNode, false);
57     touchEvent.postEventNodeId = uiNode->GetId();
58     touchEvent.id += PASS_THROUGH_EVENT_ID;
59     auto frameNode = AceType::DynamicCast<FrameNode>(uiNode);
60     CHECK_NULL_RETURN(frameNode, false);
61     auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
62     CHECK_NULL_RETURN(pipelineContext, false);
63     auto eventManager = pipelineContext->GetEventManager();
64     CHECK_NULL_RETURN(eventManager, false);
65     touchEvent.passThrough = true;
66     passThroughResult_ = false;
67     if (touchEvent.type != TouchType::MOVE) {
68         if (!CheckTouchEvent(uiNode, touchEvent)) {
69             return false;
70         }
71         postInputEventAction_.push_back({ uiNode, touchEvent });
72     }
73     // Check if there's a pending drag cancel operation
74     if (!eventManager->IsDragCancelPending()) {
75         // Normal touch event processing: dispatch the touch event through the pipeline context
76         // for standard event handling and gesture recognition
77         targetNode_ = frameNode;
78         pipelineContext->OnTouchEvent(touchEvent, frameNode, false);
79         targetNode_.Reset();
80     } else {
81         // Abnormal state handling: when drag cancel is pending, use specialized event validation
82         // to check and clean up invalid touch events (e.g., UP/CANCEL without corresponding DOWN)
83         eventManager->CheckUpEvent(touchEvent);
84     }
85     touchEvent.passThrough = false;
86     if (touchEvent.type == TouchType::UP || touchEvent.type == TouchType::CANCEL) {
87         ClearPostInputActions(uiNode, touchEvent.id);
88     }
89     return passThroughResult_;
90 }
91 
PostMouseEvent(const RefPtr<NG::UINode> & uiNode,MouseEvent && mouseEvent)92 bool PostEventManager::PostMouseEvent(const RefPtr<NG::UINode>& uiNode, MouseEvent&& mouseEvent)
93 {
94     CHECK_NULL_RETURN(uiNode, false);
95     mouseEvent.id += PASS_THROUGH_EVENT_ID;
96     mouseEvent.postEventNodeId = uiNode->GetId();
97     auto frameNode = AceType::DynamicCast<FrameNode>(uiNode);
98     CHECK_NULL_RETURN(frameNode, false);
99     targetNode_ = frameNode;
100     auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
101     CHECK_NULL_RETURN(pipelineContext, false);
102     mouseEvent.passThrough = true;
103     passThroughResult_ = false;
104     pipelineContext->OnMouseEvent(mouseEvent, frameNode);
105     mouseEvent.passThrough = false;
106     targetNode_.Reset();
107     return passThroughResult_;
108 }
109 
PostAxisEvent(const RefPtr<NG::UINode> & uiNode,AxisEvent && axisEvent)110 bool PostEventManager::PostAxisEvent(const RefPtr<NG::UINode>& uiNode, AxisEvent&& axisEvent)
111 {
112     CHECK_NULL_RETURN(uiNode, false);
113     axisEvent.id += PASS_THROUGH_EVENT_ID;
114     axisEvent.postEventNodeId = uiNode->GetId();
115     auto frameNode = AceType::DynamicCast<FrameNode>(uiNode);
116     CHECK_NULL_RETURN(frameNode, false);
117     auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
118     CHECK_NULL_RETURN(pipelineContext, false);
119     axisEvent.passThrough = true;
120     passThroughResult_ = false;
121     pipelineContext->OnAxisEvent(axisEvent, frameNode);
122     axisEvent.passThrough = false;
123     return passThroughResult_;
124 }
125 
CheckTouchEvent(const RefPtr<NG::UINode> & targetNode,const TouchEvent & touchEvent)126 bool PostEventManager::CheckTouchEvent(const RefPtr<NG::UINode>& targetNode, const TouchEvent& touchEvent)
127 {
128     CHECK_NULL_RETURN(targetNode, false);
129     bool hasDown = false;
130     bool hasUpOrCancel = false;
131     for (const auto& item : postInputEventAction_) {
132         if (item.targetNode != targetNode || item.touchEvent.id != touchEvent.id) {
133             continue;
134         }
135         if (item.touchEvent.type == TouchType::DOWN) {
136             hasDown = true;
137         }
138         if (item.touchEvent.type == TouchType::UP || item.touchEvent.type == TouchType::CANCEL) {
139             hasUpOrCancel = true;
140         }
141     }
142     switch (touchEvent.type) {
143         case TouchType::DOWN:
144             if (hasDown && !hasUpOrCancel) {
145                 TAG_LOGD(AceLogTag::ACE_INPUTKEYFLOW,
146                     "CheckTouchEvent: duplicate DOWN event detected for id=%{public}d, dropping this event",
147                     touchEvent.id);
148                 return false;
149             }
150             if (hasUpOrCancel) {
151                 ClearPostInputActions(targetNode, touchEvent.id);
152             }
153             return true;
154         case TouchType::UP:
155         case TouchType::CANCEL:
156             return hasDown && !hasUpOrCancel;
157         default:
158             TAG_LOGD(AceLogTag::ACE_INPUTKEYFLOW, "CheckTouchEvent: unsupported touch type=%{public}d, id=%{public}d",
159                 static_cast<int>(touchEvent.type), touchEvent.id);
160             return false;
161     }
162 }
163 
ClearPostInputActions(const RefPtr<NG::UINode> & targetNode,int32_t id)164 void PostEventManager::ClearPostInputActions(const RefPtr<NG::UINode>& targetNode, int32_t id)
165 {
166     for (auto item = postInputEventAction_.begin(); item != postInputEventAction_.end();) {
167         if (item->targetNode == targetNode && item->touchEvent.id == id) {
168             item = postInputEventAction_.erase(item);
169         } else {
170             ++item;
171         }
172     }
173 }
174 
PostDownEvent(const RefPtr<NG::UINode> & targetNode,const TouchEvent & touchEvent)175 bool PostEventManager::PostDownEvent(const RefPtr<NG::UINode>& targetNode, const TouchEvent& touchEvent)
176 {
177     CHECK_NULL_RETURN(targetNode, false);
178 
179     for (const auto& iter : postEventAction_) {
180         if (iter.targetNode == targetNode && iter.touchEvent.type == TouchType::DOWN &&
181             iter.touchEvent.id == touchEvent.id) {
182             auto lastEventMap = lastEventMap_;
183             auto lastItem = lastEventMap.find(touchEvent.id);
184             if (lastItem != lastEventMap.end()) {
185                 auto event = lastItem->second.touchEvent;
186                 event.type = TouchType::CANCEL;
187                 PostUpEvent(lastItem->second.targetNode, event);
188                 break;
189             }
190             TAG_LOGW(
191                 AceLogTag::ACE_INPUTKEYFLOW, "PostEvent receive DOWN event twice, id is %{public}d", touchEvent.id);
192             return false;
193         }
194     }
195     auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
196     CHECK_NULL_RETURN(pipelineContext, false);
197     auto eventManager = pipelineContext->GetEventManager();
198     CHECK_NULL_RETURN(eventManager, false);
199     auto scalePoint = touchEvent.CreateScalePoint(pipelineContext->GetViewScale());
200     eventManager->GetEventTreeRecord(EventTreeType::POST_EVENT).AddTouchPoint(scalePoint);
201     TouchRestrict touchRestrict { TouchRestrict::NONE };
202     touchRestrict.sourceType = touchEvent.sourceType;
203     touchRestrict.touchEvent = touchEvent;
204     touchRestrict.inputEventType = InputEventType::TOUCH_SCREEN;
205     touchRestrict.touchTestType = EventTreeType::POST_EVENT;
206     auto result = eventManager->PostEventTouchTest(scalePoint, targetNode, touchRestrict);
207     if (!result) {
208         TAG_LOGI(AceLogTag::ACE_INPUTKEYFLOW, "PostDownEvent id: %{public}d touch test result is empty", touchEvent.id);
209         return false;
210     }
211     if (StylusDetectorMgr::GetInstance()->IsNeedInterceptedTouchEvent(
212         scalePoint, eventManager->postEventTouchTestResults_)) {
213         eventManager->ClearTouchTestTargetForPenStylus(scalePoint);
214         return true;
215     }
216     HandlePostEvent(targetNode, touchEvent);
217     return true;
218 }
219 
PostMoveEvent(const RefPtr<NG::UINode> & targetNode,const TouchEvent & touchEvent)220 bool PostEventManager::PostMoveEvent(const RefPtr<NG::UINode>& targetNode, const TouchEvent& touchEvent)
221 {
222     CHECK_NULL_RETURN(targetNode, false);
223 
224     if (!HaveReceiveDownEvent(targetNode, touchEvent.id) || HaveReceiveUpOrCancelEvent(targetNode, touchEvent.id)) {
225         TAG_LOGW(AceLogTag::ACE_INPUTKEYFLOW,
226             "PostMoveEvent id: %{public}d doesn't receive down event or has receive up or cancel event", touchEvent.id);
227         return false;
228     }
229 
230     HandlePostEvent(targetNode, touchEvent);
231     return true;
232 }
233 
PostUpEvent(const RefPtr<NG::UINode> & targetNode,const TouchEvent & touchEvent)234 bool PostEventManager::PostUpEvent(const RefPtr<NG::UINode>& targetNode, const TouchEvent& touchEvent)
235 {
236     CHECK_NULL_RETURN(targetNode, false);
237 
238     if (!HaveReceiveDownEvent(targetNode, touchEvent.id) || HaveReceiveUpOrCancelEvent(targetNode, touchEvent.id)) {
239         TAG_LOGW(AceLogTag::ACE_INPUTKEYFLOW,
240             "PostUpEvent id: %{public}d doesn't receive down event or has receive up or cancel event", touchEvent.id);
241         return false;
242     }
243 
244     HandlePostEvent(targetNode, touchEvent);
245     return true;
246 }
247 
HandlePostEvent(const RefPtr<NG::UINode> & targetNode,const TouchEvent & touchEvent)248 void PostEventManager::HandlePostEvent(const RefPtr<NG::UINode>& targetNode, const TouchEvent& touchEvent)
249 {
250     // push dispatchAction and store
251     PostEventAction postEventAction;
252     postEventAction.targetNode = targetNode;
253     postEventAction.touchEvent = touchEvent;
254     lastEventMap_[touchEvent.id] = postEventAction;
255     postEventAction_.push_back(postEventAction);
256     auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck();
257     CHECK_NULL_VOID(pipelineContext);
258     auto eventManager = pipelineContext->GetEventManager();
259     if (touchEvent.type != TouchType::DOWN && touchEvent.type != TouchType::MOVE) {
260         eventManager->GetEventTreeRecord(EventTreeType::POST_EVENT).AddTouchPoint(touchEvent);
261     }
262     eventManager->PostEventFlushTouchEventEnd(touchEvent);
263     eventManager->PostEventDispatchTouchEvent(touchEvent);
264     // when receive UP event, clear DispatchAction which is same targetNode and same id
265     CheckAndClearPostEventAction(targetNode, touchEvent.id);
266 }
267 
CheckAndClearPostEventAction(const RefPtr<NG::UINode> & targetNode,int32_t id)268 void PostEventManager::CheckAndClearPostEventAction(const RefPtr<NG::UINode>& targetNode, int32_t id)
269 {
270     bool receiveDown = false;
271     bool receiveUp = false;
272     for (const auto& iter : postEventAction_) {
273         if (iter.targetNode == targetNode && iter.touchEvent.id == id) {
274             if (iter.touchEvent.type == TouchType::DOWN) {
275                 receiveDown = true;
276             } else if ((iter.touchEvent.type == TouchType::UP || iter.touchEvent.type == TouchType::CANCEL) &&
277                        receiveDown) {
278                 receiveUp = true;
279             }
280         }
281     }
282     if (receiveUp) {
283         for (auto iter = postEventAction_.begin(); iter != postEventAction_.end();) {
284             if (iter->targetNode == targetNode && iter->touchEvent.id == id) {
285                 iter = postEventAction_.erase(iter);
286             } else {
287                 ++iter;
288             }
289         }
290         lastEventMap_.erase(id);
291     }
292 }
293 
HaveReceiveDownEvent(const RefPtr<NG::UINode> & targetNode,int32_t id)294 bool PostEventManager::HaveReceiveDownEvent(const RefPtr<NG::UINode>& targetNode, int32_t id)
295 {
296     return std::any_of(postEventAction_.begin(), postEventAction_.end(), [targetNode, id](const auto& actionItem) {
297         return actionItem.targetNode == targetNode && actionItem.touchEvent.type == TouchType::DOWN &&
298                actionItem.touchEvent.id == id;
299     });
300 }
301 
HaveReceiveUpOrCancelEvent(const RefPtr<NG::UINode> & targetNode,int32_t id)302 bool PostEventManager::HaveReceiveUpOrCancelEvent(const RefPtr<NG::UINode>& targetNode, int32_t id)
303 {
304     return std::any_of(postEventAction_.begin(), postEventAction_.end(), [targetNode, id](const auto& actionItem) {
305         return actionItem.targetNode == targetNode &&
306                (actionItem.touchEvent.type == TouchType::UP || actionItem.touchEvent.type == TouchType::CANCEL) &&
307                actionItem.touchEvent.id == id;
308     });
309 }
310 
CheckPointValidity(const TouchEvent & touchEvent)311 bool PostEventManager::CheckPointValidity(const TouchEvent& touchEvent)
312 {
313     return !std::any_of(postEventAction_.begin(), postEventAction_.end(), [touchEvent](const auto& actionItem) {
314         return actionItem.touchEvent.id == touchEvent.id && actionItem.touchEvent.time == touchEvent.time;
315     });
316 }
317 
SetPassThroughResult(bool passThroughResult)318 void PostEventManager::SetPassThroughResult(bool passThroughResult)
319 {
320     passThroughResult_ = passThroughResult;
321 }
322 
GetPostTargetNode()323 RefPtr<FrameNode> PostEventManager::GetPostTargetNode()
324 {
325     return targetNode_.Upgrade();
326 }
327 } // namespace OHOS::Ace::NG
328