1 /*
2 * Copyright (c) 2021-2024 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 "event_resample.h"
17
18 #include "event_log_helper.h"
19 #include "input_device_manager.h"
20 #include "i_input_windows_manager.h"
21
22 #undef MMI_LOG_DOMAIN
23 #define MMI_LOG_DOMAIN MMI_LOG_SERVER
24 #undef MMI_LOG_TAG
25 #define MMI_LOG_TAG "EventResample"
26
27 namespace OHOS {
28 namespace MMI {
EventResample()29 EventResample::EventResample(){};
~EventResample()30 EventResample::~EventResample(){};
31
OnEventConsume(std::shared_ptr<PointerEvent> pointerEvent,int64_t frameTime,ErrCode & status)32 std::shared_ptr<PointerEvent> EventResample::OnEventConsume(std::shared_ptr<PointerEvent> pointerEvent,
33 int64_t frameTime, ErrCode &status)
34 {
35 CALL_DEBUG_ENTER;
36 MotionEvent* outEvent = nullptr;
37 ErrCode result = ERR_OK;
38
39 status = ERR_OK;
40 if (ERR_OK != InitializeInputEvent(pointerEvent, frameTime)) {
41 status = ERR_WOULD_BLOCK;
42 return pointerEvent;
43 }
44
45 do {
46 // All events are dispathed so consume batches
47 if (PointerEvent::POINTER_ACTION_UNKNOWN == inputEvent_.pointerAction) {
48 result = ConsumeBatch(frameTime_, &outEvent);
49 frameTime_ = 0;
50 if ((ERR_OK == result) && (nullptr != outEvent)) {
51 status = result;
52 break;
53 } else {
54 status = result;
55 return nullptr;
56 }
57 }
58
59 // Add event into batch
60 if (UpdateBatch(&outEvent, result)) {
61 break;
62 }
63
64 // Update touch state object
65 EventDump("UpdateTouchState", inputEvent_);
66 EventLogHelper::PrintEventData(pointerEvent_, MMI_LOG_HEADER);
67 PrintfDeviceName();
68 UpdateTouchState(inputEvent_);
69 return pointerEvent_;
70 } while (0);
71
72 if ((ERR_OK == result) && (nullptr != outEvent)) {
73 // Update pointer event
74 UpdatePointerEvent(outEvent);
75 EventLogHelper::PrintEventData(pointerEvent_, MMI_LOG_HEADER);
76 PrintfDeviceName();
77 return pointerEvent_;
78 }
79
80 return nullptr;
81 }
82
GetPointerEvent()83 std::shared_ptr<PointerEvent> EventResample::GetPointerEvent()
84 {
85 return pointerEvent_;
86 }
87
EventDump(const char * msg,MotionEvent & event)88 void EventResample::EventDump(const char *msg, MotionEvent &event)
89 {
90 MMI_HILOGD("%{public}s: pointerAction:%{public}d, actionTime:%{public}" PRId64 ", pointerCount:%{public}d,"
91 "sourceType:%{public}d, deviceId:%{public}d, eventId:%{public}d",
92 msg, event.pointerAction, event.actionTime, event.pointerCount,
93 event.sourceType, event.deviceId, event.eventId);
94 for (auto &it : event.pointers) {
95 MMI_HILOGD("ID:%{public}d, coordX:%d, coordY:%d, toolType:%{public}d",
96 it.second.id, it.second.coordX, it.second.coordY, it.second.toolType);
97 }
98 }
99
InitializeInputEvent(std::shared_ptr<PointerEvent> pointerEvent,int64_t frameTime)100 ErrCode EventResample::InitializeInputEvent(std::shared_ptr<PointerEvent> pointerEvent, int64_t frameTime)
101 {
102 int32_t pointerAction = PointerEvent::POINTER_ACTION_UNKNOWN;
103
104 if (pointerEvent != nullptr) {
105 pointerEvent_ = pointerEvent;
106 }
107
108 if (frameTime_ <= 0) {
109 if (0 != frameTime) {
110 frameTime_ = frameTime;
111 } else if (nullptr != pointerEvent) {
112 frameTime_ = GetSysClockTime();
113 } else {
114 frameTime_ = 0;
115 }
116 }
117
118 // Check that event can be consumed and initialize motion event.
119 if (nullptr != pointerEvent) {
120 EventLogHelper::PrintEventData(pointerEvent_, MMI_LOG_HEADER);
121 PrintfDeviceName();
122 pointerAction = pointerEvent->GetPointerAction();
123 MMI_HILOGD("pointerAction:%{public}d, actionTime:%{public}" PRId64 " frameTime_:%{public}" PRId64,
124 pointerAction, pointerEvent->GetActionTime(), frameTime_);
125 switch (pointerAction) {
126 case PointerEvent::POINTER_ACTION_DOWN:
127 case PointerEvent::POINTER_ACTION_MOVE:
128 case PointerEvent::POINTER_ACTION_UP:
129 break;
130 default: {
131 MotionEvent event;
132 event.InitializeFrom(pointerEvent);
133 UpdateTouchState(event);
134 return ERR_WOULD_BLOCK;
135 }
136 }
137 inputEvent_.Reset();
138 inputEvent_.InitializeFrom(pointerEvent);
139
140 EventDump("Input Event", inputEvent_);
141 } else {
142 inputEvent_.Reset();
143 }
144
145 return ERR_OK;
146 }
147
UpdateBatch(MotionEvent ** outEvent,ErrCode & result)148 bool EventResample::UpdateBatch(MotionEvent** outEvent, ErrCode &result)
149 {
150 ssize_t batchIndex = FindBatch(inputEvent_.deviceId, inputEvent_.sourceType);
151 if (batchIndex >= 0) {
152 Batch& batch = batches_.at(batchIndex);
153 if (CanAddSample(batch, inputEvent_)) {
154 batch.samples.push_back(inputEvent_);
155 MMI_HILOGD("Event added to batch, deviceId:%{public}d, sourceType:%{public}d, pointerAction:%{public}d",
156 inputEvent_.deviceId, inputEvent_.sourceType, inputEvent_.pointerAction);
157 return true;
158 }
159 }
160
161 // Start a new batch
162 if (PointerEvent::POINTER_ACTION_MOVE == inputEvent_.pointerAction) {
163 Batch batch;
164 batch.samples.push_back(inputEvent_);
165 batches_.push_back(std::move(batch));
166 return true;
167 }
168
169 return false;
170 }
171
UpdatePointerEvent(MotionEvent * outEvent)172 void EventResample::UpdatePointerEvent(MotionEvent* outEvent)
173 {
174 EventDump("Output Event", *outEvent);
175 CHKPV(pointerEvent_);
176 pointerEvent_->SetActionTime(outEvent->actionTime);
177 pointerEvent_->SetPointerAction(outEvent->pointerAction);
178 pointerEvent_->SetActionTime(outEvent->actionTime);
179 pointerEvent_->SetId(outEvent->eventId);
180
181 for (auto &it : outEvent->pointers) {
182 PointerEvent::PointerItem item;
183 if (pointerEvent_->GetPointerItem(it.first, item)) {
184 int32_t toolWindowX = item.GetToolWindowX();
185 int32_t toolWindowY = item.GetToolWindowY();
186 if (EventLogHelper::IsBetaVersion() && !pointerEvent_->HasFlag(InputEvent::EVENT_FLAG_PRIVACY_MODE)) {
187 MMI_HILOGD("Output event: toolWindowX:%{public}d, toolWindowY:%{public}d", toolWindowX, toolWindowY);
188 } else {
189 MMI_HILOGD("Output event: toolWindowX:%d, toolWindowY:%d", toolWindowX, toolWindowY);
190 }
191 auto logicX = it.second.coordX;
192 auto logicY = it.second.coordY;
193 item.SetDisplayX(logicX);
194 item.SetDisplayY(logicY);
195
196 auto windowXY = TransformSampleWindowXY(pointerEvent_, item, logicX, logicY);
197 item.SetWindowX(windowXY.first);
198 item.SetWindowY(windowXY.second);
199
200 if (PointerEvent::POINTER_ACTION_MOVE == outEvent->pointerAction) {
201 item.SetPressed(true);
202 } else if (PointerEvent::POINTER_ACTION_UP == outEvent->pointerAction) {
203 item.SetPressed(false);
204 } else {
205 MMI_HILOGD("Output event:Pointer action:%{public}d", outEvent->pointerAction);
206 }
207 pointerEvent_->UpdatePointerItem(it.first, item);
208 }
209 }
210 }
211
TransformSampleWindowXY(std::shared_ptr<PointerEvent> pointerEvent,PointerEvent::PointerItem & item,int32_t logicX,int32_t logicY)212 std::pair<int32_t, int32_t> EventResample::TransformSampleWindowXY(std::shared_ptr<PointerEvent> pointerEvent,
213 PointerEvent::PointerItem &item, int32_t logicX, int32_t logicY)
214 {
215 CALL_DEBUG_ENTER;
216 if (pointerEvent == nullptr) {
217 return {logicX + item.GetToolWindowX(), logicY + item.GetToolWindowY()};
218 }
219 auto windows = WIN_MGR->GetWindowGroupInfoByDisplayId(pointerEvent->GetTargetDisplayId());
220 for (const auto &window : windows) {
221 if (pointerEvent->GetTargetWindowId() == window.id) {
222 if (window.transform.empty()) {
223 return {logicX + item.GetToolWindowX(), logicY + item.GetToolWindowY()};
224 }
225 auto windowXY = WIN_MGR->TransformWindowXY(window, logicX, logicY);
226 auto windowX = static_cast<int32_t>(windowXY.first);
227 auto windowY = static_cast<int32_t>(windowXY.second);
228 return {windowX, windowY};
229 }
230 }
231 return {logicX, logicY};
232 }
233
ConsumeBatch(int64_t frameTime,MotionEvent ** outEvent)234 ErrCode EventResample::ConsumeBatch(int64_t frameTime, MotionEvent** outEvent)
235 {
236 int32_t result = 0;
237 for (size_t i = batches_.size(); i > 0;) {
238 i--;
239 Batch& batch = batches_.at(i);
240 if (frameTime < 0) {
241 result = ConsumeSamples(batch, batch.samples.size(), outEvent);
242 batches_.erase(batches_.begin() + i);
243 return result;
244 }
245
246 int64_t sampleTime = frameTime;
247 if (resampleTouch_) {
248 sampleTime -= RESAMPLE_LATENCY;
249 }
250 ssize_t split = FindSampleNoLaterThan(batch, sampleTime);
251 if (split < 0) {
252 continue;
253 }
254
255 result = ConsumeSamples(batch, split + 1, outEvent);
256 const MotionEvent* next;
257 if (batch.samples.empty()) {
258 batches_.erase(batches_.begin() + i);
259 next = NULL;
260 } else {
261 next = &batch.samples.at(0);
262 }
263 if (!result && resampleTouch_) {
264 ResampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next);
265 }
266 return result;
267 }
268
269 return ERR_WOULD_BLOCK;
270 }
271
ConsumeSamples(Batch & batch,size_t count,MotionEvent ** outEvent)272 ErrCode EventResample::ConsumeSamples(Batch& batch, size_t count, MotionEvent** outEvent)
273 {
274 outputEvent_.Reset();
275
276 for (size_t i = 0; i < count; i++) {
277 MotionEvent& event = batch.samples.at(i);
278 UpdateTouchState(event);
279 if (i > 0) {
280 AddSample(&outputEvent_, &event);
281 } else {
282 outputEvent_.InitializeFrom(event);
283 }
284 }
285 batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
286
287 *outEvent = &outputEvent_;
288
289 return ERR_OK;
290 }
291
AddSample(MotionEvent * outEvent,const MotionEvent * event)292 void EventResample::AddSample(MotionEvent* outEvent, const MotionEvent* event)
293 {
294 outEvent->actionTime = event->actionTime;
295 for (auto &it : event->pointers) {
296 outEvent->pointers[it.first] = it.second;
297 }
298 }
299
UpdateTouchState(MotionEvent & event)300 void EventResample::UpdateTouchState(MotionEvent &event)
301 {
302 int32_t deviceId = event.deviceId;
303 int32_t source = event.sourceType;
304
305 switch (event.pointerAction) {
306 case PointerEvent::POINTER_ACTION_DOWN: {
307 ssize_t idx = FindTouchState(deviceId, source);
308 if (idx < 0) {
309 TouchState newState;
310 touchStates_.push_back(newState);
311 idx = static_cast<ssize_t>(touchStates_.size()) - 1;
312 }
313 TouchState& touchState = touchStates_.at(idx);
314 touchState.Initialize(deviceId, source);
315 touchState.AddHistory(event);
316 break;
317 }
318 case PointerEvent::POINTER_ACTION_MOVE: {
319 ssize_t idx = FindTouchState(deviceId, source);
320 if (idx >= 0) {
321 TouchState& touchState = touchStates_.at(idx);
322 touchState.AddHistory(event);
323 RewriteMessage(touchState, event);
324 }
325 break;
326 }
327 case PointerEvent::POINTER_ACTION_UP:
328 default: {
329 ssize_t idx = FindTouchState(deviceId, source);
330 if (idx >= 0) {
331 TouchState& touchState = touchStates_.at(idx);
332 RewriteMessage(touchState, event);
333 touchStates_.erase(touchStates_.begin() + idx);
334 }
335 frameTime_ = 0;
336 idx = FindBatch(deviceId, source);
337 if (idx >= 0) {
338 batches_.erase(batches_.begin() + idx);
339 }
340 break;
341 }
342 }
343 }
344
ResampleTouchState(int64_t sampleTime,MotionEvent * event,const MotionEvent * next)345 void EventResample::ResampleTouchState(int64_t sampleTime, MotionEvent* event, const MotionEvent* next)
346 {
347 if (!resampleTouch_ || (PointerEvent::SOURCE_TYPE_TOUCHSCREEN != event->sourceType)
348 || (PointerEvent::POINTER_ACTION_MOVE != event->pointerAction)) {
349 return;
350 }
351
352 ssize_t idx = FindTouchState(event->deviceId, event->sourceType);
353 if (idx < 0) {
354 return;
355 }
356
357 TouchState &touchState = touchStates_.at(idx);
358 if (touchState.historySize < 1) {
359 return;
360 }
361
362 // Ensure that the current sample has all of the pointers that need to be reported.
363 const History* current = touchState.GetHistory(0);
364 for (auto &it : event->pointers) {
365 if (!current->HasPointerId(it.first)) {
366 return;
367 }
368 }
369
370 // Find the data to use for resampling.
371 const History* other;
372 History future;
373 float alpha;
374 if (next) {
375 // Interpolate between current sample and future sample.
376 // So current->actionTime <= sampleTime <= future.actionTime.
377 future.InitializeFrom(*next);
378 other = &future;
379 int64_t delta = future.actionTime - current->actionTime;
380 if (delta < RESAMPLE_MIN_DELTA) {
381 return;
382 }
383 alpha = static_cast<float>(sampleTime - current->actionTime) / delta;
384 } else if (touchState.historySize >= HISTORY_SIZE_MAX) {
385 // Extrapolate future sample using current sample and past sample.
386 // So other->actionTime <= current->actionTime <= sampleTime.
387 other = touchState.GetHistory(1);
388 int64_t delta = current->actionTime - other->actionTime;
389 if (delta < RESAMPLE_MIN_DELTA) {
390 return;
391 } else if (delta > RESAMPLE_MAX_DELTA) {
392 return;
393 }
394 int64_t maxPredict = current->actionTime + std::min(delta / 2, RESAMPLE_MAX_PREDICTION);
395 if (sampleTime > maxPredict) {
396 sampleTime = maxPredict;
397 }
398 alpha = static_cast<float>(current->actionTime - sampleTime) / delta;
399 } else {
400 return;
401 }
402
403 // Resample touch coordinates.
404 ResampleCoordinates(sampleTime, event, touchState, current, other, alpha);
405 }
406
ResampleCoordinates(int64_t sampleTime,MotionEvent * event,TouchState & touchState,const History * current,const History * other,float alpha)407 void EventResample::ResampleCoordinates(int64_t sampleTime, MotionEvent* event, TouchState &touchState,
408 const History* current, const History* other, float alpha)
409 {
410 History oldLastResample;
411 oldLastResample.InitializeFrom(touchState.lastResample);
412 touchState.lastResample.actionTime = sampleTime;
413
414 for (auto &it : event->pointers) {
415 uint32_t id = it.first;
416 if (oldLastResample.HasPointerId(id) && touchState.RecentCoordinatesAreIdentical(id)) {
417 auto lastItem = touchState.lastResample.pointers.find(id);
418 if (lastItem != touchState.lastResample.pointers.end()) {
419 auto oldLastItem = oldLastResample.pointers.find(id);
420 lastItem->second.CopyFrom(oldLastItem->second);
421 }
422 continue;
423 }
424
425 Pointer resampledCoords;
426 const Pointer& currentCoords = current->GetPointerById(id);
427 resampledCoords.CopyFrom(currentCoords);
428 auto item = event->pointers.find(id);
429 if (item == event->pointers.end()) {
430 return;
431 }
432 if (other->HasPointerId(id) && ShouldResampleTool(item->second.toolType)) {
433 const Pointer& otherCoords = other->GetPointerById(id);
434 resampledCoords.coordX = CalcCoord(currentCoords.coordX, otherCoords.coordX, alpha);
435 resampledCoords.coordY = CalcCoord(currentCoords.coordY, otherCoords.coordY, alpha);
436 }
437 item->second.CopyFrom(resampledCoords);
438 event->actionTime = sampleTime;
439 }
440 }
441
FindBatch(int32_t deviceId,int32_t source) const442 ssize_t EventResample::FindBatch(int32_t deviceId, int32_t source) const
443 {
444 ssize_t idx = 0;
445 for (auto it = batches_.begin(); it < batches_.end(); ++it, ++idx) {
446 const MotionEvent& head = it->samples.at(0);
447 if ((head.deviceId == deviceId) && (head.sourceType == source)) {
448 return idx;
449 }
450 }
451 return -1;
452 }
453
FindTouchState(int32_t deviceId,int32_t source) const454 ssize_t EventResample::FindTouchState(int32_t deviceId, int32_t source) const
455 {
456 ssize_t idx = 0;
457 for (auto it = touchStates_.begin(); it < touchStates_.end(); ++it, ++idx) {
458 if ((it->deviceId == deviceId) && (it->source == source)) {
459 return idx;
460 }
461 }
462 return -1;
463 }
464
CanAddSample(const Batch & batch,MotionEvent & event)465 bool EventResample::CanAddSample(const Batch &batch, MotionEvent &event)
466 {
467 const MotionEvent& head = batch.samples.at(0);
468 uint32_t pointerCount = event.pointerCount;
469 int32_t pointerAction = event.pointerAction;
470 if ((head.pointerCount != pointerCount) || (head.pointerAction != pointerAction)) {
471 return false;
472 }
473
474 return true;
475 }
476
RewriteMessage(TouchState & state,MotionEvent & event)477 void EventResample::RewriteMessage(TouchState& state, MotionEvent &event)
478 {
479 for (auto &it : event.pointers) {
480 uint32_t id = it.first;
481 if (state.lastResample.HasPointerId(id)) {
482 if ((event.actionTime < state.lastResample.actionTime) || state.RecentCoordinatesAreIdentical(id)) {
483 Pointer& msgCoords = it.second;
484 const Pointer& resampleCoords = state.lastResample.GetPointerById(id);
485 msgCoords.CopyFrom(resampleCoords);
486 } else {
487 state.lastResample.pointers.erase(id);
488 }
489 }
490 }
491 }
492
FindSampleNoLaterThan(const Batch & batch,int64_t time)493 ssize_t EventResample::FindSampleNoLaterThan(const Batch& batch, int64_t time)
494 {
495 size_t numSamples = batch.samples.size();
496 size_t idx = 0;
497 while ((idx < numSamples) && (batch.samples.at(idx).actionTime <= time)) {
498 idx += 1;
499 }
500 return ssize_t(idx) - 1;
501 }
502
ShouldResampleTool(int32_t toolType)503 bool EventResample::ShouldResampleTool(int32_t toolType)
504 {
505 switch (toolType) {
506 case PointerEvent::TOOL_TYPE_FINGER:
507 case PointerEvent::TOOL_TYPE_PEN:
508 return true;
509 default:
510 return false;
511 }
512 }
513
PrintfDeviceName()514 void EventResample::PrintfDeviceName()
515 {
516 auto device = INPUT_DEV_MGR->GetInputDevice(pointerEvent_->GetDeviceId());
517 CHKPV(device);
518 MMI_HILOGI("InputTracking id:%{public}d event created by:%{public}s", pointerEvent_->GetId(),
519 device->GetName().c_str());
520 }
521
522 } // namespace MMI
523 } // namespace OHOS
524