• 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 <com_android_input_flags.h>
24 #include <ftl/enum.h>
25 #include <linux/input-event-codes.h>
26 #include <log/log_main.h>
27 #include <ui/FloatRect.h>
28 
29 #include "TouchCursorInputMapperCommon.h"
30 #include "input/Input.h"
31 
32 namespace input_flags = com::android::input::flags;
33 
34 namespace android {
35 
36 namespace {
37 
38 // This will disable the tap to click while the user is typing on a physical keyboard
39 const bool ENABLE_TOUCHPAD_PALM_REJECTION = input_flags::enable_touchpad_typing_palm_rejection();
40 
41 // In addition to v1, v2 will also cancel ongoing move gestures while typing and add delay in
42 // re-enabling the tap to click.
43 const bool ENABLE_TOUCHPAD_PALM_REJECTION_V2 =
44         input_flags::enable_v2_touchpad_typing_palm_rejection();
45 
gesturesButtonToMotionEventButton(uint32_t gesturesButton)46 uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) {
47     switch (gesturesButton) {
48         case GESTURES_BUTTON_LEFT:
49             return AMOTION_EVENT_BUTTON_PRIMARY;
50         case GESTURES_BUTTON_MIDDLE:
51             return AMOTION_EVENT_BUTTON_TERTIARY;
52         case GESTURES_BUTTON_RIGHT:
53             return AMOTION_EVENT_BUTTON_SECONDARY;
54         case GESTURES_BUTTON_BACK:
55             return AMOTION_EVENT_BUTTON_BACK;
56         case GESTURES_BUTTON_FORWARD:
57             return AMOTION_EVENT_BUTTON_FORWARD;
58         default:
59             return 0;
60     }
61 }
62 
63 } // namespace
64 
GestureConverter(InputReaderContext & readerContext,const InputDeviceContext & deviceContext,int32_t deviceId)65 GestureConverter::GestureConverter(InputReaderContext& readerContext,
66                                    const InputDeviceContext& deviceContext, int32_t deviceId)
67       : mDeviceId(deviceId),
68         mReaderContext(readerContext),
69         mEnableFlingStop(input_flags::enable_touchpad_fling_stop()) {
70     deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
71     deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
72 }
73 
dump() const74 std::string GestureConverter::dump() const {
75     std::stringstream out;
76     out << "Orientation: " << ftl::enum_string(mOrientation) << "\n";
77     out << "Axis info:\n";
78     out << "  X: " << mXAxisInfo << "\n";
79     out << "  Y: " << mYAxisInfo << "\n";
80     out << StringPrintf("Button state: 0x%08x\n", mButtonState);
81     out << "Down time: " << mDownTime << "\n";
82     out << "Current classification: " << ftl::enum_string(mCurrentClassification) << "\n";
83     out << "Is hovering: " << mIsHovering << "\n";
84     out << "Enable Tap Timestamp: " << mWhenToEnableTapToClick << "\n";
85     return out.str();
86 }
87 
reset(nsecs_t when)88 std::list<NotifyArgs> GestureConverter::reset(nsecs_t when) {
89     std::list<NotifyArgs> out;
90     switch (mCurrentClassification) {
91         case MotionClassification::TWO_FINGER_SWIPE:
92             out += endScroll(when, when);
93             break;
94         case MotionClassification::MULTI_FINGER_SWIPE:
95             out += handleMultiFingerSwipeLift(when, when);
96             break;
97         case MotionClassification::PINCH:
98             out += endPinch(when, when);
99             break;
100         case MotionClassification::NONE:
101             // When a button is pressed, the Gestures library always ends the current gesture,
102             // so we don't have to worry about the case where buttons need to be lifted during a
103             // pinch or swipe.
104             if (mButtonState) {
105                 out += releaseAllButtons(when, when);
106             }
107             break;
108         default:
109             break;
110     }
111     mCurrentClassification = MotionClassification::NONE;
112     mDownTime = 0;
113     return out;
114 }
115 
populateMotionRanges(InputDeviceInfo & info) const116 void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const {
117     info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0.0f, 1.0f, 0, 0, 0);
118 
119     if (!mBoundsInLogicalDisplay.isEmpty()) {
120         info.addMotionRange(AMOTION_EVENT_AXIS_X, SOURCE, mBoundsInLogicalDisplay.left,
121                             mBoundsInLogicalDisplay.right, 0, 0, 0);
122         info.addMotionRange(AMOTION_EVENT_AXIS_Y, SOURCE, mBoundsInLogicalDisplay.top,
123                             mBoundsInLogicalDisplay.bottom, 0, 0, 0);
124     }
125 
126     info.addMotionRange(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, SOURCE, -1.0f, 1.0f, 0, 0, 0);
127     info.addMotionRange(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, SOURCE, -1.0f, 1.0f, 0, 0, 0);
128 
129     // The other axes that can be reported don't have ranges that are easy to define. RELATIVE_X/Y
130     // and GESTURE_SCROLL_X/Y_DISTANCE are the result of acceleration functions being applied to
131     // finger movements, so their maximum values can't simply be derived from the size of the
132     // touchpad. GESTURE_PINCH_SCALE_FACTOR's maximum value depends on the minimum finger separation
133     // that the pad can report, which cannot be determined from its raw axis information. (Assuming
134     // a minimum finger separation of 1 unit would let us calculate a theoretical maximum, but it
135     // would be orders of magnitude too high, so probably not very useful.)
136 }
137 
handleGesture(nsecs_t when,nsecs_t readTime,nsecs_t gestureStartTime,const Gesture & gesture)138 std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t readTime,
139                                                       nsecs_t gestureStartTime,
140                                                       const Gesture& gesture) {
141     if (!mDisplayId) {
142         // Ignore gestures when there is no target display configured.
143         return {};
144     }
145 
146     switch (gesture.type) {
147         case kGestureTypeMove:
148             return handleMove(when, readTime, gestureStartTime, gesture);
149         case kGestureTypeButtonsChange:
150             return handleButtonsChange(when, readTime, gesture);
151         case kGestureTypeScroll:
152             return handleScroll(when, readTime, gesture);
153         case kGestureTypeFling:
154             return handleFling(when, readTime, gestureStartTime, gesture);
155         case kGestureTypeSwipe:
156             return handleMultiFingerSwipe(when, readTime, 3, gesture.details.swipe.dx,
157                                           gesture.details.swipe.dy);
158         case kGestureTypeFourFingerSwipe:
159             return handleMultiFingerSwipe(when, readTime, 4, gesture.details.four_finger_swipe.dx,
160                                           gesture.details.four_finger_swipe.dy);
161         case kGestureTypeSwipeLift:
162         case kGestureTypeFourFingerSwipeLift:
163             return handleMultiFingerSwipeLift(when, readTime);
164         case kGestureTypePinch:
165             return handlePinch(when, readTime, gesture);
166         default:
167             return {};
168     }
169 }
170 
handleMove(nsecs_t when,nsecs_t readTime,nsecs_t gestureStartTime,const Gesture & gesture)171 std::list<NotifyArgs> GestureConverter::handleMove(nsecs_t when, nsecs_t readTime,
172                                                    nsecs_t gestureStartTime,
173                                                    const Gesture& gesture) {
174     float deltaX = gesture.details.move.dx;
175     float deltaY = gesture.details.move.dy;
176     if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) {
177         bool wasHoverCancelled = mIsHoverCancelled;
178         // Gesture will be cancelled if it started before the user started typing and
179         // there is a active IME connection.
180         mIsHoverCancelled = gestureStartTime <= mReaderContext.getLastKeyDownTimestamp() &&
181                 mReaderContext.getPolicy()->isInputMethodConnectionActive();
182 
183         if (!wasHoverCancelled && mIsHoverCancelled) {
184             // This is the first event of the cancelled gesture, we won't return because we need to
185             // generate a HOVER_EXIT event
186             return exitHover(when, readTime);
187         } else if (mIsHoverCancelled) {
188             return {};
189         }
190     }
191 
192     rotateDelta(mOrientation, &deltaX, &deltaY);
193 
194     // Update the cursor, and enable tap to click if the gesture is not cancelled
195     if (!mIsHoverCancelled) {
196         // handleFling calls hoverMove with zero delta on FLING_TAP_DOWN. Don't enable tap to click
197         // for this case as subsequent handleButtonsChange may choose to ignore this tap.
198         if ((ENABLE_TOUCHPAD_PALM_REJECTION || ENABLE_TOUCHPAD_PALM_REJECTION_V2) &&
199             (std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) {
200             enableTapToClick(when);
201         }
202     }
203 
204     std::list<NotifyArgs> out;
205     const bool down = isPointerDown(mButtonState);
206     if (!down) {
207         out += enterHover(when, readTime);
208     }
209 
210     PointerCoords coords;
211     coords.clear();
212     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
213     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
214     coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
215 
216     const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
217     out.push_back(makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState,
218                                  /*pointerCount=*/1, &coords));
219     return out;
220 }
221 
handleButtonsChange(nsecs_t when,nsecs_t readTime,const Gesture & gesture)222 std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime,
223                                                             const Gesture& gesture) {
224     std::list<NotifyArgs> out = {};
225 
226     PointerCoords coords;
227     coords.clear();
228     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
229     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
230 
231     // V2 palm rejection should override V1
232     if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) {
233         enableTapToClick(when);
234         if (gesture.details.buttons.is_tap && when <= mWhenToEnableTapToClick) {
235             // return early to prevent this tap
236             return out;
237         }
238     } else if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) {
239         enableTapToClick(when);
240         if (gesture.details.buttons.is_tap) {
241             // return early to prevent this tap
242             return out;
243         }
244     }
245 
246     const uint32_t buttonsPressed = gesture.details.buttons.down;
247     bool pointerDown = isPointerDown(mButtonState) ||
248             buttonsPressed &
249                     (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT);
250     coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
251 
252     uint32_t newButtonState = mButtonState;
253     std::list<NotifyArgs> pressEvents = {};
254     for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
255         if (buttonsPressed & button) {
256             uint32_t actionButton = gesturesButtonToMotionEventButton(button);
257             newButtonState |= actionButton;
258             pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
259                                                  actionButton, newButtonState,
260                                                  /*pointerCount=*/1, &coords));
261         }
262     }
263     if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
264         mDownTime = when;
265         out += exitHover(when, readTime);
266         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
267                                      /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
268                                      &coords));
269     }
270     out.splice(out.end(), pressEvents);
271 
272     // The same button may be in both down and up in the same gesture, in which case we should treat
273     // it as having gone down and then up. So, we treat a single button change gesture as two state
274     // changes: a set of buttons going down, followed by a set of buttons going up.
275     mButtonState = newButtonState;
276 
277     const uint32_t buttonsReleased = gesture.details.buttons.up;
278     for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
279         if (buttonsReleased & button) {
280             uint32_t actionButton = gesturesButtonToMotionEventButton(button);
281             newButtonState &= ~actionButton;
282             out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
283                                          actionButton, newButtonState, /* pointerCount= */ 1,
284                                          &coords));
285         }
286     }
287     if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
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, &coords));
291         mButtonState = newButtonState;
292         out += enterHover(when, readTime);
293     }
294     mButtonState = newButtonState;
295     return out;
296 }
297 
releaseAllButtons(nsecs_t when,nsecs_t readTime)298 std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t readTime) {
299     std::list<NotifyArgs> out;
300 
301     PointerCoords coords;
302     coords.clear();
303     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
304     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
305     const bool pointerDown = isPointerDown(mButtonState);
306     coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
307     uint32_t newButtonState = mButtonState;
308     for (uint32_t button = AMOTION_EVENT_BUTTON_PRIMARY; button <= AMOTION_EVENT_BUTTON_FORWARD;
309          button <<= 1) {
310         if (mButtonState & button) {
311             newButtonState &= ~button;
312             out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
313                                          button, newButtonState, /*pointerCount=*/1, &coords));
314         }
315     }
316     mButtonState = 0;
317     if (pointerDown) {
318         coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
319         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
320                                      mButtonState, /*pointerCount=*/1, &coords));
321         out += enterHover(when, readTime);
322     }
323     return out;
324 }
325 
handleScroll(nsecs_t when,nsecs_t readTime,const Gesture & gesture)326 std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readTime,
327                                                      const Gesture& gesture) {
328     std::list<NotifyArgs> out;
329     PointerCoords& coords = mFakeFingerCoords[0];
330     if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) {
331         out += exitHover(when, readTime);
332 
333         mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
334         coords.clear();
335         coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
336         mDownTime = when;
337         NotifyMotionArgs args =
338                 makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0,
339                                mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
340         args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
341         out.push_back(args);
342     }
343     float deltaX = gesture.details.scroll.dx;
344     float deltaY = gesture.details.scroll.dy;
345     rotateDelta(mOrientation, &deltaX, &deltaY);
346 
347     coords.setAxisValue(AMOTION_EVENT_AXIS_X, coords.getAxisValue(AMOTION_EVENT_AXIS_X) + deltaX);
348     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y) + deltaY);
349     // TODO(b/262876643): set AXIS_GESTURE_{X,Y}_OFFSET.
350     coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, -gesture.details.scroll.dx);
351     coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, -gesture.details.scroll.dy);
352     NotifyMotionArgs args =
353             makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
354                            mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
355     args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
356     out.push_back(args);
357     return out;
358 }
359 
handleFling(nsecs_t when,nsecs_t readTime,nsecs_t gestureStartTime,const Gesture & gesture)360 std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTime,
361                                                     nsecs_t gestureStartTime,
362                                                     const Gesture& gesture) {
363     switch (gesture.details.fling.fling_state) {
364         case GESTURES_FLING_START:
365             if (mCurrentClassification == MotionClassification::TWO_FINGER_SWIPE) {
366                 // We don't actually want to use the gestures library's fling velocity values (to
367                 // ensure consistency between touchscreen and touchpad flings), so we're just using
368                 // the "start fling" gestures as a marker for the end of a two-finger scroll
369                 // gesture.
370                 mFlingMayBeInProgress = true;
371                 return endScroll(when, readTime);
372             }
373             break;
374         case GESTURES_FLING_TAP_DOWN:
375             if (mCurrentClassification == MotionClassification::NONE) {
376                 if (mEnableFlingStop && mFlingMayBeInProgress) {
377                     // The user has just touched the pad again after ending a two-finger scroll
378                     // motion, which might have started a fling. We want to stop the fling, but
379                     // unfortunately there's currently no API for doing so. Instead, send and
380                     // immediately cancel a fake finger to cause the scrolling to stop and hopefully
381                     // avoid side effects (e.g. activation of UI elements).
382                     // TODO(b/326056750): add an API for fling stops.
383                     mFlingMayBeInProgress = false;
384                     PointerCoords coords;
385                     coords.clear();
386                     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
387                     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
388 
389                     std::list<NotifyArgs> out;
390                     mDownTime = when;
391                     mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
392                     out += exitHover(when, readTime);
393                     out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
394                                                  /*actionButton=*/0, /*buttonState=*/0,
395                                                  /*pointerCount=*/1, &coords));
396                     out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_CANCEL,
397                                                  /*actionButton=*/0, /*buttonState=*/0,
398                                                  /*pointerCount=*/1, &coords));
399                     out += enterHover(when, readTime);
400                     mCurrentClassification = MotionClassification::NONE;
401                     return out;
402                 } else {
403                     // Use the tap down state of a fling gesture as an indicator that a contact
404                     // has been initiated with the touchpad. We treat this as a move event with zero
405                     // magnitude, which will also result in the pointer icon being updated.
406                     // TODO(b/282023644): Add a signal in libgestures for when a stable contact has
407                     //  been initiated with a touchpad.
408                     return handleMove(when, readTime, gestureStartTime,
409                                       Gesture(kGestureMove, gesture.start_time, gesture.end_time,
410                                               /*dx=*/0.f,
411                                               /*dy=*/0.f));
412                 }
413             }
414             break;
415         default:
416             break;
417     }
418 
419     return {};
420 }
421 
endScroll(nsecs_t when,nsecs_t readTime)422 std::list<NotifyArgs> GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
423     std::list<NotifyArgs> out;
424     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0);
425     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0);
426     NotifyMotionArgs args =
427             makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
428                            mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
429     args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
430     out.push_back(args);
431     mCurrentClassification = MotionClassification::NONE;
432     out += enterHover(when, readTime);
433     return out;
434 }
435 
handleMultiFingerSwipe(nsecs_t when,nsecs_t readTime,uint32_t fingerCount,float dx,float dy)436 [[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipe(nsecs_t when,
437                                                                              nsecs_t readTime,
438                                                                              uint32_t fingerCount,
439                                                                              float dx, float dy) {
440     std::list<NotifyArgs> out = {};
441 
442     if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
443         // If the user changes the number of fingers mid-way through a swipe (e.g. they start with
444         // three and then put a fourth finger down), the gesture library will treat it as two
445         // separate swipes with an appropriate lift event between them, so we don't have to worry
446         // about the finger count changing mid-swipe.
447 
448         out += exitHover(when, readTime);
449 
450         mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE;
451 
452         mSwipeFingerCount = fingerCount;
453 
454         constexpr float FAKE_FINGER_SPACING = 100;
455         float xCoord = 0.f - FAKE_FINGER_SPACING * (mSwipeFingerCount - 1) / 2;
456         for (size_t i = 0; i < mSwipeFingerCount; i++) {
457             PointerCoords& coords = mFakeFingerCoords[i];
458             coords.clear();
459             // PointerChoreographer will add the cursor position to these pointers.
460             coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCoord);
461             coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
462             coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
463             xCoord += FAKE_FINGER_SPACING;
464         }
465 
466         mDownTime = when;
467         mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT,
468                                           fingerCount);
469         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
470                                      /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
471                                      mFakeFingerCoords.data()));
472         for (size_t i = 1; i < mSwipeFingerCount; i++) {
473             out.push_back(makeMotionArgs(when, readTime,
474                                          AMOTION_EVENT_ACTION_POINTER_DOWN |
475                                                  (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
476                                          /* actionButton= */ 0, mButtonState,
477                                          /* pointerCount= */ i + 1, mFakeFingerCoords.data()));
478         }
479     }
480     float rotatedDeltaX = dx, rotatedDeltaY = -dy;
481     rotateDelta(mOrientation, &rotatedDeltaX, &rotatedDeltaY);
482     for (size_t i = 0; i < mSwipeFingerCount; i++) {
483         PointerCoords& coords = mFakeFingerCoords[i];
484         coords.setAxisValue(AMOTION_EVENT_AXIS_X,
485                             coords.getAxisValue(AMOTION_EVENT_AXIS_X) + rotatedDeltaX);
486         coords.setAxisValue(AMOTION_EVENT_AXIS_Y,
487                             coords.getAxisValue(AMOTION_EVENT_AXIS_Y) + rotatedDeltaY);
488     }
489     float xOffset = dx / (mXAxisInfo.maxValue - mXAxisInfo.minValue);
490     float yOffset = -dy / (mYAxisInfo.maxValue - mYAxisInfo.minValue);
491     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset);
492     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
493     out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
494                                  mButtonState, /* pointerCount= */ mSwipeFingerCount,
495                                  mFakeFingerCoords.data()));
496     return out;
497 }
498 
handleMultiFingerSwipeLift(nsecs_t when,nsecs_t readTime)499 [[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipeLift(nsecs_t when,
500                                                                                  nsecs_t readTime) {
501     std::list<NotifyArgs> out = {};
502     if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
503         return out;
504     }
505     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0);
506     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0);
507 
508     for (size_t i = mSwipeFingerCount; i > 1; i--) {
509         out.push_back(makeMotionArgs(when, readTime,
510                                      AMOTION_EVENT_ACTION_POINTER_UP |
511                                              ((i - 1) << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
512                                      /* actionButton= */ 0, mButtonState, /* pointerCount= */ i,
513                                      mFakeFingerCoords.data()));
514     }
515     out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP,
516                                  /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
517                                  mFakeFingerCoords.data()));
518     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT, 0);
519     mCurrentClassification = MotionClassification::NONE;
520     out += enterHover(when, readTime);
521     mSwipeFingerCount = 0;
522     return out;
523 }
524 
handlePinch(nsecs_t when,nsecs_t readTime,const Gesture & gesture)525 [[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime,
526                                                                   const Gesture& gesture) {
527     // Pinch gesture phases are reported a little differently from others, in that the same details
528     // struct is used for all phases of the gesture, just with different zoom_state values. When
529     // zoom_state is START or END, dz will always be 1, so we don't need to move the pointers in
530     // those cases.
531 
532     if (mCurrentClassification != MotionClassification::PINCH) {
533         LOG_ALWAYS_FATAL_IF(gesture.details.pinch.zoom_state != GESTURES_ZOOM_START,
534                             "First pinch gesture does not have the START zoom state (%d instead).",
535                             gesture.details.pinch.zoom_state);
536         std::list<NotifyArgs> out;
537 
538         out += exitHover(when, readTime);
539 
540         mCurrentClassification = MotionClassification::PINCH;
541         mPinchFingerSeparation = INITIAL_PINCH_SEPARATION_PX;
542         // PointerChoreographer will add the cursor position to these pointers.
543         mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
544         mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 0.f - mPinchFingerSeparation / 2);
545         mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
546         mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
547         mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 0.f + mPinchFingerSeparation / 2);
548         mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
549         mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
550         mDownTime = when;
551         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
552                                      /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
553                                      mFakeFingerCoords.data()));
554         out.push_back(makeMotionArgs(when, readTime,
555                                      AMOTION_EVENT_ACTION_POINTER_DOWN |
556                                              1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
557                                      /* actionButton= */ 0, mButtonState, /* pointerCount= */ 2,
558                                      mFakeFingerCoords.data()));
559         return out;
560     }
561 
562     if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) {
563         return endPinch(when, readTime);
564     }
565 
566     mPinchFingerSeparation *= gesture.details.pinch.dz;
567     // PointerChoreographer will add the cursor position to these pointers.
568     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR,
569                                       gesture.details.pinch.dz);
570     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 0.f - mPinchFingerSeparation / 2);
571     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
572     mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 0.f + mPinchFingerSeparation / 2);
573     mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
574     return {makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0,
575                            mButtonState, /*pointerCount=*/2, mFakeFingerCoords.data())};
576 }
577 
endPinch(nsecs_t when,nsecs_t readTime)578 std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) {
579     std::list<NotifyArgs> out;
580 
581     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
582     out.push_back(makeMotionArgs(when, readTime,
583                                  AMOTION_EVENT_ACTION_POINTER_UP |
584                                          1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
585                                  /*actionButton=*/0, mButtonState, /*pointerCount=*/2,
586                                  mFakeFingerCoords.data()));
587     out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
588                                  mButtonState, /*pointerCount=*/1, mFakeFingerCoords.data()));
589     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0);
590     mCurrentClassification = MotionClassification::NONE;
591     out += enterHover(when, readTime);
592     return out;
593 }
594 
enterHover(nsecs_t when,nsecs_t readTime)595 std::list<NotifyArgs> GestureConverter::enterHover(nsecs_t when, nsecs_t readTime) {
596     if (!mIsHovering) {
597         mIsHovering = true;
598         return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_ENTER)};
599     } else {
600         return {};
601     }
602 }
603 
exitHover(nsecs_t when,nsecs_t readTime)604 std::list<NotifyArgs> GestureConverter::exitHover(nsecs_t when, nsecs_t readTime) {
605     if (mIsHovering) {
606         mIsHovering = false;
607         return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_EXIT)};
608     } else {
609         return {};
610     }
611 }
612 
makeHoverEvent(nsecs_t when,nsecs_t readTime,int32_t action)613 NotifyMotionArgs GestureConverter::makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action) {
614     PointerCoords coords;
615     coords.clear();
616     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
617     coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
618     return makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState,
619                           /*pointerCount=*/1, &coords);
620 }
621 
makeMotionArgs(nsecs_t when,nsecs_t readTime,int32_t action,int32_t actionButton,int32_t buttonState,uint32_t pointerCount,const PointerCoords * pointerCoords)622 NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
623                                                   int32_t actionButton, int32_t buttonState,
624                                                   uint32_t pointerCount,
625                                                   const PointerCoords* pointerCoords) {
626     return {mReaderContext.getNextId(),
627             when,
628             readTime,
629             mDeviceId,
630             SOURCE,
631             *mDisplayId,
632             /* policyFlags= */ POLICY_FLAG_WAKE,
633             action,
634             /* actionButton= */ actionButton,
635             /* flags= */ action == AMOTION_EVENT_ACTION_CANCEL ? AMOTION_EVENT_FLAG_CANCELED : 0,
636             mReaderContext.getGlobalMetaState(),
637             buttonState,
638             mCurrentClassification,
639             AMOTION_EVENT_EDGE_FLAG_NONE,
640             pointerCount,
641             mFingerProps.data(),
642             pointerCoords,
643             /* xPrecision= */ 1.0f,
644             /* yPrecision= */ 1.0f,
645             /* xCursorPosition= */ 0.f,
646             /* yCursorPosition= */ 0.f,
647             /* downTime= */ mDownTime,
648             /* videoFrames= */ {}};
649 }
650 
enableTapToClick(nsecs_t when)651 void GestureConverter::enableTapToClick(nsecs_t when) {
652     if (mReaderContext.isPreventingTouchpadTaps()) {
653         mWhenToEnableTapToClick = when + TAP_ENABLE_DELAY_NANOS.count();
654         mReaderContext.setPreventingTouchpadTaps(false);
655     }
656 }
657 
658 } // namespace android
659