1 /* 2 * Copyright (c) 2021-2022 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 <cmath> 17 #include "ui_action.h" 18 19 namespace OHOS::uitest { 20 using namespace std; 21 using namespace nlohmann; 22 DecomposeClick(PointerMatrix & recv,const Point & point,const UiOpArgs & options)23 static void DecomposeClick(PointerMatrix &recv, const Point &point, const UiOpArgs &options) 24 { 25 constexpr uint32_t fingers = 1; 26 constexpr uint32_t steps = 2; 27 PointerMatrix pointer(fingers, steps); 28 pointer.PushAction(TouchEvent {ActionStage::DOWN, point, 0, options.clickHoldMs_}); 29 pointer.PushAction(TouchEvent {ActionStage::UP, point, options.clickHoldMs_, 0}); 30 recv = move(pointer); 31 } 32 DecomposeLongClick(PointerMatrix & recv,const Point & point,const UiOpArgs & options)33 static void DecomposeLongClick(PointerMatrix &recv, const Point &point, const UiOpArgs &options) 34 { 35 // should sleep after touch-down to make long-click duration 36 constexpr uint32_t fingers = 1; 37 constexpr uint32_t steps = 2; 38 PointerMatrix pointer(fingers, steps); 39 pointer.PushAction(TouchEvent {ActionStage::DOWN, point, 0, options.longClickHoldMs_}); 40 pointer.PushAction(TouchEvent {ActionStage::UP, point, options.longClickHoldMs_, 0}); 41 recv = move(pointer); 42 } 43 DecomposeDoubleClick(PointerMatrix & recv,const Point & point,const UiOpArgs & options)44 static void DecomposeDoubleClick(PointerMatrix &recv, const Point &point, const UiOpArgs &options) 45 { 46 const auto msInterval = options.doubleClickIntervalMs_; 47 constexpr uint32_t fingers = 1; 48 constexpr uint32_t steps = 4; 49 PointerMatrix pointer(fingers, steps); 50 pointer.PushAction(TouchEvent {ActionStage::DOWN, point, 0, options.clickHoldMs_}); 51 pointer.PushAction(TouchEvent {ActionStage::UP, point, options.clickHoldMs_, msInterval}); 52 53 pointer.PushAction(TouchEvent {ActionStage::DOWN, point, 0, options.clickHoldMs_}); 54 pointer.PushAction(TouchEvent {ActionStage::UP, point, options.clickHoldMs_, 0}); 55 recv = move(pointer); 56 } 57 DecomposeComputeSwipe(PointerMatrix & recv,const Point & from,const Point & to,TouchOp type,const UiOpArgs & options)58 static void DecomposeComputeSwipe(PointerMatrix &recv, const Point &from, const Point &to, TouchOp type, 59 const UiOpArgs &options) 60 { 61 if (from.displayId_ != to.displayId_) { 62 LOG_W("Cross-screen operation is not support."); 63 return; 64 } 65 const int32_t distanceX = to.px_ - from.px_; 66 const int32_t distanceY = to.py_ - from.py_; 67 const uint32_t distance = sqrt(distanceX * distanceX + distanceY * distanceY); 68 const uint32_t timeCostMs = (distance * 1000) / options.swipeVelocityPps_; 69 if (distance < 1) { 70 // do not need to execute swipe 71 return; 72 } 73 uint32_t steps = options.swipeStepsCounts_; 74 uint32_t intervalMs = timeCostMs / steps + 1; 75 constexpr uint32_t fingers = 1; 76 constexpr uint32_t intervalMsInSwipe = 5; 77 if (type != TouchOp::FLING) { 78 steps = timeCostMs / intervalMsInSwipe; 79 intervalMs = intervalMsInSwipe; 80 } 81 PointerMatrix pointer(fingers, steps + 1); 82 83 pointer.PushAction(TouchEvent {ActionStage::DOWN, from, 0, intervalMs}); 84 float stepLengthX = static_cast<double>(distanceX) / static_cast<double>(steps); 85 float stepLengthY = static_cast<double>(distanceY) / static_cast<double>(steps); 86 87 for (uint32_t step = 1; step < steps; step++) { 88 const int32_t pointX = from.px_ + stepLengthX * step; 89 const int32_t pointY = from.py_ + stepLengthY * step; 90 const uint32_t timeOffsetMs = (timeCostMs * step) / steps; 91 Point wayPoint(pointX, pointY, from.displayId_); 92 pointer.PushAction(TouchEvent {ActionStage::MOVE, wayPoint, timeOffsetMs, intervalMs}); 93 } 94 95 pointer.PushAction(TouchEvent {ActionStage::UP, to, timeCostMs, intervalMs}); 96 if (type == TouchOp::DRAG) { 97 // drag needs longPressDown firstly 98 pointer.At(fingers - 1, 0).holdMs_ += options.longClickHoldMs_; 99 for (uint32_t idx = 1; idx < pointer.GetSize(); idx++) { 100 pointer.At(fingers - 1, idx).downTimeOffsetMs_ += options.longClickHoldMs_; 101 } 102 } 103 recv = move(pointer); 104 } 105 Decompose(PointerMatrix & recv,const UiOpArgs & options) const106 void GenericClick::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 107 { 108 DCHECK(type_ >= TouchOp::CLICK && type_ <= TouchOp::DOUBLE_CLICK_P); 109 switch (type_) { 110 case CLICK: 111 DecomposeClick(recv, point_, options); 112 break; 113 case LONG_CLICK: 114 DecomposeLongClick(recv, point_, options); 115 break; 116 case DOUBLE_CLICK_P: 117 DecomposeDoubleClick(recv, point_, options); 118 break; 119 default: 120 break; 121 } 122 for (uint32_t index = 0; index < recv.GetSize(); index++) { 123 recv.At(recv.GetFingers() - 1, index).flags_ = type_; 124 } 125 } 126 Decompose(PointerMatrix & recv,const UiOpArgs & options) const127 void GenericSwipe::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 128 { 129 DCHECK(type_ >= TouchOp::SWIPE && type_ <= TouchOp::FLING); 130 DecomposeComputeSwipe(recv, from_, to_, type_, options); 131 for (uint32_t index = 0; index < recv.GetSize(); index++) { 132 recv.At(recv.GetFingers() - 1, index).flags_ = type_; 133 } 134 } 135 Decompose(PointerMatrix & recv,const UiOpArgs & options) const136 void GenericPinch::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 137 { 138 const int32_t distanceX0 = abs(rect_.GetCenterX() - rect_.left_) * abs(scale_ - 1); 139 PointerMatrix pointer1; 140 PointerMatrix pointer2; 141 if (scale_ > 1) { 142 auto fromPoint0 = Point(rect_.GetCenterX() - options.pinchWidgetDeadZone_, rect_.GetCenterY(), 143 rect_.displayId_); 144 auto toPoint0 = Point((fromPoint0.px_ - distanceX0), rect_.GetCenterY(), rect_.displayId_); 145 auto fromPoint1 = Point(rect_.GetCenterX() + options.pinchWidgetDeadZone_, rect_.GetCenterY(), 146 rect_.displayId_); 147 auto toPoint1 = Point((fromPoint1.px_ + distanceX0), rect_.GetCenterY(), rect_.displayId_); 148 DecomposeComputeSwipe(pointer1, fromPoint0, toPoint0, TouchOp::SWIPE, options); 149 DecomposeComputeSwipe(pointer2, fromPoint1, toPoint1, TouchOp::SWIPE, options); 150 } else if (scale_ < 1) { 151 auto fromPoint0 = Point(rect_.left_ + options.pinchWidgetDeadZone_, rect_.GetCenterY(), rect_.displayId_); 152 auto toPoint0 = Point((fromPoint0.px_ + distanceX0), rect_.GetCenterY(), rect_.displayId_); 153 auto fromPoint1 = Point(rect_.right_ - options.pinchWidgetDeadZone_, rect_.GetCenterY(), rect_.displayId_); 154 auto toPoint1 = Point((fromPoint1.px_ - distanceX0), rect_.GetCenterY(), rect_.displayId_); 155 DecomposeComputeSwipe(pointer1, fromPoint0, toPoint0, TouchOp::SWIPE, options); 156 DecomposeComputeSwipe(pointer2, fromPoint1, toPoint1, TouchOp::SWIPE, options); 157 } 158 159 PointerMatrix pointer3(pointer1.GetFingers() + pointer2.GetFingers(), pointer1.GetSteps()); 160 for (uint32_t index = 0; index < pointer1.GetSize(); index++) { 161 pointer3.PushAction(pointer1.At(0, index)); 162 } 163 for (uint32_t index = 0; index < pointer2.GetSize(); index++) { 164 pointer3.PushAction(pointer2.At(0, index)); 165 } 166 recv = move(pointer3); 167 } 168 Decompose(PointerMatrix & recv,const UiOpArgs & options) const169 void MultiPointerAction::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 170 { 171 PointerMatrix matrix(pointers_.GetFingers(), pointers_.GetSteps() + 1); 172 constexpr int32_t flag = 0x10000; // set the low 16 bits of data as coordinates. 173 for (uint32_t finger = 0; finger < pointers_.GetFingers(); finger++) { 174 uint32_t timeOffsetMs = 0; 175 uint32_t intervalMs = 0; 176 constexpr uint32_t unitConversionConstant = 1000; 177 for (uint32_t step = 0; step < pointers_.GetSteps() - 1; step++) { 178 if (pointers_.At(finger, step + 1).point_.displayId_ != pointers_.At(finger, step).point_.displayId_) { 179 LOG_W("Cross-screen operation is not suypport."); 180 return; 181 } 182 auto displayId = pointers_.At(finger, step).point_.displayId_; 183 const int32_t pxTo = (pointers_.At(finger, step + 1).point_.px_) % flag; 184 const int32_t pxFrom = (pointers_.At(finger, step).point_.px_) % flag; 185 const int32_t distanceX = pxTo - pxFrom; 186 const int32_t pyTo = pointers_.At(finger, step + 1).point_.py_; 187 const int32_t pyFrom = pointers_.At(finger, step).point_.py_; 188 const int32_t distanceY = pyTo - pyFrom; 189 auto stayMs = (pointers_.At(finger, step).point_.px_) / flag; 190 const uint32_t distance = sqrt(distanceX * distanceX + distanceY * distanceY); 191 intervalMs = (distance * unitConversionConstant) / options.swipeVelocityPps_; 192 auto holdMs = (stayMs == 0) ? intervalMs : stayMs; 193 if (step == 0) { 194 matrix.PushAction(TouchEvent {ActionStage::DOWN, {pxFrom, pyFrom, displayId}, 0, holdMs}); 195 } else { 196 timeOffsetMs += intervalMs; 197 matrix.PushAction(TouchEvent {ActionStage::MOVE, {pxFrom, pyFrom, displayId}, 198 timeOffsetMs, holdMs}); 199 } 200 } 201 auto endPx = (pointers_.At(finger, pointers_.GetSteps() - 1).point_.px_) % flag; 202 auto endPy = pointers_.At(finger, pointers_.GetSteps() - 1).point_.py_; 203 auto displayId = pointers_.At(finger, pointers_.GetSteps() - 1).point_.displayId_; 204 auto endTime = (pointers_.At(finger, pointers_.GetSteps() - 1).point_.px_) / flag; 205 auto endStayTime = (endTime == 0) ? intervalMs : endTime; 206 matrix.PushAction(TouchEvent {ActionStage::MOVE, {endPx, endPy, displayId}, timeOffsetMs, endStayTime}); 207 matrix.PushAction(TouchEvent {ActionStage::UP, {endPx, endPy, displayId}, timeOffsetMs, intervalMs}); 208 } 209 recv = move(matrix); 210 } 211 Decompose(vector<TouchPadEvent> & recv,const UiOpArgs & options,const Point displaySize) const212 void TouchPadAction::Decompose(vector<TouchPadEvent> &recv, const UiOpArgs &options, 213 const Point displaySize) const 214 { 215 int32_t numTwo = 2; 216 int32_t displayCenterX = displaySize.px_ / numTwo; 217 int32_t displayCenterY = displaySize.py_ / numTwo; 218 int32_t pxFrom = displayCenterX; 219 int32_t pyFrom = displayCenterY; 220 int32_t pxTo = displayCenterX; 221 int32_t pyTo = displayCenterY; 222 switch (direction_) { 223 case TO_LEFT: 224 pxFrom += displayCenterX / numTwo; 225 pxTo -= displayCenterX / numTwo; 226 break; 227 case TO_RIGHT: 228 pxFrom -= displayCenterX / numTwo; 229 pxTo += displayCenterX / numTwo; 230 break; 231 case TO_UP: 232 pyFrom += displayCenterY / numTwo; 233 pyTo -= displayCenterY / numTwo; 234 break; 235 case TO_DOWN: 236 pyFrom -= displayCenterY / numTwo; 237 pyTo += displayCenterY / numTwo; 238 break; 239 default: 240 break; 241 } 242 const int32_t distanceX = pxTo - pxFrom; 243 const int32_t distanceY = pyTo - pyFrom; 244 const uint32_t distance = sqrt(distanceX * distanceX + distanceY * distanceY); 245 const uint32_t timeCostMs = (distance * 1000) / options.swipeVelocityPps_; 246 constexpr uint32_t intervalMs = 5; 247 uint32_t steps = timeCostMs / intervalMs; 248 recv.push_back(TouchPadEvent {ActionStage::DOWN, {pxFrom, pyFrom}, fingers_, intervalMs}); 249 float stepLengthX = static_cast<double>(distanceX) / static_cast<double>(steps); 250 float stepLengthY = static_cast<double>(distanceY) / static_cast<double>(steps); 251 for (uint32_t step = 1; step < steps; step++) { 252 const int32_t pointX = pxFrom + stepLengthX * step; 253 const int32_t pointY = pyFrom + stepLengthY * step; 254 recv.push_back(TouchPadEvent {ActionStage::MOVE, {pointX, pointY}, fingers_, intervalMs}); 255 } 256 if (stay_) { 257 uint32_t stayPointerTimes = 5; 258 for (uint32_t stayPointerTime = 0; stayPointerTime < stayPointerTimes; stayPointerTime++) { 259 recv.push_back(TouchPadEvent {ActionStage::MOVE, {pxTo, pyTo}, fingers_, 200}); 260 } 261 } 262 recv.push_back(TouchPadEvent {ActionStage::UP, {pxTo, pyTo}, fingers_, intervalMs}); 263 } 264 PointerMatrix()265 PointerMatrix::PointerMatrix() {}; 266 PointerMatrix(uint32_t fingersNum,uint32_t stepsNum)267 PointerMatrix::PointerMatrix(uint32_t fingersNum, uint32_t stepsNum) 268 { 269 this->fingerNum_ = fingersNum; 270 this->stepNum_ = stepsNum; 271 this->capacity_ = this->fingerNum_ * this->stepNum_; 272 this->size_ = 0; 273 this->data_ = std::make_unique<TouchEvent[]>(this->capacity_); 274 } 275 operator =(PointerMatrix && other)276 PointerMatrix& PointerMatrix::operator=(PointerMatrix&& other) 277 { 278 this->data_ = move(other.data_); 279 this->fingerNum_ = other.fingerNum_; 280 this->stepNum_ = other.stepNum_; 281 this->capacity_ = other.capacity_; 282 this->size_ = other.size_; 283 other.fingerNum_ = 0; 284 other.stepNum_ = 0; 285 other.capacity_ = 0; 286 other.size_ = 0; 287 return *this; 288 } 289 ~PointerMatrix()290 PointerMatrix::~PointerMatrix() {} 291 PushAction(const TouchEvent & ptr)292 void PointerMatrix::PushAction(const TouchEvent& ptr) 293 { 294 if (this->capacity_ == this->size_) { 295 return; 296 } 297 *(this->data_.get() + this->size_) = ptr; 298 this->size_++; 299 } 300 Empty() const301 bool PointerMatrix::Empty() const 302 { 303 if (this->size_ == 0) { 304 return true; 305 } 306 return false; 307 } 308 At(uint32_t fingerIndex,uint32_t stepIndex) const309 TouchEvent& PointerMatrix::At(uint32_t fingerIndex, uint32_t stepIndex) const 310 { 311 return *(this->data_.get() + (fingerIndex * this->stepNum_ + stepIndex)); 312 } 313 GetCapacity() const314 uint32_t PointerMatrix::GetCapacity() const 315 { 316 return this->capacity_; 317 } 318 GetSize() const319 uint32_t PointerMatrix::GetSize() const 320 { 321 return this->size_; 322 } 323 GetSteps() const324 uint32_t PointerMatrix::GetSteps() const 325 { 326 return this->stepNum_; 327 } 328 GetFingers() const329 uint32_t PointerMatrix::GetFingers() const 330 { 331 return this->fingerNum_; 332 } 333 SetToolType(const TouchToolType type)334 void PointerMatrix::SetToolType(const TouchToolType type) 335 { 336 touchToolType_ = type; 337 } 338 GetToolType() const339 TouchToolType PointerMatrix::GetToolType() const 340 { 341 return touchToolType_; 342 } 343 SetTouchPressure(const float pressure)344 void PointerMatrix::SetTouchPressure(const float pressure) 345 { 346 touchPressure_ = pressure; 347 } 348 GetTouchPressure() const349 float PointerMatrix::GetTouchPressure() const 350 { 351 return touchPressure_; 352 } 353 ConvertToPenEvents(PointerMatrix & recv) const354 void PointerMatrix::ConvertToPenEvents(PointerMatrix &recv) const 355 { 356 DCHECK(this->fingerNum_ == 1); 357 recv.SetToolType(TouchToolType::PEN); 358 constexpr uint32_t intervalMs = 5; 359 recv.PushAction(TouchEvent { ActionStage::PROXIMITY_IN, this->At(0, 0).point_, 0, intervalMs }); 360 for (uint32_t step = 0; step < stepNum_; step++) { 361 auto touchEvent = At(0, step); 362 recv.PushAction(touchEvent); 363 } 364 recv.PushAction(TouchEvent { ActionStage::PROXIMITY_OUT, this->At(0, this->GetSteps() - 1).point_, 0, 365 intervalMs }); 366 } 367 ConvertToMouseEvents(vector<MouseEvent> & recv) const368 void PointerMatrix::ConvertToMouseEvents(vector<MouseEvent> &recv) const 369 { 370 for (uint32_t finger = 0; finger < fingerNum_; finger++) { 371 for (uint32_t step = 0; step < stepNum_; step++) { 372 auto touchEvent = At(finger, step); 373 recv.push_back(MouseEvent {touchEvent.stage_, touchEvent.point_, MouseButton::BUTTON_LEFT, {}, 374 touchEvent.holdMs_}); 375 } 376 } 377 } 378 Decompose(std::vector<MouseEvent> & recv,const UiOpArgs & opt) const379 void MouseMoveTo::Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const 380 { 381 recv.push_back(MouseEvent {ActionStage::MOVE, point_, MouseButton::BUTTON_NONE, {}, 0}); 382 } 383 Decompose(std::vector<MouseEvent> & recv,const UiOpArgs & opt) const384 void MouseSwipe::Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const 385 { 386 DCHECK(type_ >= TouchOp::SWIPE && type_ <= TouchOp::DRAG); 387 PointerMatrix touchEvents; 388 DecomposeComputeSwipe(touchEvents, from_, to_, type_, opt); 389 touchEvents.ConvertToMouseEvents(recv); 390 if (type_ == TouchOp::SWIPE) { 391 recv.front().stage_ = ActionStage::MOVE; 392 recv.back().stage_ = ActionStage::MOVE; 393 for (size_t index = 0; index < recv.size(); index++) { 394 recv[index].button_ = MouseButton::BUTTON_NONE; 395 } 396 } 397 } 398 Decompose(std::vector<MouseEvent> & recv,const UiOpArgs & opt) const399 void MouseClick::Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const 400 { 401 DCHECK(type_ >= TouchOp::CLICK && type_ <= TouchOp::DOUBLE_CLICK_P); 402 PointerMatrix touchEvents; 403 switch (type_) { 404 case CLICK: 405 DecomposeClick(touchEvents, point_, opt); 406 break; 407 case LONG_CLICK: 408 DecomposeLongClick(touchEvents, point_, opt); 409 break; 410 case DOUBLE_CLICK_P: 411 DecomposeDoubleClick(touchEvents, point_, opt); 412 break; 413 default: 414 break; 415 } 416 touchEvents.ConvertToMouseEvents(recv); 417 for (size_t index = 0; index < recv.size(); index++) { 418 recv[index].button_ = button_; 419 } 420 vector<KeyEvent> keyAction1; 421 keyAction1.push_back(KeyEvent {ActionStage::DOWN, key1_, opt.keyHoldMs_}); 422 keyAction1.push_back(KeyEvent {ActionStage::DOWN, key2_, opt.keyHoldMs_}); 423 auto keyDown = MouseEvent {ActionStage::MOVE, point_, MouseButton::BUTTON_NONE, keyAction1, opt.clickHoldMs_}; 424 recv.insert(recv.begin(), keyDown); 425 426 vector<KeyEvent> keyAction2; 427 keyAction2.push_back(KeyEvent {ActionStage::UP, key1_, opt.keyHoldMs_}); 428 keyAction2.push_back(KeyEvent {ActionStage::UP, key2_, opt.keyHoldMs_}); 429 auto keyUp = MouseEvent {ActionStage::NONE, point_, MouseButton::BUTTON_NONE, keyAction2, 0}; 430 recv.push_back(keyUp); 431 } 432 Decompose(std::vector<MouseEvent> & recv,const UiOpArgs & opt) const433 void MouseScroll::Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const 434 { 435 recv.push_back(MouseEvent {ActionStage::MOVE, point_, MouseButton::BUTTON_NONE, {}, 0}); 436 constexpr int32_t thousandMilliseconds = 1000; 437 auto focusTimeMs = thousandMilliseconds / speed_ / 2; 438 auto stage = (scrollValue_ > 0) ? AXIS_DOWN : AXIS_UP; 439 vector<KeyEvent> keyAction1; 440 keyAction1.push_back(KeyEvent {ActionStage::DOWN, key1_, opt.keyHoldMs_}); 441 keyAction1.push_back(KeyEvent {ActionStage::DOWN, key2_, opt.keyHoldMs_}); 442 recv.push_back(MouseEvent {stage, point_, MouseButton::BUTTON_NONE, keyAction1, focusTimeMs}); 443 recv.push_back(MouseEvent {ActionStage::AXIS_STOP, point_, MouseButton::BUTTON_NONE, {}, focusTimeMs}); 444 445 auto steps = abs(scrollValue_); 446 for (auto index = 1; index < steps - 1; index++) { 447 recv.push_back(MouseEvent {stage, point_, MouseButton::BUTTON_NONE, {}, focusTimeMs}); 448 recv.push_back(MouseEvent {ActionStage::AXIS_STOP, point_, MouseButton::BUTTON_NONE, {}, focusTimeMs}); 449 } 450 451 vector<KeyEvent> keyAction2; 452 keyAction2.push_back(KeyEvent {ActionStage::UP, key1_, opt.keyHoldMs_}); 453 keyAction2.push_back(KeyEvent {ActionStage::UP, key2_, opt.keyHoldMs_}); 454 if (steps > 1) { 455 recv.push_back(MouseEvent {stage, point_, MouseButton::BUTTON_NONE, {}, focusTimeMs}); 456 recv.push_back(MouseEvent {ActionStage::AXIS_STOP, point_, MouseButton::BUTTON_NONE, keyAction2, 457 focusTimeMs}); 458 } else { 459 recv.push_back(MouseEvent {ActionStage::NONE, point_, MouseButton::BUTTON_NONE, keyAction2, focusTimeMs}); 460 } 461 } 462 Decompose(PointerMatrix & recv,const UiOpArgs & options) const463 void GenericAtomicAction::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 464 { 465 DCHECK(stage_ >= ActionStage::DOWN && stage_ <= ActionStage::UP); 466 constexpr uint32_t fingers = 1; 467 constexpr uint32_t steps = 1; 468 PointerMatrix pointer(fingers, steps); 469 pointer.PushAction(TouchEvent {stage_, point_, 0, 0, 0}); 470 recv = move(pointer); 471 } 472 } 473