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