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