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