• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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