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