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