• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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