1 /*
2  * Copyright (C) 2019 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 "CursorInputMapper.h"
20 
21 #include "CursorButtonAccumulator.h"
22 #include "CursorScrollAccumulator.h"
23 #include "TouchCursorInputMapperCommon.h"
24 
25 namespace android {
26 
27 // --- CursorMotionAccumulator ---
28 
CursorMotionAccumulator()29 CursorMotionAccumulator::CursorMotionAccumulator() {
30     clearRelativeAxes();
31 }
32 
reset(InputDeviceContext & deviceContext)33 void CursorMotionAccumulator::reset(InputDeviceContext& deviceContext) {
34     clearRelativeAxes();
35 }
36 
clearRelativeAxes()37 void CursorMotionAccumulator::clearRelativeAxes() {
38     mRelX = 0;
39     mRelY = 0;
40 }
41 
process(const RawEvent * rawEvent)42 void CursorMotionAccumulator::process(const RawEvent* rawEvent) {
43     if (rawEvent->type == EV_REL) {
44         switch (rawEvent->code) {
45             case REL_X:
46                 mRelX = rawEvent->value;
47                 break;
48             case REL_Y:
49                 mRelY = rawEvent->value;
50                 break;
51         }
52     }
53 }
54 
finishSync()55 void CursorMotionAccumulator::finishSync() {
56     clearRelativeAxes();
57 }
58 
59 // --- CursorInputMapper ---
60 
CursorInputMapper(InputDeviceContext & deviceContext)61 CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext)
62       : InputMapper(deviceContext) {}
63 
~CursorInputMapper()64 CursorInputMapper::~CursorInputMapper() {}
65 
getSources()66 uint32_t CursorInputMapper::getSources() {
67     return mSource;
68 }
69 
populateDeviceInfo(InputDeviceInfo * info)70 void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
71     InputMapper::populateDeviceInfo(info);
72 
73     if (mParameters.mode == Parameters::MODE_POINTER) {
74         float minX, minY, maxX, maxY;
75         if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
76             info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
77             info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f);
78         }
79     } else {
80         info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
81         info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
82     }
83     info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
84 
85     if (mCursorScrollAccumulator.haveRelativeVWheel()) {
86         info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
87     }
88     if (mCursorScrollAccumulator.haveRelativeHWheel()) {
89         info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
90     }
91 }
92 
dump(std::string & dump)93 void CursorInputMapper::dump(std::string& dump) {
94     dump += INDENT2 "Cursor Input Mapper:\n";
95     dumpParameters(dump);
96     dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale);
97     dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale);
98     dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
99     dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
100     dump += StringPrintf(INDENT3 "HaveVWheel: %s\n",
101                          toString(mCursorScrollAccumulator.haveRelativeVWheel()));
102     dump += StringPrintf(INDENT3 "HaveHWheel: %s\n",
103                          toString(mCursorScrollAccumulator.haveRelativeHWheel()));
104     dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
105     dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
106     dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
107     dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
108     dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
109     dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
110 }
111 
configure(nsecs_t when,const InputReaderConfiguration * config,uint32_t changes)112 void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
113                                   uint32_t changes) {
114     InputMapper::configure(when, config, changes);
115 
116     if (!changes) { // first time only
117         mCursorScrollAccumulator.configure(getDeviceContext());
118 
119         // Configure basic parameters.
120         configureParameters();
121 
122         // Configure device mode.
123         switch (mParameters.mode) {
124             case Parameters::MODE_POINTER_RELATIVE:
125                 // Should not happen during first time configuration.
126                 ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
127                 mParameters.mode = Parameters::MODE_POINTER;
128                 [[fallthrough]];
129             case Parameters::MODE_POINTER:
130                 mSource = AINPUT_SOURCE_MOUSE;
131                 mXPrecision = 1.0f;
132                 mYPrecision = 1.0f;
133                 mXScale = 1.0f;
134                 mYScale = 1.0f;
135                 mPointerController = getContext()->getPointerController(getDeviceId());
136                 break;
137             case Parameters::MODE_NAVIGATION:
138                 mSource = AINPUT_SOURCE_TRACKBALL;
139                 mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
140                 mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
141                 mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
142                 mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
143                 break;
144         }
145 
146         mVWheelScale = 1.0f;
147         mHWheelScale = 1.0f;
148     }
149 
150     if ((!changes && config->pointerCapture) ||
151         (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) {
152         if (config->pointerCapture) {
153             if (mParameters.mode == Parameters::MODE_POINTER) {
154                 mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
155                 mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
156                 // Keep PointerController around in order to preserve the pointer position.
157                 mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE);
158             } else {
159                 ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
160             }
161         } else {
162             if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) {
163                 mParameters.mode = Parameters::MODE_POINTER;
164                 mSource = AINPUT_SOURCE_MOUSE;
165             } else {
166                 ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
167             }
168         }
169         bumpGeneration();
170         if (changes) {
171             NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
172             getListener()->notifyDeviceReset(&args);
173         }
174     }
175 
176     if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
177         mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters);
178         mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters);
179         mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters);
180     }
181 
182     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
183         mOrientation = DISPLAY_ORIENTATION_0;
184         if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
185             std::optional<DisplayViewport> internalViewport =
186                     config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
187             if (internalViewport) {
188                 mOrientation = internalViewport->orientation;
189             }
190         }
191 
192         bumpGeneration();
193     }
194 }
195 
configureParameters()196 void CursorInputMapper::configureParameters() {
197     mParameters.mode = Parameters::MODE_POINTER;
198     String8 cursorModeString;
199     if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"),
200                                                              cursorModeString)) {
201         if (cursorModeString == "navigation") {
202             mParameters.mode = Parameters::MODE_NAVIGATION;
203         } else if (cursorModeString != "pointer" && cursorModeString != "default") {
204             ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
205         }
206     }
207 
208     mParameters.orientationAware = false;
209     getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
210                                                          mParameters.orientationAware);
211 
212     mParameters.hasAssociatedDisplay = false;
213     if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) {
214         mParameters.hasAssociatedDisplay = true;
215     }
216 }
217 
dumpParameters(std::string & dump)218 void CursorInputMapper::dumpParameters(std::string& dump) {
219     dump += INDENT3 "Parameters:\n";
220     dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
221                          toString(mParameters.hasAssociatedDisplay));
222 
223     switch (mParameters.mode) {
224         case Parameters::MODE_POINTER:
225             dump += INDENT4 "Mode: pointer\n";
226             break;
227         case Parameters::MODE_POINTER_RELATIVE:
228             dump += INDENT4 "Mode: relative pointer\n";
229             break;
230         case Parameters::MODE_NAVIGATION:
231             dump += INDENT4 "Mode: navigation\n";
232             break;
233         default:
234             ALOG_ASSERT(false);
235     }
236 
237     dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
238 }
239 
reset(nsecs_t when)240 void CursorInputMapper::reset(nsecs_t when) {
241     mButtonState = 0;
242     mDownTime = 0;
243 
244     mPointerVelocityControl.reset();
245     mWheelXVelocityControl.reset();
246     mWheelYVelocityControl.reset();
247 
248     mCursorButtonAccumulator.reset(getDeviceContext());
249     mCursorMotionAccumulator.reset(getDeviceContext());
250     mCursorScrollAccumulator.reset(getDeviceContext());
251 
252     InputMapper::reset(when);
253 }
254 
process(const RawEvent * rawEvent)255 void CursorInputMapper::process(const RawEvent* rawEvent) {
256     mCursorButtonAccumulator.process(rawEvent);
257     mCursorMotionAccumulator.process(rawEvent);
258     mCursorScrollAccumulator.process(rawEvent);
259 
260     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
261         sync(rawEvent->when);
262     }
263 }
264 
sync(nsecs_t when)265 void CursorInputMapper::sync(nsecs_t when) {
266     int32_t lastButtonState = mButtonState;
267     int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
268     mButtonState = currentButtonState;
269 
270     bool wasDown = isPointerDown(lastButtonState);
271     bool down = isPointerDown(currentButtonState);
272     bool downChanged;
273     if (!wasDown && down) {
274         mDownTime = when;
275         downChanged = true;
276     } else if (wasDown && !down) {
277         downChanged = true;
278     } else {
279         downChanged = false;
280     }
281     nsecs_t downTime = mDownTime;
282     bool buttonsChanged = currentButtonState != lastButtonState;
283     int32_t buttonsPressed = currentButtonState & ~lastButtonState;
284     int32_t buttonsReleased = lastButtonState & ~currentButtonState;
285 
286     float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
287     float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
288     bool moved = deltaX != 0 || deltaY != 0;
289 
290     // Rotate delta according to orientation if needed.
291     if (mParameters.orientationAware && mParameters.hasAssociatedDisplay &&
292         (deltaX != 0.0f || deltaY != 0.0f)) {
293         rotateDelta(mOrientation, &deltaX, &deltaY);
294     }
295 
296     // Move the pointer.
297     PointerProperties pointerProperties;
298     pointerProperties.clear();
299     pointerProperties.id = 0;
300     pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
301 
302     PointerCoords pointerCoords;
303     pointerCoords.clear();
304 
305     float vscroll = mCursorScrollAccumulator.getRelativeVWheel();
306     float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
307     bool scrolled = vscroll != 0 || hscroll != 0;
308 
309     mWheelYVelocityControl.move(when, nullptr, &vscroll);
310     mWheelXVelocityControl.move(when, &hscroll, nullptr);
311 
312     mPointerVelocityControl.move(when, &deltaX, &deltaY);
313 
314     int32_t displayId;
315     float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
316     float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
317     if (mSource == AINPUT_SOURCE_MOUSE) {
318         if (moved || scrolled || buttonsChanged) {
319             mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
320 
321             if (moved) {
322                 mPointerController->move(deltaX, deltaY);
323             }
324 
325             if (buttonsChanged) {
326                 mPointerController->setButtonState(currentButtonState);
327             }
328 
329             mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
330         }
331 
332         mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
333         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
334         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
335         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
336         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
337         displayId = mPointerController->getDisplayId();
338     } else {
339         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
340         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
341         displayId = ADISPLAY_ID_NONE;
342     }
343 
344     pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
345 
346     // Moving an external trackball or mouse should wake the device.
347     // We don't do this for internal cursor devices to prevent them from waking up
348     // the device in your pocket.
349     // TODO: Use the input device configuration to control this behavior more finely.
350     uint32_t policyFlags = 0;
351     if ((buttonsPressed || moved || scrolled) && getDeviceContext().isExternal()) {
352         policyFlags |= POLICY_FLAG_WAKE;
353     }
354 
355     // Synthesize key down from buttons if needed.
356     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
357                          displayId, policyFlags, lastButtonState, currentButtonState);
358 
359     // Send motion event.
360     if (downChanged || moved || scrolled || buttonsChanged) {
361         int32_t metaState = getContext()->getGlobalMetaState();
362         int32_t buttonState = lastButtonState;
363         int32_t motionEventAction;
364         if (downChanged) {
365             motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
366         } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) {
367             motionEventAction = AMOTION_EVENT_ACTION_MOVE;
368         } else {
369             motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
370         }
371 
372         if (buttonsReleased) {
373             BitSet32 released(buttonsReleased);
374             while (!released.isEmpty()) {
375                 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
376                 buttonState &= ~actionButton;
377                 NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, getDeviceId(),
378                                              mSource, displayId, policyFlags,
379                                              AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
380                                              metaState, buttonState, MotionClassification::NONE,
381                                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
382                                              &pointerCoords, mXPrecision, mYPrecision,
383                                              xCursorPosition, yCursorPosition, downTime,
384                                              /* videoFrames */ {});
385                 getListener()->notifyMotion(&releaseArgs);
386             }
387         }
388 
389         NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
390                               policyFlags, motionEventAction, 0, 0, metaState, currentButtonState,
391                               MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
392                               &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
393                               xCursorPosition, yCursorPosition, downTime,
394                               /* videoFrames */ {});
395         getListener()->notifyMotion(&args);
396 
397         if (buttonsPressed) {
398             BitSet32 pressed(buttonsPressed);
399             while (!pressed.isEmpty()) {
400                 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
401                 buttonState |= actionButton;
402                 NotifyMotionArgs pressArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
403                                            displayId, policyFlags,
404                                            AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
405                                            metaState, buttonState, MotionClassification::NONE,
406                                            AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
407                                            &pointerCoords, mXPrecision, mYPrecision,
408                                            xCursorPosition, yCursorPosition, downTime,
409                                            /* videoFrames */ {});
410                 getListener()->notifyMotion(&pressArgs);
411             }
412         }
413 
414         ALOG_ASSERT(buttonState == currentButtonState);
415 
416         // Send hover move after UP to tell the application that the mouse is hovering now.
417         if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
418             NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
419                                        displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
420                                        0, metaState, currentButtonState, MotionClassification::NONE,
421                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
422                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
423                                        yCursorPosition, downTime, /* videoFrames */ {});
424             getListener()->notifyMotion(&hoverArgs);
425         }
426 
427         // Send scroll events.
428         if (scrolled) {
429             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
430             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
431 
432             NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
433                                         displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
434                                         metaState, currentButtonState, MotionClassification::NONE,
435                                         AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
436                                         &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
437                                         yCursorPosition, downTime, /* videoFrames */ {});
438             getListener()->notifyMotion(&scrollArgs);
439         }
440     }
441 
442     // Synthesize key up from buttons if needed.
443     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
444                          displayId, policyFlags, lastButtonState, currentButtonState);
445 
446     mCursorMotionAccumulator.finishSync();
447     mCursorScrollAccumulator.finishSync();
448 }
449 
getScanCodeState(uint32_t sourceMask,int32_t scanCode)450 int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
451     if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
452         return getDeviceContext().getScanCodeState(scanCode);
453     } else {
454         return AKEY_STATE_UNKNOWN;
455     }
456 }
457 
getAssociatedDisplayId()458 std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
459     if (mParameters.hasAssociatedDisplay) {
460         if (mParameters.mode == Parameters::MODE_POINTER) {
461             return std::make_optional(mPointerController->getDisplayId());
462         } else {
463             // If the device is orientationAware and not a mouse,
464             // it expects to dispatch events to any display
465             return std::make_optional(ADISPLAY_ID_NONE);
466         }
467     }
468     return std::nullopt;
469 }
470 
471 } // namespace android
472