• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "gestures/GestureConverter.h"
18 
19 #include <optional>
20 #include <sstream>
21 
22 #include <android-base/stringprintf.h>
23 #include <ftl/enum.h>
24 #include <linux/input-event-codes.h>
25 #include <log/log_main.h>
26 #include <ui/FloatRect.h>
27 
28 #include "TouchCursorInputMapperCommon.h"
29 #include "input/Input.h"
30 
31 namespace android {
32 
33 namespace {
34 
gesturesButtonToMotionEventButton(uint32_t gesturesButton)35 uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) {
36     switch (gesturesButton) {
37         case GESTURES_BUTTON_LEFT:
38             return AMOTION_EVENT_BUTTON_PRIMARY;
39         case GESTURES_BUTTON_MIDDLE:
40             return AMOTION_EVENT_BUTTON_TERTIARY;
41         case GESTURES_BUTTON_RIGHT:
42             return AMOTION_EVENT_BUTTON_SECONDARY;
43         case GESTURES_BUTTON_BACK:
44             return AMOTION_EVENT_BUTTON_BACK;
45         case GESTURES_BUTTON_FORWARD:
46             return AMOTION_EVENT_BUTTON_FORWARD;
47         default:
48             return 0;
49     }
50 }
51 
52 } // namespace
53 
GestureConverter(InputReaderContext & readerContext,const InputDeviceContext & deviceContext,int32_t deviceId)54 GestureConverter::GestureConverter(InputReaderContext& readerContext,
55                                    const InputDeviceContext& deviceContext, int32_t deviceId)
56       : mDeviceId(deviceId),
57         mReaderContext(readerContext),
58         mPointerController(readerContext.getPointerController(deviceId)) {
59     deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
60     deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
61 }
62 
dump() const63 std::string GestureConverter::dump() const {
64     std::stringstream out;
65     out << "Orientation: " << ftl::enum_string(mOrientation) << "\n";
66     out << "Axis info:\n";
67     out << "  X: " << mXAxisInfo << "\n";
68     out << "  Y: " << mYAxisInfo << "\n";
69     out << StringPrintf("Button state: 0x%08x\n", mButtonState);
70     out << "Down time: " << mDownTime << "\n";
71     out << "Current classification: " << ftl::enum_string(mCurrentClassification) << "\n";
72     return out.str();
73 }
74 
reset(nsecs_t when)75 std::list<NotifyArgs> GestureConverter::reset(nsecs_t when) {
76     std::list<NotifyArgs> out;
77     switch (mCurrentClassification) {
78         case MotionClassification::TWO_FINGER_SWIPE:
79             out.push_back(endScroll(when, when));
80             break;
81         case MotionClassification::MULTI_FINGER_SWIPE:
82             out += handleMultiFingerSwipeLift(when, when);
83             break;
84         case MotionClassification::PINCH:
85             out += endPinch(when, when);
86             break;
87         case MotionClassification::NONE:
88             // When a button is pressed, the Gestures library always ends the current gesture,
89             // so we don't have to worry about the case where buttons need to be lifted during a
90             // pinch or swipe.
91             if (mButtonState) {
92                 out += releaseAllButtons(when, when);
93             }
94             break;
95         default:
96             break;
97     }
98     mCurrentClassification = MotionClassification::NONE;
99     mDownTime = 0;
100     return out;
101 }
102 
populateMotionRanges(InputDeviceInfo & info) const103 void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const {
104     info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0.0f, 1.0f, 0, 0, 0);
105 
106     // TODO(b/259547750): set this using the raw axis ranges from the touchpad when pointer capture
107     // is enabled.
108     if (std::optional<FloatRect> rect = mPointerController->getBounds(); rect.has_value()) {
109         info.addMotionRange(AMOTION_EVENT_AXIS_X, SOURCE, rect->left, rect->right, 0, 0, 0);
110         info.addMotionRange(AMOTION_EVENT_AXIS_Y, SOURCE, rect->top, rect->bottom, 0, 0, 0);
111     }
112 
113     info.addMotionRange(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, SOURCE, -1.0f, 1.0f, 0, 0, 0);
114     info.addMotionRange(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, SOURCE, -1.0f, 1.0f, 0, 0, 0);
115 
116     // The other axes that can be reported don't have ranges that are easy to define. RELATIVE_X/Y
117     // and GESTURE_SCROLL_X/Y_DISTANCE are the result of acceleration functions being applied to
118     // finger movements, so their maximum values can't simply be derived from the size of the
119     // touchpad. GESTURE_PINCH_SCALE_FACTOR's maximum value depends on the minimum finger separation
120     // that the pad can report, which cannot be determined from its raw axis information. (Assuming
121     // a minimum finger separation of 1 unit would let us calculate a theoretical maximum, but it
122     // would be orders of magnitude too high, so probably not very useful.)
123 }
124 
handleGesture(nsecs_t when,nsecs_t readTime,const Gesture & gesture)125 std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t readTime,
126                                                       const Gesture& gesture) {
127     switch (gesture.type) {
128         case kGestureTypeMove:
129             return {handleMove(when, readTime, gesture)};
130         case kGestureTypeButtonsChange:
131             return handleButtonsChange(when, readTime, gesture);
132         case kGestureTypeScroll:
133             return handleScroll(when, readTime, gesture);
134         case kGestureTypeFling:
135             return handleFling(when, readTime, gesture);
136         case kGestureTypeSwipe:
137             return handleMultiFingerSwipe(when, readTime, 3, gesture.details.swipe.dx,
138                                           gesture.details.swipe.dy);
139         case kGestureTypeFourFingerSwipe:
140             return handleMultiFingerSwipe(when, readTime, 4, gesture.details.four_finger_swipe.dx,
141                                           gesture.details.four_finger_swipe.dy);
142         case kGestureTypeSwipeLift:
143         case kGestureTypeFourFingerSwipeLift:
144             return handleMultiFingerSwipeLift(when, readTime);
145         case kGestureTypePinch:
146             return handlePinch(when, readTime, gesture);
147         default:
148             return {};
149     }
150 }
151 
handleMove(nsecs_t when,nsecs_t readTime,const Gesture & gesture)152 NotifyMotionArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime,
153                                               const Gesture& gesture) {
154     float deltaX = gesture.details.move.dx;
155     float deltaY = gesture.details.move.dy;
156     if (std::abs(deltaX) > 0 || std::abs(deltaY) > 0) {
157         enableTapToClick();
158     }
159     rotateDelta(mOrientation, &deltaX, &deltaY);
160 
161     mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
162     mPointerController->move(deltaX, deltaY);
163     mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
164 
165     const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
166 
167     PointerCoords coords;
168     coords.clear();
169     coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
170     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
171     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
172     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
173     const bool down = isPointerDown(mButtonState);
174     coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
175 
176     const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
177     return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState,
178                           /* pointerCount= */ 1, mFingerProps.data(), &coords, xCursorPosition,
179                           yCursorPosition);
180 }
181 
handleButtonsChange(nsecs_t when,nsecs_t readTime,const Gesture & gesture)182 std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime,
183                                                             const Gesture& gesture) {
184     std::list<NotifyArgs> out = {};
185 
186     mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
187     mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
188 
189     const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
190 
191     PointerCoords coords;
192     coords.clear();
193     coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
194     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
195     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
196     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
197 
198     if (mReaderContext.isPreventingTouchpadTaps()) {
199         enableTapToClick();
200         if (gesture.details.buttons.is_tap) {
201             // return early to prevent this tap
202             return out;
203         }
204     }
205 
206     const uint32_t buttonsPressed = gesture.details.buttons.down;
207     bool pointerDown = isPointerDown(mButtonState) ||
208             buttonsPressed &
209                     (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT);
210     coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
211 
212     uint32_t newButtonState = mButtonState;
213     std::list<NotifyArgs> pressEvents = {};
214     for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
215         if (buttonsPressed & button) {
216             uint32_t actionButton = gesturesButtonToMotionEventButton(button);
217             newButtonState |= actionButton;
218             pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
219                                                  actionButton, newButtonState,
220                                                  /* pointerCount= */ 1, mFingerProps.data(),
221                                                  &coords, xCursorPosition, yCursorPosition));
222         }
223     }
224     if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
225         mDownTime = when;
226         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
227                                      /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
228                                      mFingerProps.data(), &coords, xCursorPosition,
229                                      yCursorPosition));
230     }
231     out.splice(out.end(), pressEvents);
232 
233     // The same button may be in both down and up in the same gesture, in which case we should treat
234     // it as having gone down and then up. So, we treat a single button change gesture as two state
235     // changes: a set of buttons going down, followed by a set of buttons going up.
236     mButtonState = newButtonState;
237 
238     const uint32_t buttonsReleased = gesture.details.buttons.up;
239     for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
240         if (buttonsReleased & button) {
241             uint32_t actionButton = gesturesButtonToMotionEventButton(button);
242             newButtonState &= ~actionButton;
243             out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
244                                          actionButton, newButtonState, /* pointerCount= */ 1,
245                                          mFingerProps.data(), &coords, xCursorPosition,
246                                          yCursorPosition));
247         }
248     }
249     if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
250         coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
251         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
252                                      newButtonState, /* pointerCount= */ 1, mFingerProps.data(),
253                                      &coords, xCursorPosition, yCursorPosition));
254         // Send a HOVER_MOVE to tell the application that the mouse is hovering again.
255         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_HOVER_MOVE,
256                                      /*actionButton=*/0, newButtonState, /*pointerCount=*/1,
257                                      mFingerProps.data(), &coords, xCursorPosition,
258                                      yCursorPosition));
259     }
260     mButtonState = newButtonState;
261     return out;
262 }
263 
releaseAllButtons(nsecs_t when,nsecs_t readTime)264 std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t readTime) {
265     std::list<NotifyArgs> out;
266     const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
267 
268     PointerCoords coords;
269     coords.clear();
270     coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
271     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
272     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
273     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
274     const bool pointerDown = isPointerDown(mButtonState);
275     coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
276     uint32_t newButtonState = mButtonState;
277     for (uint32_t button = AMOTION_EVENT_BUTTON_PRIMARY; button <= AMOTION_EVENT_BUTTON_FORWARD;
278          button <<= 1) {
279         if (mButtonState & button) {
280             newButtonState &= ~button;
281             out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
282                                          button, newButtonState, /*pointerCount=*/1,
283                                          mFingerProps.data(), &coords, xCursorPosition,
284                                          yCursorPosition));
285         }
286     }
287     if (pointerDown) {
288         coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
289         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
290                                      newButtonState, /*pointerCount=*/1, mFingerProps.data(),
291                                      &coords, xCursorPosition, yCursorPosition));
292     }
293     mButtonState = 0;
294     return out;
295 }
296 
handleScroll(nsecs_t when,nsecs_t readTime,const Gesture & gesture)297 std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readTime,
298                                                      const Gesture& gesture) {
299     std::list<NotifyArgs> out;
300     PointerCoords& coords = mFakeFingerCoords[0];
301     const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
302     if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) {
303         mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
304         coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
305         coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
306         coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
307         mDownTime = when;
308         NotifyMotionArgs args =
309                 makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0,
310                                mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
311                                mFakeFingerCoords.data(), xCursorPosition, yCursorPosition);
312         args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
313         out.push_back(args);
314     }
315     float deltaX = gesture.details.scroll.dx;
316     float deltaY = gesture.details.scroll.dy;
317     rotateDelta(mOrientation, &deltaX, &deltaY);
318 
319     coords.setAxisValue(AMOTION_EVENT_AXIS_X, coords.getAxisValue(AMOTION_EVENT_AXIS_X) + deltaX);
320     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y) + deltaY);
321     // TODO(b/262876643): set AXIS_GESTURE_{X,Y}_OFFSET.
322     coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, -gesture.details.scroll.dx);
323     coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, -gesture.details.scroll.dy);
324     NotifyMotionArgs args =
325             makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
326                            mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
327                            mFakeFingerCoords.data(), xCursorPosition, yCursorPosition);
328     args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
329     out.push_back(args);
330     return out;
331 }
332 
handleFling(nsecs_t when,nsecs_t readTime,const Gesture & gesture)333 std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTime,
334                                                     const Gesture& gesture) {
335     switch (gesture.details.fling.fling_state) {
336         case GESTURES_FLING_START:
337             if (mCurrentClassification == MotionClassification::TWO_FINGER_SWIPE) {
338                 // We don't actually want to use the gestures library's fling velocity values (to
339                 // ensure consistency between touchscreen and touchpad flings), so we're just using
340                 // the "start fling" gestures as a marker for the end of a two-finger scroll
341                 // gesture.
342                 return {endScroll(when, readTime)};
343             }
344             break;
345         case GESTURES_FLING_TAP_DOWN:
346             if (mCurrentClassification == MotionClassification::NONE) {
347                 // Use the tap down state of a fling gesture as an indicator that a contact
348                 // has been initiated with the touchpad. We treat this as a move event with zero
349                 // magnitude, which will also result in the pointer icon being updated.
350                 // TODO(b/282023644): Add a signal in libgestures for when a stable contact has been
351                 //  initiated with a touchpad.
352                 if (!mReaderContext.isPreventingTouchpadTaps()) {
353                     enableTapToClick();
354                 }
355                 return {handleMove(when, readTime,
356                                    Gesture(kGestureMove, gesture.start_time, gesture.end_time,
357                                            /*dx=*/0.f,
358                                            /*dy=*/0.f))};
359             }
360             break;
361         default:
362             break;
363     }
364 
365     return {};
366 }
367 
endScroll(nsecs_t when,nsecs_t readTime)368 NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
369     const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
370     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0);
371     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0);
372     NotifyMotionArgs args =
373             makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
374                            mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
375                            mFakeFingerCoords.data(), xCursorPosition, yCursorPosition);
376     args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
377     mCurrentClassification = MotionClassification::NONE;
378     return args;
379 }
380 
handleMultiFingerSwipe(nsecs_t when,nsecs_t readTime,uint32_t fingerCount,float dx,float dy)381 [[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipe(nsecs_t when,
382                                                                              nsecs_t readTime,
383                                                                              uint32_t fingerCount,
384                                                                              float dx, float dy) {
385     std::list<NotifyArgs> out = {};
386 
387     const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
388     if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
389         // If the user changes the number of fingers mid-way through a swipe (e.g. they start with
390         // three and then put a fourth finger down), the gesture library will treat it as two
391         // separate swipes with an appropriate lift event between them, so we don't have to worry
392         // about the finger count changing mid-swipe.
393         mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE;
394         mSwipeFingerCount = fingerCount;
395 
396         constexpr float FAKE_FINGER_SPACING = 100;
397         float xCoord = xCursorPosition - FAKE_FINGER_SPACING * (mSwipeFingerCount - 1) / 2;
398         for (size_t i = 0; i < mSwipeFingerCount; i++) {
399             PointerCoords& coords = mFakeFingerCoords[i];
400             coords.clear();
401             coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCoord);
402             coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
403             coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
404             xCoord += FAKE_FINGER_SPACING;
405         }
406 
407         mDownTime = when;
408         mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT,
409                                           fingerCount);
410         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
411                                      /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
412                                      mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
413                                      yCursorPosition));
414         for (size_t i = 1; i < mSwipeFingerCount; i++) {
415             out.push_back(makeMotionArgs(when, readTime,
416                                          AMOTION_EVENT_ACTION_POINTER_DOWN |
417                                                  (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
418                                          /* actionButton= */ 0, mButtonState,
419                                          /* pointerCount= */ i + 1, mFingerProps.data(),
420                                          mFakeFingerCoords.data(), xCursorPosition,
421                                          yCursorPosition));
422         }
423     }
424     float rotatedDeltaX = dx, rotatedDeltaY = -dy;
425     rotateDelta(mOrientation, &rotatedDeltaX, &rotatedDeltaY);
426     for (size_t i = 0; i < mSwipeFingerCount; i++) {
427         PointerCoords& coords = mFakeFingerCoords[i];
428         coords.setAxisValue(AMOTION_EVENT_AXIS_X,
429                             coords.getAxisValue(AMOTION_EVENT_AXIS_X) + rotatedDeltaX);
430         coords.setAxisValue(AMOTION_EVENT_AXIS_Y,
431                             coords.getAxisValue(AMOTION_EVENT_AXIS_Y) + rotatedDeltaY);
432     }
433     float xOffset = dx / (mXAxisInfo.maxValue - mXAxisInfo.minValue);
434     float yOffset = -dy / (mYAxisInfo.maxValue - mYAxisInfo.minValue);
435     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset);
436     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
437     out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
438                                  mButtonState, /* pointerCount= */ mSwipeFingerCount,
439                                  mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
440                                  yCursorPosition));
441     return out;
442 }
443 
handleMultiFingerSwipeLift(nsecs_t when,nsecs_t readTime)444 [[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipeLift(nsecs_t when,
445                                                                                  nsecs_t readTime) {
446     std::list<NotifyArgs> out = {};
447     if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
448         return out;
449     }
450     const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
451     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0);
452     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0);
453 
454     for (size_t i = mSwipeFingerCount; i > 1; i--) {
455         out.push_back(makeMotionArgs(when, readTime,
456                                      AMOTION_EVENT_ACTION_POINTER_UP |
457                                              ((i - 1) << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
458                                      /* actionButton= */ 0, mButtonState, /* pointerCount= */ i,
459                                      mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
460                                      yCursorPosition));
461     }
462     out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP,
463                                  /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
464                                  mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
465                                  yCursorPosition));
466     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT, 0);
467     mCurrentClassification = MotionClassification::NONE;
468     mSwipeFingerCount = 0;
469     return out;
470 }
471 
handlePinch(nsecs_t when,nsecs_t readTime,const Gesture & gesture)472 [[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime,
473                                                                   const Gesture& gesture) {
474     const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
475 
476     // Pinch gesture phases are reported a little differently from others, in that the same details
477     // struct is used for all phases of the gesture, just with different zoom_state values. When
478     // zoom_state is START or END, dz will always be 1, so we don't need to move the pointers in
479     // those cases.
480 
481     if (mCurrentClassification != MotionClassification::PINCH) {
482         LOG_ALWAYS_FATAL_IF(gesture.details.pinch.zoom_state != GESTURES_ZOOM_START,
483                             "First pinch gesture does not have the START zoom state (%d instead).",
484                             gesture.details.pinch.zoom_state);
485         mCurrentClassification = MotionClassification::PINCH;
486         mPinchFingerSeparation = INITIAL_PINCH_SEPARATION_PX;
487         mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
488         mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
489                                           xCursorPosition - mPinchFingerSeparation / 2);
490         mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
491         mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
492         mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X,
493                                           xCursorPosition + mPinchFingerSeparation / 2);
494         mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
495         mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
496         mDownTime = when;
497         std::list<NotifyArgs> out;
498         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
499                                      /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
500                                      mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
501                                      yCursorPosition));
502         out.push_back(makeMotionArgs(when, readTime,
503                                      AMOTION_EVENT_ACTION_POINTER_DOWN |
504                                              1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
505                                      /* actionButton= */ 0, mButtonState, /* pointerCount= */ 2,
506                                      mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
507                                      yCursorPosition));
508         return out;
509     }
510 
511     if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) {
512         return endPinch(when, readTime);
513     }
514 
515     mPinchFingerSeparation *= gesture.details.pinch.dz;
516     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR,
517                                       gesture.details.pinch.dz);
518     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
519                                       xCursorPosition - mPinchFingerSeparation / 2);
520     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
521     mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X,
522                                       xCursorPosition + mPinchFingerSeparation / 2);
523     mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
524     return {makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0,
525                            mButtonState, /*pointerCount=*/2, mFingerProps.data(),
526                            mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)};
527 }
528 
endPinch(nsecs_t when,nsecs_t readTime)529 std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) {
530     std::list<NotifyArgs> out;
531     const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
532 
533     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
534     out.push_back(makeMotionArgs(when, readTime,
535                                  AMOTION_EVENT_ACTION_POINTER_UP |
536                                          1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
537                                  /*actionButton=*/0, mButtonState, /*pointerCount=*/2,
538                                  mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
539                                  yCursorPosition));
540     out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
541                                  mButtonState, /*pointerCount=*/1, mFingerProps.data(),
542                                  mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
543     mCurrentClassification = MotionClassification::NONE;
544     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0);
545     return out;
546 }
547 
makeMotionArgs(nsecs_t when,nsecs_t readTime,int32_t action,int32_t actionButton,int32_t buttonState,uint32_t pointerCount,const PointerProperties * pointerProperties,const PointerCoords * pointerCoords,float xCursorPosition,float yCursorPosition)548 NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
549                                                   int32_t actionButton, int32_t buttonState,
550                                                   uint32_t pointerCount,
551                                                   const PointerProperties* pointerProperties,
552                                                   const PointerCoords* pointerCoords,
553                                                   float xCursorPosition, float yCursorPosition) {
554     return {mReaderContext.getNextId(),
555             when,
556             readTime,
557             mDeviceId,
558             SOURCE,
559             mPointerController->getDisplayId(),
560             /* policyFlags= */ POLICY_FLAG_WAKE,
561             action,
562             /* actionButton= */ actionButton,
563             /* flags= */ action == AMOTION_EVENT_ACTION_CANCEL ? AMOTION_EVENT_FLAG_CANCELED : 0,
564             mReaderContext.getGlobalMetaState(),
565             buttonState,
566             mCurrentClassification,
567             AMOTION_EVENT_EDGE_FLAG_NONE,
568             pointerCount,
569             pointerProperties,
570             pointerCoords,
571             /* xPrecision= */ 1.0f,
572             /* yPrecision= */ 1.0f,
573             xCursorPosition,
574             yCursorPosition,
575             /* downTime= */ mDownTime,
576             /* videoFrames= */ {}};
577 }
578 
enableTapToClick()579 void GestureConverter::enableTapToClick() {
580     mReaderContext.setPreventingTouchpadTaps(false);
581 }
582 
583 } // namespace android
584