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 // clang-format off
18 #include "../Macros.h"
19 // clang-format on
20 
21 #include "RotaryEncoderInputMapper.h"
22 
23 #include <utils/Timers.h>
24 #include <optional>
25 
26 #include "CursorScrollAccumulator.h"
27 
28 namespace android {
29 
RotaryEncoderInputMapper(InputDeviceContext & deviceContext,const InputReaderConfiguration & readerConfig)30 RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
31                                                    const InputReaderConfiguration& readerConfig)
32       : InputMapper(deviceContext, readerConfig), mOrientation(ui::ROTATION_0) {
33     mSource = AINPUT_SOURCE_ROTARY_ENCODER;
34 }
35 
~RotaryEncoderInputMapper()36 RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {}
37 
getSources() const38 uint32_t RotaryEncoderInputMapper::getSources() const {
39     return mSource;
40 }
41 
populateDeviceInfo(InputDeviceInfo & info)42 void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
43     InputMapper::populateDeviceInfo(info);
44 
45     if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) {
46         const PropertyMap& config = getDeviceContext().getConfiguration();
47         std::optional<float> res = config.getFloat("device.res");
48         if (!res.has_value()) {
49             ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
50         }
51         std::optional<float> scalingFactor = config.getFloat("device.scalingFactor");
52         if (!scalingFactor.has_value()) {
53             ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
54                   "default to 1.0!\n");
55         }
56         mScalingFactor = scalingFactor.value_or(1.0f);
57         info.addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
58                             res.value_or(0.0f) * mScalingFactor);
59     }
60 }
61 
dump(std::string & dump)62 void RotaryEncoderInputMapper::dump(std::string& dump) {
63     dump += INDENT2 "Rotary Encoder Input Mapper:\n";
64     dump += StringPrintf(INDENT3 "HaveWheel: %s\n",
65                          toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel()));
66     dump += StringPrintf(INDENT3 "HaveSlopController: %s\n", toString(mSlopController != nullptr));
67 }
68 
reconfigure(nsecs_t when,const InputReaderConfiguration & config,ConfigurationChanges changes)69 std::list<NotifyArgs> RotaryEncoderInputMapper::reconfigure(nsecs_t when,
70                                                             const InputReaderConfiguration& config,
71                                                             ConfigurationChanges changes) {
72     std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
73     if (!changes.any()) {
74         mRotaryEncoderScrollAccumulator.configure(getDeviceContext());
75 
76         const PropertyMap& propertyMap = getDeviceContext().getConfiguration();
77         float slopThreshold = propertyMap.getInt("rotary_encoder.slop_threshold").value_or(0);
78         int32_t slopDurationNs = milliseconds_to_nanoseconds(
79                 propertyMap.getInt("rotary_encoder.slop_duration_ms").value_or(0));
80         if (slopThreshold > 0 && slopDurationNs > 0) {
81             mSlopController = std::make_unique<SlopController>(slopThreshold, slopDurationNs);
82         } else {
83             mSlopController = nullptr;
84         }
85     }
86     if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
87         std::optional<DisplayViewport> internalViewport =
88                 config.getDisplayViewportByType(ViewportType::INTERNAL);
89         if (internalViewport) {
90             mOrientation = internalViewport->orientation;
91         } else {
92             mOrientation = ui::ROTATION_0;
93         }
94     }
95     return out;
96 }
97 
reset(nsecs_t when)98 std::list<NotifyArgs> RotaryEncoderInputMapper::reset(nsecs_t when) {
99     mRotaryEncoderScrollAccumulator.reset(getDeviceContext());
100 
101     return InputMapper::reset(when);
102 }
103 
process(const RawEvent & rawEvent)104 std::list<NotifyArgs> RotaryEncoderInputMapper::process(const RawEvent& rawEvent) {
105     std::list<NotifyArgs> out;
106     mRotaryEncoderScrollAccumulator.process(rawEvent);
107 
108     if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
109         out += sync(rawEvent.when, rawEvent.readTime);
110     }
111     return out;
112 }
113 
sync(nsecs_t when,nsecs_t readTime)114 std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
115     std::list<NotifyArgs> out;
116 
117     float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
118     if (mSlopController) {
119         scroll = mSlopController->consumeEvent(when, scroll);
120     }
121 
122     bool scrolled = scroll != 0;
123 
124     // Send motion event.
125     if (scrolled) {
126         int32_t metaState = getContext()->getGlobalMetaState();
127         // This is not a pointer, so it's not associated with a display.
128         ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID;
129 
130         if (mOrientation == ui::ROTATION_180) {
131             scroll = -scroll;
132         }
133 
134         PointerCoords pointerCoords;
135         pointerCoords.clear();
136         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
137 
138         PointerProperties pointerProperties;
139         pointerProperties.clear();
140         pointerProperties.id = 0;
141         pointerProperties.toolType = ToolType::UNKNOWN;
142 
143         uint32_t policyFlags = 0;
144         if (getDeviceContext().isExternal()) {
145             policyFlags |= POLICY_FLAG_WAKE;
146         }
147 
148         out.push_back(
149                 NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
150                                  displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
151                                  metaState, /*buttonState=*/0, MotionClassification::NONE,
152                                  AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
153                                  &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
154                                  AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /*videoFrames=*/{}));
155     }
156 
157     mRotaryEncoderScrollAccumulator.finishSync();
158     return out;
159 }
160 
161 } // namespace android
162