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
isGestureNoFocusChange(MotionClassification classification)63 bool isGestureNoFocusChange(MotionClassification classification) {
64 switch (classification) {
65 case MotionClassification::TWO_FINGER_SWIPE:
66 case MotionClassification::MULTI_FINGER_SWIPE:
67 case MotionClassification::PINCH:
68 // Most gestures can be performed on an unfocused window, so they should not
69 // not affect window focus.
70 return true;
71 case MotionClassification::NONE:
72 case MotionClassification::AMBIGUOUS_GESTURE:
73 case MotionClassification::DEEP_PRESS:
74 return false;
75 }
76 }
77
78 } // namespace
79
GestureConverter(InputReaderContext & readerContext,const InputDeviceContext & deviceContext,int32_t deviceId)80 GestureConverter::GestureConverter(InputReaderContext& readerContext,
81 const InputDeviceContext& deviceContext, int32_t deviceId)
82 : mDeviceId(deviceId),
83 mReaderContext(readerContext),
84 mEnableFlingStop(input_flags::enable_touchpad_fling_stop()),
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 ((ENABLE_TOUCHPAD_PALM_REJECTION || ENABLE_TOUCHPAD_PALM_REJECTION_V2) &&
227 (std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) {
228 enableTapToClick(when);
229 }
230 }
231
232 std::list<NotifyArgs> out;
233 const bool down = isPointerDown(mButtonState);
234 if (!down) {
235 out += enterHover(when, readTime);
236 }
237
238 PointerCoords coords;
239 coords.clear();
240 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
241 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
242 coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
243
244 const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
245 out.push_back(makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState,
246 /*pointerCount=*/1, &coords));
247 return out;
248 }
249
handleButtonsChange(nsecs_t when,nsecs_t readTime,const Gesture & gesture)250 std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime,
251 const Gesture& gesture) {
252 std::list<NotifyArgs> out = {};
253
254 PointerCoords coords;
255 coords.clear();
256 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
257 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
258
259 // V2 palm rejection should override V1
260 if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) {
261 enableTapToClick(when);
262 if (gesture.details.buttons.is_tap && when <= mWhenToEnableTapToClick) {
263 // return early to prevent this tap
264 return out;
265 }
266 } else if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) {
267 enableTapToClick(when);
268 if (gesture.details.buttons.is_tap) {
269 // return early to prevent this tap
270 return out;
271 }
272 }
273
274 const uint32_t buttonsPressed = gesture.details.buttons.down;
275 const uint32_t buttonsReleased = gesture.details.buttons.up;
276
277 if (mThreeFingerTapShortcutEnabled && gesture.details.buttons.is_tap &&
278 buttonsPressed == GESTURES_BUTTON_MIDDLE && buttonsReleased == GESTURES_BUTTON_MIDDLE) {
279 mReaderContext.getPolicy()->notifyTouchpadThreeFingerTap();
280 return out;
281 }
282
283 bool pointerDown = isPointerDown(mButtonState) ||
284 buttonsPressed &
285 (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT);
286 coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
287
288 uint32_t newButtonState = mButtonState;
289 std::list<NotifyArgs> pressEvents = {};
290 for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
291 if (buttonsPressed & button) {
292 uint32_t actionButton = gesturesButtonToMotionEventButton(button);
293 newButtonState |= actionButton;
294 pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
295 actionButton, newButtonState,
296 /*pointerCount=*/1, &coords));
297 }
298 }
299 if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
300 mDownTime = when;
301 out += exitHover(when, readTime);
302 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
303 /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
304 &coords));
305 }
306 out.splice(out.end(), pressEvents);
307
308 // The same button may be in both down and up in the same gesture, in which case we should treat
309 // it as having gone down and then up. So, we treat a single button change gesture as two state
310 // changes: a set of buttons going down, followed by a set of buttons going up.
311 mButtonState = newButtonState;
312
313 for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
314 if (buttonsReleased & button) {
315 uint32_t actionButton = gesturesButtonToMotionEventButton(button);
316 newButtonState &= ~actionButton;
317 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
318 actionButton, newButtonState, /* pointerCount= */ 1,
319 &coords));
320 }
321 }
322 if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
323 coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
324 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
325 newButtonState, /* pointerCount= */ 1, &coords));
326 mButtonState = newButtonState;
327 out += enterHover(when, readTime);
328 }
329 mButtonState = newButtonState;
330 return out;
331 }
332
releaseAllButtons(nsecs_t when,nsecs_t readTime)333 std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t readTime) {
334 std::list<NotifyArgs> out;
335
336 PointerCoords coords;
337 coords.clear();
338 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
339 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
340 const bool pointerDown = isPointerDown(mButtonState);
341 coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
342 uint32_t newButtonState = mButtonState;
343 for (uint32_t button = AMOTION_EVENT_BUTTON_PRIMARY; button <= AMOTION_EVENT_BUTTON_FORWARD;
344 button <<= 1) {
345 if (mButtonState & button) {
346 newButtonState &= ~button;
347 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
348 button, newButtonState, /*pointerCount=*/1, &coords));
349 }
350 }
351 mButtonState = 0;
352 if (pointerDown) {
353 coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
354 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
355 mButtonState, /*pointerCount=*/1, &coords));
356 out += enterHover(when, readTime);
357 }
358 return out;
359 }
360
handleScroll(nsecs_t when,nsecs_t readTime,const Gesture & gesture)361 std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readTime,
362 const Gesture& gesture) {
363 std::list<NotifyArgs> out;
364 PointerCoords& coords = mFakeFingerCoords[0];
365 if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) {
366 out += exitHover(when, readTime);
367
368 mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
369 coords.clear();
370 coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
371 mDownTime = when;
372 NotifyMotionArgs args =
373 makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0,
374 mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
375 out.push_back(args);
376 }
377 float deltaX = gesture.details.scroll.dx;
378 float deltaY = gesture.details.scroll.dy;
379 rotateDelta(mOrientation, &deltaX, &deltaY);
380
381 coords.setAxisValue(AMOTION_EVENT_AXIS_X, coords.getAxisValue(AMOTION_EVENT_AXIS_X) + deltaX);
382 coords.setAxisValue(AMOTION_EVENT_AXIS_Y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y) + deltaY);
383 // TODO(b/262876643): set AXIS_GESTURE_{X,Y}_OFFSET.
384 coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, -gesture.details.scroll.dx);
385 coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, -gesture.details.scroll.dy);
386 NotifyMotionArgs args =
387 makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
388 mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
389 out.push_back(args);
390 return out;
391 }
392
handleFling(nsecs_t when,nsecs_t readTime,nsecs_t gestureStartTime,const Gesture & gesture)393 std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTime,
394 nsecs_t gestureStartTime,
395 const Gesture& gesture) {
396 switch (gesture.details.fling.fling_state) {
397 case GESTURES_FLING_START:
398 if (mCurrentClassification == MotionClassification::TWO_FINGER_SWIPE) {
399 // We don't actually want to use the gestures library's fling velocity values (to
400 // ensure consistency between touchscreen and touchpad flings), so we're just using
401 // the "start fling" gestures as a marker for the end of a two-finger scroll
402 // gesture.
403 mFlingMayBeInProgress = true;
404 return endScroll(when, readTime);
405 }
406 break;
407 case GESTURES_FLING_TAP_DOWN:
408 if (mCurrentClassification == MotionClassification::NONE) {
409 if (mEnableFlingStop && mFlingMayBeInProgress) {
410 // The user has just touched the pad again after ending a two-finger scroll
411 // motion, which might have started a fling. We want to stop the fling, but
412 // unfortunately there's currently no API for doing so. Instead, send and
413 // immediately cancel a fake finger to cause the scrolling to stop and hopefully
414 // avoid side effects (e.g. activation of UI elements).
415 // TODO(b/326056750): add an API for fling stops.
416 mFlingMayBeInProgress = false;
417 PointerCoords coords;
418 coords.clear();
419 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
420 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
421
422 std::list<NotifyArgs> out;
423 mDownTime = when;
424 mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
425 out += exitHover(when, readTime);
426 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
427 /*actionButton=*/0, /*buttonState=*/0,
428 /*pointerCount=*/1, &coords));
429 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_CANCEL,
430 /*actionButton=*/0, /*buttonState=*/0,
431 /*pointerCount=*/1, &coords));
432 out += enterHover(when, readTime);
433 mCurrentClassification = MotionClassification::NONE;
434 return out;
435 } else {
436 // Use the tap down state of a fling gesture as an indicator that a contact
437 // has been initiated with the touchpad. We treat this as a move event with zero
438 // magnitude, which will also result in the pointer icon being updated.
439 // TODO(b/282023644): Add a signal in libgestures for when a stable contact has
440 // been initiated with a touchpad.
441 return handleMove(when, readTime, gestureStartTime,
442 Gesture(kGestureMove, gesture.start_time, gesture.end_time,
443 /*dx=*/0.f,
444 /*dy=*/0.f));
445 }
446 }
447 break;
448 default:
449 break;
450 }
451
452 return {};
453 }
454
endScroll(nsecs_t when,nsecs_t readTime)455 std::list<NotifyArgs> GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
456 std::list<NotifyArgs> out;
457 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0);
458 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0);
459 NotifyMotionArgs args =
460 makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
461 mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
462 out.push_back(args);
463 mCurrentClassification = MotionClassification::NONE;
464 out += enterHover(when, readTime);
465 return out;
466 }
467
handleMultiFingerSwipe(nsecs_t when,nsecs_t readTime,uint32_t fingerCount,float dx,float dy)468 [[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipe(nsecs_t when,
469 nsecs_t readTime,
470 uint32_t fingerCount,
471 float dx, float dy) {
472 std::list<NotifyArgs> out = {};
473 if (!mEnableSystemGestures) {
474 return out;
475 }
476
477 if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
478 // If the user changes the number of fingers mid-way through a swipe (e.g. they start with
479 // three and then put a fourth finger down), the gesture library will treat it as two
480 // separate swipes with an appropriate lift event between them, so we don't have to worry
481 // about the finger count changing mid-swipe.
482
483 out += exitHover(when, readTime);
484
485 mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE;
486
487 mSwipeFingerCount = fingerCount;
488
489 constexpr float FAKE_FINGER_SPACING = 100;
490 float xCoord = 0.f - FAKE_FINGER_SPACING * (mSwipeFingerCount - 1) / 2;
491 for (size_t i = 0; i < mSwipeFingerCount; i++) {
492 PointerCoords& coords = mFakeFingerCoords[i];
493 coords.clear();
494 // PointerChoreographer will add the cursor position to these pointers.
495 coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCoord);
496 coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
497 coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
498 xCoord += FAKE_FINGER_SPACING;
499 }
500
501 mDownTime = when;
502 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT,
503 fingerCount);
504 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
505 /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
506 mFakeFingerCoords.data()));
507 for (size_t i = 1; i < mSwipeFingerCount; i++) {
508 out.push_back(makeMotionArgs(when, readTime,
509 AMOTION_EVENT_ACTION_POINTER_DOWN |
510 (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
511 /* actionButton= */ 0, mButtonState,
512 /* pointerCount= */ i + 1, mFakeFingerCoords.data()));
513 }
514 }
515 float rotatedDeltaX = dx, rotatedDeltaY = -dy;
516 rotateDelta(mOrientation, &rotatedDeltaX, &rotatedDeltaY);
517 for (size_t i = 0; i < mSwipeFingerCount; i++) {
518 PointerCoords& coords = mFakeFingerCoords[i];
519 coords.setAxisValue(AMOTION_EVENT_AXIS_X,
520 coords.getAxisValue(AMOTION_EVENT_AXIS_X) + rotatedDeltaX);
521 coords.setAxisValue(AMOTION_EVENT_AXIS_Y,
522 coords.getAxisValue(AMOTION_EVENT_AXIS_Y) + rotatedDeltaY);
523 }
524 float xOffset = dx / (mXAxisInfo.maxValue - mXAxisInfo.minValue);
525 float yOffset = -dy / (mYAxisInfo.maxValue - mYAxisInfo.minValue);
526 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset);
527 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
528 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
529 mButtonState, /* pointerCount= */ mSwipeFingerCount,
530 mFakeFingerCoords.data()));
531 return out;
532 }
533
handleMultiFingerSwipeLift(nsecs_t when,nsecs_t readTime)534 [[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipeLift(nsecs_t when,
535 nsecs_t readTime) {
536 std::list<NotifyArgs> out = {};
537 if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
538 return out;
539 }
540 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0);
541 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0);
542
543 for (size_t i = mSwipeFingerCount; i > 1; i--) {
544 out.push_back(makeMotionArgs(when, readTime,
545 AMOTION_EVENT_ACTION_POINTER_UP |
546 ((i - 1) << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
547 /* actionButton= */ 0, mButtonState, /* pointerCount= */ i,
548 mFakeFingerCoords.data()));
549 }
550 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP,
551 /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
552 mFakeFingerCoords.data()));
553 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT, 0);
554 mCurrentClassification = MotionClassification::NONE;
555 out += enterHover(when, readTime);
556 mSwipeFingerCount = 0;
557 return out;
558 }
559
handlePinch(nsecs_t when,nsecs_t readTime,const Gesture & gesture)560 [[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime,
561 const Gesture& gesture) {
562 // Pinch gesture phases are reported a little differently from others, in that the same details
563 // struct is used for all phases of the gesture, just with different zoom_state values. When
564 // zoom_state is START or END, dz will always be 1, so we don't need to move the pointers in
565 // those cases.
566
567 if (mCurrentClassification != MotionClassification::PINCH) {
568 LOG_ALWAYS_FATAL_IF(gesture.details.pinch.zoom_state != GESTURES_ZOOM_START,
569 "First pinch gesture does not have the START zoom state (%d instead).",
570 gesture.details.pinch.zoom_state);
571 std::list<NotifyArgs> out;
572
573 out += exitHover(when, readTime);
574
575 mCurrentClassification = MotionClassification::PINCH;
576 mPinchFingerSeparation = INITIAL_PINCH_SEPARATION_PX;
577 // PointerChoreographer will add the cursor position to these pointers.
578 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
579 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 0.f - mPinchFingerSeparation / 2);
580 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
581 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
582 mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 0.f + mPinchFingerSeparation / 2);
583 mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
584 mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
585 mDownTime = when;
586 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
587 /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
588 mFakeFingerCoords.data()));
589 out.push_back(makeMotionArgs(when, readTime,
590 AMOTION_EVENT_ACTION_POINTER_DOWN |
591 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
592 /* actionButton= */ 0, mButtonState, /* pointerCount= */ 2,
593 mFakeFingerCoords.data()));
594 return out;
595 }
596
597 if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) {
598 return endPinch(when, readTime);
599 }
600
601 mPinchFingerSeparation *= gesture.details.pinch.dz;
602 // PointerChoreographer will add the cursor position to these pointers.
603 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR,
604 gesture.details.pinch.dz);
605 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 0.f - mPinchFingerSeparation / 2);
606 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
607 mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 0.f + mPinchFingerSeparation / 2);
608 mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
609 return {makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0,
610 mButtonState, /*pointerCount=*/2, mFakeFingerCoords.data())};
611 }
612
endPinch(nsecs_t when,nsecs_t readTime)613 std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) {
614 std::list<NotifyArgs> out;
615
616 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
617 out.push_back(makeMotionArgs(when, readTime,
618 AMOTION_EVENT_ACTION_POINTER_UP |
619 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
620 /*actionButton=*/0, mButtonState, /*pointerCount=*/2,
621 mFakeFingerCoords.data()));
622 out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
623 mButtonState, /*pointerCount=*/1, mFakeFingerCoords.data()));
624 mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0);
625 mCurrentClassification = MotionClassification::NONE;
626 out += enterHover(when, readTime);
627 return out;
628 }
629
enterHover(nsecs_t when,nsecs_t readTime)630 std::list<NotifyArgs> GestureConverter::enterHover(nsecs_t when, nsecs_t readTime) {
631 if (!mIsHovering) {
632 mIsHovering = true;
633 return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_ENTER)};
634 } else {
635 return {};
636 }
637 }
638
exitHover(nsecs_t when,nsecs_t readTime)639 std::list<NotifyArgs> GestureConverter::exitHover(nsecs_t when, nsecs_t readTime) {
640 if (mIsHovering) {
641 mIsHovering = false;
642 return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_EXIT)};
643 } else {
644 return {};
645 }
646 }
647
makeHoverEvent(nsecs_t when,nsecs_t readTime,int32_t action)648 NotifyMotionArgs GestureConverter::makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action) {
649 PointerCoords coords;
650 coords.clear();
651 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
652 coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
653 return makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState,
654 /*pointerCount=*/1, &coords);
655 }
656
makeMotionArgs(nsecs_t when,nsecs_t readTime,int32_t action,int32_t actionButton,int32_t buttonState,uint32_t pointerCount,const PointerCoords * pointerCoords)657 NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
658 int32_t actionButton, int32_t buttonState,
659 uint32_t pointerCount,
660 const PointerCoords* pointerCoords) {
661 int32_t flags = 0;
662 if (action == AMOTION_EVENT_ACTION_CANCEL) {
663 flags |= AMOTION_EVENT_FLAG_CANCELED;
664 }
665 if (mEnableNoFocusChange && isGestureNoFocusChange(mCurrentClassification)) {
666 flags |= AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
667 }
668 if (mCurrentClassification == MotionClassification::TWO_FINGER_SWIPE) {
669 // This helps to make GestureDetector responsive.
670 flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
671 }
672
673 return {mReaderContext.getNextId(),
674 when,
675 readTime,
676 mDeviceId,
677 SOURCE,
678 *mDisplayId,
679 /* policyFlags= */ POLICY_FLAG_WAKE,
680 action,
681 /* actionButton= */ actionButton,
682 flags,
683 mReaderContext.getGlobalMetaState(),
684 buttonState,
685 mCurrentClassification,
686 AMOTION_EVENT_EDGE_FLAG_NONE,
687 pointerCount,
688 mFingerProps.data(),
689 pointerCoords,
690 /* xPrecision= */ 1.0f,
691 /* yPrecision= */ 1.0f,
692 /* xCursorPosition= */ 0.f,
693 /* yCursorPosition= */ 0.f,
694 /* downTime= */ mDownTime,
695 /* videoFrames= */ {}};
696 }
697
enableTapToClick(nsecs_t when)698 void GestureConverter::enableTapToClick(nsecs_t when) {
699 if (mReaderContext.isPreventingTouchpadTaps()) {
700 mWhenToEnableTapToClick = when + TAP_ENABLE_DELAY_NANOS.count();
701 mReaderContext.setPreventingTouchpadTaps(false);
702 }
703 }
704
705 } // namespace android
706