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