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:%{private}d, toolWindowY:%{private}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 item.SetDisplayXPos(logicX);
196 item.SetDisplayYPos(logicY);
197
198 auto windowXY = TransformSampleWindowXY(pointerEvent_, item, logicX, logicY);
199 item.SetWindowX(static_cast<int32_t>(windowXY.first));
200 item.SetWindowY(static_cast<int32_t>(windowXY.second));
201 item.SetWindowXPos(windowXY.first);
202 item.SetWindowYPos(windowXY.second);
203
204 if (PointerEvent::POINTER_ACTION_MOVE == outEvent->pointerAction) {
205 item.SetPressed(true);
206 } else if (PointerEvent::POINTER_ACTION_UP == outEvent->pointerAction) {
207 item.SetPressed(false);
208 } else {
209 MMI_HILOGD("Output event:Pointer action:%{public}d", outEvent->pointerAction);
210 }
211 pointerEvent_->UpdatePointerItem(it.first, item);
212 }
213 }
214 }
215
TransformSampleWindowXY(std::shared_ptr<PointerEvent> pointerEvent,PointerEvent::PointerItem & item,double logicX,double logicY)216 std::pair<double, double> EventResample::TransformSampleWindowXY(std::shared_ptr<PointerEvent> pointerEvent,
217 PointerEvent::PointerItem &item, double logicX, double logicY)
218 {
219 CALL_DEBUG_ENTER;
220 if (pointerEvent == nullptr) {
221 return {logicX + item.GetToolWindowX(), logicY + item.GetToolWindowY()};
222 }
223 auto windows = WIN_MGR->GetWindowGroupInfoByDisplayIdCopy(pointerEvent->GetTargetDisplayId());
224 for (const auto &window : windows) {
225 if (pointerEvent->GetTargetWindowId() == window.id) {
226 if (window.transform.empty()) {
227 return {logicX + item.GetToolWindowX(), logicY + item.GetToolWindowY()};
228 }
229 auto physicalDisplayInfo = WIN_MGR->GetPhysicalDisplay(window.displayId);
230 if (physicalDisplayInfo == nullptr) {
231 MMI_HILOGE("physicalDisplayInfo is null");
232 return {logicX + item.GetToolWindowX(), logicY + item.GetToolWindowY()};
233 }
234 auto windowXY = WIN_MGR->TransformWindowXY(window, logicX + physicalDisplayInfo->x,
235 logicY + physicalDisplayInfo->y);
236 return {windowXY.first, windowXY.second};
237 }
238 }
239 return {logicX, logicY};
240 }
241
ConsumeBatch(int64_t frameTime,MotionEvent ** outEvent)242 ErrCode EventResample::ConsumeBatch(int64_t frameTime, MotionEvent** outEvent)
243 {
244 int32_t result = 0;
245 for (size_t i = batches_.size(); i > 0;) {
246 i--;
247 Batch& batch = batches_.at(i);
248 if (frameTime < 0) {
249 result = ConsumeSamples(batch, batch.samples.size(), outEvent);
250 batches_.erase(batches_.begin() + i);
251 return result;
252 }
253
254 int64_t sampleTime = frameTime;
255 if (resampleTouch_) {
256 sampleTime -= RESAMPLE_LATENCY;
257 }
258 ssize_t split = FindSampleNoLaterThan(batch, sampleTime);
259 if (split < 0) {
260 continue;
261 }
262
263 result = ConsumeSamples(batch, split + 1, outEvent);
264 const MotionEvent* next;
265 if (batch.samples.empty()) {
266 batches_.erase(batches_.begin() + i);
267 next = NULL;
268 } else {
269 next = &batch.samples.at(0);
270 }
271 if (!result && resampleTouch_) {
272 ResampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next);
273 }
274 return result;
275 }
276
277 return ERR_WOULD_BLOCK;
278 }
279
ConsumeSamples(Batch & batch,size_t count,MotionEvent ** outEvent)280 ErrCode EventResample::ConsumeSamples(Batch& batch, size_t count, MotionEvent** outEvent)
281 {
282 outputEvent_.Reset();
283
284 for (size_t i = 0; i < count; i++) {
285 MotionEvent& event = batch.samples.at(i);
286 UpdateTouchState(event);
287 if (i > 0) {
288 AddSample(&outputEvent_, &event);
289 } else {
290 outputEvent_.InitializeFrom(event);
291 }
292 }
293 batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
294
295 *outEvent = &outputEvent_;
296
297 return ERR_OK;
298 }
299
AddSample(MotionEvent * outEvent,const MotionEvent * event)300 void EventResample::AddSample(MotionEvent* outEvent, const MotionEvent* event)
301 {
302 outEvent->actionTime = event->actionTime;
303 for (auto &it : event->pointers) {
304 outEvent->pointers[it.first] = it.second;
305 }
306 }
307
UpdateTouchState(MotionEvent & event)308 void EventResample::UpdateTouchState(MotionEvent &event)
309 {
310 int32_t deviceId = event.deviceId;
311 int32_t source = event.sourceType;
312
313 switch (event.pointerAction) {
314 case PointerEvent::POINTER_ACTION_DOWN: {
315 ssize_t idx = FindTouchState(deviceId, source);
316 if (idx < 0) {
317 TouchState newState;
318 touchStates_.push_back(newState);
319 idx = static_cast<ssize_t>(touchStates_.size()) - 1;
320 }
321 TouchState& touchState = touchStates_.at(idx);
322 touchState.Initialize(deviceId, source);
323 touchState.AddHistory(event);
324 break;
325 }
326 case PointerEvent::POINTER_ACTION_MOVE: {
327 ssize_t idx = FindTouchState(deviceId, source);
328 if (idx >= 0) {
329 TouchState& touchState = touchStates_.at(idx);
330 touchState.AddHistory(event);
331 RewriteMessage(touchState, event);
332 }
333 break;
334 }
335 case PointerEvent::POINTER_ACTION_UP:
336 default: {
337 ssize_t idx = FindTouchState(deviceId, source);
338 if (idx >= 0) {
339 TouchState& touchState = touchStates_.at(idx);
340 RewriteMessage(touchState, event);
341 touchStates_.erase(touchStates_.begin() + idx);
342 }
343 frameTime_ = 0;
344 idx = FindBatch(deviceId, source);
345 if (idx >= 0) {
346 batches_.erase(batches_.begin() + idx);
347 }
348 break;
349 }
350 }
351 }
352
ResampleTouchState(int64_t sampleTime,MotionEvent * event,const MotionEvent * next)353 void EventResample::ResampleTouchState(int64_t sampleTime, MotionEvent* event, const MotionEvent* next)
354 {
355 if (!resampleTouch_ || (PointerEvent::SOURCE_TYPE_TOUCHSCREEN != event->sourceType)
356 || (PointerEvent::POINTER_ACTION_MOVE != event->pointerAction)) {
357 return;
358 }
359
360 ssize_t idx = FindTouchState(event->deviceId, event->sourceType);
361 if (idx < 0) {
362 return;
363 }
364
365 TouchState &touchState = touchStates_.at(idx);
366 if (touchState.historySize < 1) {
367 return;
368 }
369
370 // Ensure that the current sample has all of the pointers that need to be reported.
371 const History* current = touchState.GetHistory(0);
372 for (auto &it : event->pointers) {
373 if (!current->HasPointerId(it.first)) {
374 return;
375 }
376 }
377
378 // Find the data to use for resampling.
379 const History* other;
380 History future;
381 float alpha;
382 if (next) {
383 // Interpolate between current sample and future sample.
384 // So current->actionTime <= sampleTime <= future.actionTime.
385 future.InitializeFrom(*next);
386 other = &future;
387 int64_t delta = future.actionTime - current->actionTime;
388 if (delta < RESAMPLE_MIN_DELTA) {
389 return;
390 }
391 alpha = static_cast<float>(sampleTime - current->actionTime) / delta;
392 } else if (touchState.historySize >= HISTORY_SIZE_MAX) {
393 // Extrapolate future sample using current sample and past sample.
394 // So other->actionTime <= current->actionTime <= sampleTime.
395 other = touchState.GetHistory(1);
396 int64_t delta = current->actionTime - other->actionTime;
397 if (delta < RESAMPLE_MIN_DELTA) {
398 return;
399 } else if (delta > RESAMPLE_MAX_DELTA) {
400 return;
401 }
402 int64_t maxPredict = current->actionTime + std::min(delta / 2, RESAMPLE_MAX_PREDICTION);
403 if (sampleTime > maxPredict) {
404 sampleTime = maxPredict;
405 }
406 alpha = static_cast<float>(current->actionTime - sampleTime) / delta;
407 } else {
408 return;
409 }
410
411 // Resample touch coordinates.
412 ResampleCoordinates(sampleTime, event, touchState, current, other, alpha);
413 }
414
ResampleCoordinates(int64_t sampleTime,MotionEvent * event,TouchState & touchState,const History * current,const History * other,float alpha)415 void EventResample::ResampleCoordinates(int64_t sampleTime, MotionEvent* event, TouchState &touchState,
416 const History* current, const History* other, float alpha)
417 {
418 History oldLastResample;
419 oldLastResample.InitializeFrom(touchState.lastResample);
420 touchState.lastResample.actionTime = sampleTime;
421
422 for (auto &it : event->pointers) {
423 uint32_t id = it.first;
424 if (oldLastResample.HasPointerId(id) && touchState.RecentCoordinatesAreIdentical(id)) {
425 auto lastItem = touchState.lastResample.pointers.find(id);
426 if (lastItem != touchState.lastResample.pointers.end()) {
427 auto oldLastItem = oldLastResample.pointers.find(id);
428 lastItem->second.CopyFrom(oldLastItem->second);
429 }
430 continue;
431 }
432
433 Pointer resampledCoords;
434 const Pointer& currentCoords = current->GetPointerById(id);
435 resampledCoords.CopyFrom(currentCoords);
436 auto item = event->pointers.find(id);
437 if (item == event->pointers.end()) {
438 return;
439 }
440 if (other->HasPointerId(id) && ShouldResampleTool(item->second.toolType)) {
441 const Pointer& otherCoords = other->GetPointerById(id);
442 resampledCoords.coordX = CalcCoord(currentCoords.coordX, otherCoords.coordX, alpha);
443 resampledCoords.coordY = CalcCoord(currentCoords.coordY, otherCoords.coordY, alpha);
444 }
445 item->second.CopyFrom(resampledCoords);
446 event->actionTime = sampleTime;
447 }
448 }
449
FindBatch(int32_t deviceId,int32_t source) const450 ssize_t EventResample::FindBatch(int32_t deviceId, int32_t source) const
451 {
452 ssize_t idx = 0;
453 for (auto it = batches_.begin(); it < batches_.end(); ++it, ++idx) {
454 const MotionEvent& head = it->samples.at(0);
455 if ((head.deviceId == deviceId) && (head.sourceType == source)) {
456 return idx;
457 }
458 }
459 return -1;
460 }
461
FindTouchState(int32_t deviceId,int32_t source) const462 ssize_t EventResample::FindTouchState(int32_t deviceId, int32_t source) const
463 {
464 ssize_t idx = 0;
465 for (auto it = touchStates_.begin(); it < touchStates_.end(); ++it, ++idx) {
466 if ((it->deviceId == deviceId) && (it->source == source)) {
467 return idx;
468 }
469 }
470 return -1;
471 }
472
CanAddSample(const Batch & batch,MotionEvent & event)473 bool EventResample::CanAddSample(const Batch &batch, MotionEvent &event)
474 {
475 const MotionEvent& head = batch.samples.at(0);
476 uint32_t pointerCount = event.pointerCount;
477 int32_t pointerAction = event.pointerAction;
478 if ((head.pointerCount != pointerCount) || (head.pointerAction != pointerAction)) {
479 return false;
480 }
481
482 return true;
483 }
484
RewriteMessage(TouchState & state,MotionEvent & event)485 void EventResample::RewriteMessage(TouchState& state, MotionEvent &event)
486 {
487 for (auto &it : event.pointers) {
488 uint32_t id = it.first;
489 if (state.lastResample.HasPointerId(id)) {
490 if ((event.actionTime < state.lastResample.actionTime) || state.RecentCoordinatesAreIdentical(id)) {
491 Pointer& msgCoords = it.second;
492 const Pointer& resampleCoords = state.lastResample.GetPointerById(id);
493 msgCoords.CopyFrom(resampleCoords);
494 } else {
495 state.lastResample.pointers.erase(id);
496 }
497 }
498 }
499 }
500
FindSampleNoLaterThan(const Batch & batch,int64_t time)501 ssize_t EventResample::FindSampleNoLaterThan(const Batch& batch, int64_t time)
502 {
503 size_t numSamples = batch.samples.size();
504 size_t idx = 0;
505 while ((idx < numSamples) && (batch.samples.at(idx).actionTime <= time)) {
506 idx += 1;
507 }
508 return ssize_t(idx) - 1;
509 }
510
ShouldResampleTool(int32_t toolType)511 bool EventResample::ShouldResampleTool(int32_t toolType)
512 {
513 switch (toolType) {
514 case PointerEvent::TOOL_TYPE_FINGER:
515 case PointerEvent::TOOL_TYPE_PEN:
516 return true;
517 default:
518 return false;
519 }
520 }
521
PrintfDeviceName()522 void EventResample::PrintfDeviceName()
523 {
524 CHKPV(pointerEvent_);
525 auto device = INPUT_DEV_MGR->GetInputDevice(pointerEvent_->GetDeviceId());
526 CHKPV(device);
527 MMI_HILOGI("InputTracking id:%{public}d event created by:%{public}s", pointerEvent_->GetId(),
528 device->GetName().c_str());
529 }
530
531 } // namespace MMI
532 } // namespace OHOS
533