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