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 "PointerControllerInterface.h"
24 #include "TouchCursorInputMapperCommon.h"
25 
26 namespace android {
27 
28 // --- CursorMotionAccumulator ---
29 
CursorMotionAccumulator()30 CursorMotionAccumulator::CursorMotionAccumulator() {
31     clearRelativeAxes();
32 }
33 
reset(InputDevice * device)34 void CursorMotionAccumulator::reset(InputDevice* device) {
35     clearRelativeAxes();
36 }
37 
clearRelativeAxes()38 void CursorMotionAccumulator::clearRelativeAxes() {
39     mRelX = 0;
40     mRelY = 0;
41 }
42 
process(const RawEvent * rawEvent)43 void CursorMotionAccumulator::process(const RawEvent* rawEvent) {
44     if (rawEvent->type == EV_REL) {
45         switch (rawEvent->code) {
46             case REL_X:
47                 mRelX = rawEvent->value;
48                 break;
49             case REL_Y:
50                 mRelY = rawEvent->value;
51                 break;
52         }
53     }
54 }
55 
finishSync()56 void CursorMotionAccumulator::finishSync() {
57     clearRelativeAxes();
58 }
59 
60 // --- CursorInputMapper ---
61 
CursorInputMapper(InputDevice * device)62 CursorInputMapper::CursorInputMapper(InputDevice* device) : InputMapper(device) {}
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(getDevice());
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 = getPolicy()->obtainPointerController(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             getDevice()->notifyReset(when);
172         }
173     }
174 
175     if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
176         mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters);
177         mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters);
178         mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters);
179     }
180 
181     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
182         mOrientation = DISPLAY_ORIENTATION_0;
183         if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
184             std::optional<DisplayViewport> internalViewport =
185                     config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
186             if (internalViewport) {
187                 mOrientation = internalViewport->orientation;
188             }
189         }
190 
191         // Update the PointerController if viewports changed.
192         if (mParameters.mode == Parameters::MODE_POINTER) {
193             updatePointerControllerDisplayViewport(*config);
194         }
195         bumpGeneration();
196     }
197 }
198 
updatePointerControllerDisplayViewport(const InputReaderConfiguration & config)199 void CursorInputMapper::updatePointerControllerDisplayViewport(
200         const InputReaderConfiguration& config) {
201     std::optional<DisplayViewport> viewport =
202             config.getDisplayViewportById(config.defaultPointerDisplayId);
203     if (!viewport) {
204         ALOGW("Can't find the designated viewport with ID %" PRId32 " to update cursor input "
205               "mapper. Fall back to default display",
206               config.defaultPointerDisplayId);
207         viewport = config.getDisplayViewportById(ADISPLAY_ID_DEFAULT);
208     }
209 
210     if (!viewport) {
211         ALOGE("Still can't find a viable viewport to update cursor input mapper. Skip setting it to"
212               " PointerController.");
213         return;
214     }
215 
216     mPointerController->setDisplayViewport(*viewport);
217 }
218 
configureParameters()219 void CursorInputMapper::configureParameters() {
220     mParameters.mode = Parameters::MODE_POINTER;
221     String8 cursorModeString;
222     if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) {
223         if (cursorModeString == "navigation") {
224             mParameters.mode = Parameters::MODE_NAVIGATION;
225         } else if (cursorModeString != "pointer" && cursorModeString != "default") {
226             ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
227         }
228     }
229 
230     mParameters.orientationAware = false;
231     getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
232                                                    mParameters.orientationAware);
233 
234     mParameters.hasAssociatedDisplay = false;
235     if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) {
236         mParameters.hasAssociatedDisplay = true;
237     }
238 }
239 
dumpParameters(std::string & dump)240 void CursorInputMapper::dumpParameters(std::string& dump) {
241     dump += INDENT3 "Parameters:\n";
242     dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
243                          toString(mParameters.hasAssociatedDisplay));
244 
245     switch (mParameters.mode) {
246         case Parameters::MODE_POINTER:
247             dump += INDENT4 "Mode: pointer\n";
248             break;
249         case Parameters::MODE_POINTER_RELATIVE:
250             dump += INDENT4 "Mode: relative pointer\n";
251             break;
252         case Parameters::MODE_NAVIGATION:
253             dump += INDENT4 "Mode: navigation\n";
254             break;
255         default:
256             ALOG_ASSERT(false);
257     }
258 
259     dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
260 }
261 
reset(nsecs_t when)262 void CursorInputMapper::reset(nsecs_t when) {
263     mButtonState = 0;
264     mDownTime = 0;
265 
266     mPointerVelocityControl.reset();
267     mWheelXVelocityControl.reset();
268     mWheelYVelocityControl.reset();
269 
270     mCursorButtonAccumulator.reset(getDevice());
271     mCursorMotionAccumulator.reset(getDevice());
272     mCursorScrollAccumulator.reset(getDevice());
273 
274     InputMapper::reset(when);
275 }
276 
process(const RawEvent * rawEvent)277 void CursorInputMapper::process(const RawEvent* rawEvent) {
278     mCursorButtonAccumulator.process(rawEvent);
279     mCursorMotionAccumulator.process(rawEvent);
280     mCursorScrollAccumulator.process(rawEvent);
281 
282     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
283         sync(rawEvent->when);
284     }
285 }
286 
sync(nsecs_t when)287 void CursorInputMapper::sync(nsecs_t when) {
288     int32_t lastButtonState = mButtonState;
289     int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
290     mButtonState = currentButtonState;
291 
292     bool wasDown = isPointerDown(lastButtonState);
293     bool down = isPointerDown(currentButtonState);
294     bool downChanged;
295     if (!wasDown && down) {
296         mDownTime = when;
297         downChanged = true;
298     } else if (wasDown && !down) {
299         downChanged = true;
300     } else {
301         downChanged = false;
302     }
303     nsecs_t downTime = mDownTime;
304     bool buttonsChanged = currentButtonState != lastButtonState;
305     int32_t buttonsPressed = currentButtonState & ~lastButtonState;
306     int32_t buttonsReleased = lastButtonState & ~currentButtonState;
307 
308     float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
309     float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
310     bool moved = deltaX != 0 || deltaY != 0;
311 
312     // Rotate delta according to orientation if needed.
313     if (mParameters.orientationAware && mParameters.hasAssociatedDisplay &&
314         (deltaX != 0.0f || deltaY != 0.0f)) {
315         rotateDelta(mOrientation, &deltaX, &deltaY);
316     }
317 
318     // Move the pointer.
319     PointerProperties pointerProperties;
320     pointerProperties.clear();
321     pointerProperties.id = 0;
322     pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
323 
324     PointerCoords pointerCoords;
325     pointerCoords.clear();
326 
327     float vscroll = mCursorScrollAccumulator.getRelativeVWheel();
328     float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
329     bool scrolled = vscroll != 0 || hscroll != 0;
330 
331     mWheelYVelocityControl.move(when, nullptr, &vscroll);
332     mWheelXVelocityControl.move(when, &hscroll, nullptr);
333 
334     mPointerVelocityControl.move(when, &deltaX, &deltaY);
335 
336     int32_t displayId;
337     if (mSource == AINPUT_SOURCE_MOUSE) {
338         if (moved || scrolled || buttonsChanged) {
339             mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
340 
341             if (moved) {
342                 mPointerController->move(deltaX, deltaY);
343             }
344 
345             if (buttonsChanged) {
346                 mPointerController->setButtonState(currentButtonState);
347             }
348 
349             mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
350         }
351 
352         float x, y;
353         mPointerController->getPosition(&x, &y);
354         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
355         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
356         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
357         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
358         displayId = mPointerController->getDisplayId();
359     } else {
360         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
361         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
362         displayId = ADISPLAY_ID_NONE;
363     }
364 
365     pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
366 
367     // Moving an external trackball or mouse should wake the device.
368     // We don't do this for internal cursor devices to prevent them from waking up
369     // the device in your pocket.
370     // TODO: Use the input device configuration to control this behavior more finely.
371     uint32_t policyFlags = 0;
372     if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) {
373         policyFlags |= POLICY_FLAG_WAKE;
374     }
375 
376     // Synthesize key down from buttons if needed.
377     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
378                          displayId, policyFlags, lastButtonState, currentButtonState);
379 
380     // Send motion event.
381     if (downChanged || moved || scrolled || buttonsChanged) {
382         int32_t metaState = mContext->getGlobalMetaState();
383         int32_t buttonState = lastButtonState;
384         int32_t motionEventAction;
385         if (downChanged) {
386             motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
387         } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) {
388             motionEventAction = AMOTION_EVENT_ACTION_MOVE;
389         } else {
390             motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
391         }
392 
393         if (buttonsReleased) {
394             BitSet32 released(buttonsReleased);
395             while (!released.isEmpty()) {
396                 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
397                 buttonState &= ~actionButton;
398                 NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
399                                              mSource, displayId, policyFlags,
400                                              AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
401                                              metaState, buttonState, MotionClassification::NONE,
402                                              AMOTION_EVENT_EDGE_FLAG_NONE,
403                                              /* deviceTimestamp */ 0, 1, &pointerProperties,
404                                              &pointerCoords, mXPrecision, mYPrecision, downTime,
405                                              /* videoFrames */ {});
406                 getListener()->notifyMotion(&releaseArgs);
407             }
408         }
409 
410         NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
411                               displayId, policyFlags, motionEventAction, 0, 0, metaState,
412                               currentButtonState, MotionClassification::NONE,
413                               AMOTION_EVENT_EDGE_FLAG_NONE,
414                               /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
415                               mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
416         getListener()->notifyMotion(&args);
417 
418         if (buttonsPressed) {
419             BitSet32 pressed(buttonsPressed);
420             while (!pressed.isEmpty()) {
421                 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
422                 buttonState |= actionButton;
423                 NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
424                                            mSource, displayId, policyFlags,
425                                            AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
426                                            metaState, buttonState, MotionClassification::NONE,
427                                            AMOTION_EVENT_EDGE_FLAG_NONE,
428                                            /* deviceTimestamp */ 0, 1, &pointerProperties,
429                                            &pointerCoords, mXPrecision, mYPrecision, downTime,
430                                            /* videoFrames */ {});
431                 getListener()->notifyMotion(&pressArgs);
432             }
433         }
434 
435         ALOG_ASSERT(buttonState == currentButtonState);
436 
437         // Send hover move after UP to tell the application that the mouse is hovering now.
438         if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
439             NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
440                                        displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
441                                        0, metaState, currentButtonState, MotionClassification::NONE,
442                                        AMOTION_EVENT_EDGE_FLAG_NONE,
443                                        /* deviceTimestamp */ 0, 1, &pointerProperties,
444                                        &pointerCoords, mXPrecision, mYPrecision, downTime,
445                                        /* videoFrames */ {});
446             getListener()->notifyMotion(&hoverArgs);
447         }
448 
449         // Send scroll events.
450         if (scrolled) {
451             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
452             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
453 
454             NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
455                                         mSource, displayId, policyFlags,
456                                         AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
457                                         currentButtonState, MotionClassification::NONE,
458                                         AMOTION_EVENT_EDGE_FLAG_NONE,
459                                         /* deviceTimestamp */ 0, 1, &pointerProperties,
460                                         &pointerCoords, mXPrecision, mYPrecision, downTime,
461                                         /* videoFrames */ {});
462             getListener()->notifyMotion(&scrollArgs);
463         }
464     }
465 
466     // Synthesize key up from buttons if needed.
467     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
468                          displayId, policyFlags, lastButtonState, currentButtonState);
469 
470     mCursorMotionAccumulator.finishSync();
471     mCursorScrollAccumulator.finishSync();
472 }
473 
getScanCodeState(uint32_t sourceMask,int32_t scanCode)474 int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
475     if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
476         return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
477     } else {
478         return AKEY_STATE_UNKNOWN;
479     }
480 }
481 
fadePointer()482 void CursorInputMapper::fadePointer() {
483     if (mPointerController != nullptr) {
484         mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
485     }
486 }
487 
getAssociatedDisplay()488 std::optional<int32_t> CursorInputMapper::getAssociatedDisplay() {
489     if (mParameters.hasAssociatedDisplay) {
490         if (mParameters.mode == Parameters::MODE_POINTER) {
491             return std::make_optional(mPointerController->getDisplayId());
492         } else {
493             // If the device is orientationAware and not a mouse,
494             // it expects to dispatch events to any display
495             return std::make_optional(ADISPLAY_ID_NONE);
496         }
497     }
498     return std::nullopt;
499 }
500 
501 } // namespace android
502