1 /*
2 * Copyright (C) 2012 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 #define LOG_TAG "VelocityControl"
18
19 // Log debug messages about acceleration.
20 static constexpr bool DEBUG_ACCELERATION = false;
21
22 #include <math.h>
23 #include <limits.h>
24
25 #include <android-base/logging.h>
26 #include <input/VelocityControl.h>
27 #include <utils/BitSet.h>
28 #include <utils/Timers.h>
29
30 namespace android {
31
32 // --- VelocityControl ---
33
34 const nsecs_t VelocityControl::STOP_TIME;
35
VelocityControl()36 VelocityControl::VelocityControl() {
37 reset();
38 }
39
reset()40 void VelocityControl::reset() {
41 mLastMovementTime = LLONG_MIN;
42 mRawPositionX = 0;
43 mRawPositionY = 0;
44 mVelocityTracker.clear();
45 }
46
move(nsecs_t eventTime,float * deltaX,float * deltaY)47 void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) {
48 if ((deltaX == nullptr || *deltaX == 0) && (deltaY == nullptr || *deltaY == 0)) {
49 return;
50 }
51 if (eventTime >= mLastMovementTime + STOP_TIME) {
52 ALOGD_IF(DEBUG_ACCELERATION && mLastMovementTime != LLONG_MIN,
53 "VelocityControl: stopped, last movement was %0.3fms ago",
54 (eventTime - mLastMovementTime) * 0.000001f);
55 reset();
56 }
57
58 mLastMovementTime = eventTime;
59 if (deltaX) {
60 mRawPositionX += *deltaX;
61 }
62 if (deltaY) {
63 mRawPositionY += *deltaY;
64 }
65 mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_X, mRawPositionX);
66 mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_Y, mRawPositionY);
67 scaleDeltas(deltaX, deltaY);
68 }
69
70 // --- SimpleVelocityControl ---
71
getParameters() const72 const VelocityControlParameters& SimpleVelocityControl::getParameters() const {
73 return mParameters;
74 }
75
setParameters(const VelocityControlParameters & parameters)76 void SimpleVelocityControl::setParameters(const VelocityControlParameters& parameters) {
77 mParameters = parameters;
78 reset();
79 }
80
scaleDeltas(float * deltaX,float * deltaY)81 void SimpleVelocityControl::scaleDeltas(float* deltaX, float* deltaY) {
82 std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0);
83 std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0);
84 float scale = mParameters.scale;
85 if (vx.has_value() && vy.has_value()) {
86 float speed = hypotf(*vx, *vy) * scale;
87 if (speed >= mParameters.highThreshold) {
88 // Apply full acceleration above the high speed threshold.
89 scale *= mParameters.acceleration;
90 } else if (speed > mParameters.lowThreshold) {
91 // Linearly interpolate the acceleration to apply between the low and high
92 // speed thresholds.
93 scale *= 1 +
94 (speed - mParameters.lowThreshold) /
95 (mParameters.highThreshold - mParameters.lowThreshold) *
96 (mParameters.acceleration - 1);
97 }
98
99 ALOGD_IF(DEBUG_ACCELERATION,
100 "SimpleVelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): "
101 "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
102 mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
103 mParameters.acceleration, *vx, *vy, speed, scale / mParameters.scale);
104
105 } else {
106 ALOGD_IF(DEBUG_ACCELERATION,
107 "SimpleVelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity",
108 mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
109 mParameters.acceleration);
110 }
111
112 if (deltaX != nullptr) {
113 *deltaX *= scale;
114 }
115 if (deltaY != nullptr) {
116 *deltaY *= scale;
117 }
118 }
119
120 // --- CurvedVelocityControl ---
121
122 namespace {
123
124 /**
125 * The resolution that we assume a mouse to have, in counts per inch.
126 *
127 * Mouse resolutions vary wildly, but 800 CPI is probably the most common. There should be enough
128 * range in the available sensitivity settings to accommodate users of mice with other resolutions.
129 */
130 constexpr int32_t MOUSE_CPI = 800;
131
countsToMm(float counts)132 float countsToMm(float counts) {
133 return counts / MOUSE_CPI * 25.4;
134 }
135
136 } // namespace
137
CurvedVelocityControl()138 CurvedVelocityControl::CurvedVelocityControl()
139 : mCurveSegments(createAccelerationCurveForPointerSensitivity(0)) {}
140
setCurve(const std::vector<AccelerationCurveSegment> & curve)141 void CurvedVelocityControl::setCurve(const std::vector<AccelerationCurveSegment>& curve) {
142 mCurveSegments = curve;
143 }
144
setAccelerationEnabled(bool enabled)145 void CurvedVelocityControl::setAccelerationEnabled(bool enabled) {
146 mAccelerationEnabled = enabled;
147 }
148
scaleDeltas(float * deltaX,float * deltaY)149 void CurvedVelocityControl::scaleDeltas(float* deltaX, float* deltaY) {
150 if (!mAccelerationEnabled) {
151 ALOGD_IF(DEBUG_ACCELERATION, "CurvedVelocityControl: acceleration disabled");
152 return;
153 }
154
155 std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0);
156 std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0);
157
158 float ratio;
159 if (vx.has_value() && vy.has_value()) {
160 float vxMmPerS = countsToMm(*vx);
161 float vyMmPerS = countsToMm(*vy);
162 float speedMmPerS = sqrtf(vxMmPerS * vxMmPerS + vyMmPerS * vyMmPerS);
163
164 const AccelerationCurveSegment& seg = segmentForSpeed(speedMmPerS);
165 ratio = seg.baseGain + seg.reciprocal / speedMmPerS;
166 ALOGD_IF(DEBUG_ACCELERATION,
167 "CurvedVelocityControl: velocities (%0.3f, %0.3f) → speed %0.3f → ratio %0.3f",
168 vxMmPerS, vyMmPerS, speedMmPerS, ratio);
169 } else {
170 // We don't have enough data to compute a velocity yet. This happens early in the movement,
171 // when the speed is presumably low, so use the base gain of the first segment of the curve.
172 // (This would behave oddly for curves with a reciprocal term on the first segment, but we
173 // don't have any of those, and they'd be very strange at velocities close to zero anyway.)
174 ratio = mCurveSegments[0].baseGain;
175 ALOGD_IF(DEBUG_ACCELERATION,
176 "CurvedVelocityControl: unknown velocity, using base gain of first segment (%.3f)",
177 ratio);
178 }
179
180 if (deltaX != nullptr) {
181 *deltaX *= ratio;
182 }
183 if (deltaY != nullptr) {
184 *deltaY *= ratio;
185 }
186 }
187
segmentForSpeed(float speedMmPerS)188 const AccelerationCurveSegment& CurvedVelocityControl::segmentForSpeed(float speedMmPerS) {
189 for (const AccelerationCurveSegment& seg : mCurveSegments) {
190 if (speedMmPerS <= seg.maxPointerSpeedMmPerS) {
191 return seg;
192 }
193 }
194 ALOGE("CurvedVelocityControl: No segment found for speed %.3f; last segment should always have "
195 "a max speed of infinity.",
196 speedMmPerS);
197 return mCurveSegments.back();
198 }
199
200 } // namespace android
201