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