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