blob: edd31e91d36960b1a75285fc4051227ed757419b [file] [log] [blame]
Jeff Brown5912f952013-07-01 19:10:31 -07001/*
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"
Jeff Brown5912f952013-07-01 19:10:31 -070018
19// Log debug messages about acceleration.
dingxiaobo70dc8792021-08-23 11:25:30 +080020static constexpr bool DEBUG_ACCELERATION = false;
Jeff Brown5912f952013-07-01 19:10:31 -070021
22#include <math.h>
23#include <limits.h>
24
Harry Cuttse78184b2024-01-08 15:54:58 +000025#include <android-base/logging.h>
Jeff Brown5912f952013-07-01 19:10:31 -070026#include <input/VelocityControl.h>
27#include <utils/BitSet.h>
28#include <utils/Timers.h>
29
30namespace android {
31
32// --- VelocityControl ---
33
34const nsecs_t VelocityControl::STOP_TIME;
35
36VelocityControl::VelocityControl() {
37 reset();
38}
39
Jeff Brown5912f952013-07-01 19:10:31 -070040void VelocityControl::reset() {
41 mLastMovementTime = LLONG_MIN;
Yeabkal Wubshit384ab0f2022-09-09 16:39:18 +000042 mRawPositionX = 0;
43 mRawPositionY = 0;
Jeff Brown5912f952013-07-01 19:10:31 -070044 mVelocityTracker.clear();
45}
46
47void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) {
Harry Cuttse78184b2024-01-08 15:54:58 +000048 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
72const VelocityControlParameters& SimpleVelocityControl::getParameters() const {
73 return mParameters;
74}
75
76void SimpleVelocityControl::setParameters(const VelocityControlParameters& parameters) {
77 mParameters = parameters;
78 reset();
79}
80
81void 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);
Jeff Brown5912f952013-07-01 19:10:31 -070097 }
98
Harry Cuttse78184b2024-01-08 15:54:58 +000099 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);
Jeff Brown5912f952013-07-01 19:10:31 -0700104
Harry Cuttse78184b2024-01-08 15:54:58 +0000105 } 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 }
Jeff Brown5912f952013-07-01 19:10:31 -0700111
Harry Cuttse78184b2024-01-08 15:54:58 +0000112 if (deltaX != nullptr) {
113 *deltaX *= scale;
114 }
115 if (deltaY != nullptr) {
116 *deltaY *= scale;
117 }
118}
dingxiaobo70dc8792021-08-23 11:25:30 +0800119
Harry Cuttse78184b2024-01-08 15:54:58 +0000120// --- CurvedVelocityControl ---
Jeff Brown5912f952013-07-01 19:10:31 -0700121
Harry Cuttse78184b2024-01-08 15:54:58 +0000122namespace {
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 */
130constexpr int32_t MOUSE_CPI = 800;
131
132float countsToMm(float counts) {
133 return counts / MOUSE_CPI * 25.4;
134}
135
136} // namespace
137
138CurvedVelocityControl::CurvedVelocityControl()
139 : mCurveSegments(createAccelerationCurveForPointerSensitivity(0)) {}
140
141void CurvedVelocityControl::setCurve(const std::vector<AccelerationCurveSegment>& curve) {
142 mCurveSegments = curve;
143}
144
145void CurvedVelocityControl::setAccelerationEnabled(bool enabled) {
146 mAccelerationEnabled = enabled;
147}
148
149void 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
188const AccelerationCurveSegment& CurvedVelocityControl::segmentForSpeed(float speedMmPerS) {
189 for (const AccelerationCurveSegment& seg : mCurveSegments) {
190 if (speedMmPerS <= seg.maxPointerSpeedMmPerS) {
191 return seg;
Jeff Brown5912f952013-07-01 19:10:31 -0700192 }
193 }
Harry Cuttse78184b2024-01-08 15:54:58 +0000194 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();
Jeff Brown5912f952013-07-01 19:10:31 -0700198}
199
200} // namespace android