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