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