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