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 "RotaryEncoderInputMapper.h"
20 
21 #include "CursorScrollAccumulator.h"
22 
23 namespace android {
24 
RotaryEncoderInputMapper(InputDevice * device)25 RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device)
26       : InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) {
27     mSource = AINPUT_SOURCE_ROTARY_ENCODER;
28 }
29 
~RotaryEncoderInputMapper()30 RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {}
31 
getSources()32 uint32_t RotaryEncoderInputMapper::getSources() {
33     return mSource;
34 }
35 
populateDeviceInfo(InputDeviceInfo * info)36 void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
37     InputMapper::populateDeviceInfo(info);
38 
39     if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) {
40         float res = 0.0f;
41         if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) {
42             ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
43         }
44         if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"),
45                                                         mScalingFactor)) {
46             ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
47                   "default to 1.0!\n");
48             mScalingFactor = 1.0f;
49         }
50         info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
51                              res * mScalingFactor);
52     }
53 }
54 
dump(std::string & dump)55 void RotaryEncoderInputMapper::dump(std::string& dump) {
56     dump += INDENT2 "Rotary Encoder Input Mapper:\n";
57     dump += StringPrintf(INDENT3 "HaveWheel: %s\n",
58                          toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel()));
59 }
60 
configure(nsecs_t when,const InputReaderConfiguration * config,uint32_t changes)61 void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
62                                          uint32_t changes) {
63     InputMapper::configure(when, config, changes);
64     if (!changes) {
65         mRotaryEncoderScrollAccumulator.configure(getDevice());
66     }
67     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
68         std::optional<DisplayViewport> internalViewport =
69                 config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
70         if (internalViewport) {
71             mOrientation = internalViewport->orientation;
72         } else {
73             mOrientation = DISPLAY_ORIENTATION_0;
74         }
75     }
76 }
77 
reset(nsecs_t when)78 void RotaryEncoderInputMapper::reset(nsecs_t when) {
79     mRotaryEncoderScrollAccumulator.reset(getDevice());
80 
81     InputMapper::reset(when);
82 }
83 
process(const RawEvent * rawEvent)84 void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
85     mRotaryEncoderScrollAccumulator.process(rawEvent);
86 
87     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
88         sync(rawEvent->when);
89     }
90 }
91 
sync(nsecs_t when)92 void RotaryEncoderInputMapper::sync(nsecs_t when) {
93     PointerCoords pointerCoords;
94     pointerCoords.clear();
95 
96     PointerProperties pointerProperties;
97     pointerProperties.clear();
98     pointerProperties.id = 0;
99     pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
100 
101     float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
102     bool scrolled = scroll != 0;
103 
104     // This is not a pointer, so it's not associated with a display.
105     int32_t displayId = ADISPLAY_ID_NONE;
106 
107     // Moving the rotary encoder should wake the device (if specified).
108     uint32_t policyFlags = 0;
109     if (scrolled && getDevice()->isExternal()) {
110         policyFlags |= POLICY_FLAG_WAKE;
111     }
112 
113     if (mOrientation == DISPLAY_ORIENTATION_180) {
114         scroll = -scroll;
115     }
116 
117     // Send motion event.
118     if (scrolled) {
119         int32_t metaState = mContext->getGlobalMetaState();
120         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
121 
122         NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
123                                     displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
124                                     metaState, /* buttonState */ 0, MotionClassification::NONE,
125                                     AMOTION_EVENT_EDGE_FLAG_NONE,
126                                     /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
127                                     0, 0, 0, /* videoFrames */ {});
128         getListener()->notifyMotion(&scrollArgs);
129     }
130 
131     mRotaryEncoderScrollAccumulator.finishSync();
132 }
133 
134 } // namespace android
135