Add support to disable mouse acceleration
Bug: 382276176
Test: atest CursorInputMapperUnitTest
Flag: com.android.hardware.input.pointer_acceleration
Change-Id: I431ac6fa3b918b1d292528f614e06532837c4a82
diff --git a/include/input/AccelerationCurve.h b/include/input/AccelerationCurve.h
index 0cf648a..8a4a5d4 100644
--- a/include/input/AccelerationCurve.h
+++ b/include/input/AccelerationCurve.h
@@ -46,4 +46,15 @@
std::vector<AccelerationCurveSegment> createAccelerationCurveForPointerSensitivity(
int32_t sensitivity);
+/*
+ * Creates a flat acceleration curve for disabling pointer acceleration.
+ *
+ * This method generates a single AccelerationCurveSegment with specific values
+ * to effectively disable acceleration for both mice and touchpads.
+ * A flat acceleration curve ensures a constant gain, meaning that the output
+ * velocity is directly proportional to the input velocity, resulting in
+ * a 1:1 movement ratio between the input device and the on-screen pointer.
+ */
+std::vector<AccelerationCurveSegment> createFlatAccelerationCurve(int32_t sensitivity);
+
} // namespace android
diff --git a/libs/input/AccelerationCurve.cpp b/libs/input/AccelerationCurve.cpp
index 0a92a71..0b47f3e 100644
--- a/libs/input/AccelerationCurve.cpp
+++ b/libs/input/AccelerationCurve.cpp
@@ -40,6 +40,18 @@
constexpr std::array<double, 15> kSensitivityFactors = {1, 2, 4, 6, 7, 8, 9, 10,
11, 12, 13, 14, 16, 18, 20};
+// Calculates the base gain for a given pointer sensitivity value.
+//
+// The base gain is a scaling factor that is applied to the pointer movement.
+// Higher sensitivity values result in larger base gains, which in turn result
+// in faster pointer movements.
+//
+// The base gain is calculated using a linear mapping function that maps the
+// sensitivity range [-7, 7] to a base gain range [0.5, 2.0].
+double calculateBaseGain(int32_t sensitivity) {
+ return 0.5 + (sensitivity + 7) * (2.0 - 0.5) / (7 + 7);
+}
+
} // namespace
std::vector<AccelerationCurveSegment> createAccelerationCurveForPointerSensitivity(
@@ -60,4 +72,13 @@
return output;
}
+std::vector<AccelerationCurveSegment> createFlatAccelerationCurve(int32_t sensitivity) {
+ LOG_ALWAYS_FATAL_IF(sensitivity < -7 || sensitivity > 7, "Invalid pointer sensitivity value");
+ std::vector<AccelerationCurveSegment> output = {
+ AccelerationCurveSegment{std::numeric_limits<double>::infinity(),
+ calculateBaseGain(sensitivity),
+ /* reciprocal = */ 0}};
+ return output;
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 42b269e..404a509 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -139,9 +139,17 @@
// The mouse pointer speed, as a number from -7 (slowest) to 7 (fastest).
int32_t mousePointerSpeed;
- // Displays on which an acceleration curve shouldn't be applied for pointer movements from mice.
+ // Displays on which all pointer scaling, including linear scaling based on the
+ // user's pointer speed setting, should be disabled for mice. This differs from
+ // disabling acceleration via the 'mousePointerAccelerationEnabled' setting, where
+ // the pointer speed setting still influences the scaling factor.
std::set<ui::LogicalDisplayId> displaysWithMousePointerAccelerationDisabled;
+ // True if the connected mouse should exhibit pointer acceleration. If false,
+ // a flat acceleration curve (linear scaling) is used, but the user's pointer
+ // speed setting still affects the scaling factor.
+ bool mousePointerAccelerationEnabled;
+
// Velocity control parameters for touchpad pointer movements on the old touchpad stack (based
// on TouchInputMapper).
//
@@ -275,6 +283,7 @@
defaultPointerDisplayId(ui::LogicalDisplayId::DEFAULT),
mousePointerSpeed(0),
displaysWithMousePointerAccelerationDisabled(),
+ mousePointerAccelerationEnabled(true),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f,
static_cast<float>(
android::os::IInputConstants::
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index b33659c..e9f17e7 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -481,15 +481,21 @@
mPointerVelocityControl.setAccelerationEnabled(false);
mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
- } else {
- mPointerVelocityControl.setAccelerationEnabled(
- config.displaysWithMousePointerAccelerationDisabled.count(
- mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0);
- mPointerVelocityControl.setCurve(
- createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed));
- mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters);
- mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters);
+ return;
}
+
+ bool disableAllScaling = config.displaysWithMousePointerAccelerationDisabled.count(
+ mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) != 0;
+
+ mPointerVelocityControl.setAccelerationEnabled(!disableAllScaling);
+
+ mPointerVelocityControl.setCurve(
+ config.mousePointerAccelerationEnabled
+ ? createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed)
+ : createFlatAccelerationCurve(config.mousePointerSpeed));
+
+ mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters);
+ mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters);
}
void CursorInputMapper::configureOnChangeDisplayInfo(const InputReaderConfiguration& config) {
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index d4e8fdf..a31c4e9 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -27,6 +27,7 @@
#include <com_android_input_flags.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <input/AccelerationCurve.h>
#include <input/DisplayViewport.h>
#include <input/InputEventLabels.h>
#include <linux/input-event-codes.h>
@@ -1028,6 +1029,34 @@
WithCoords(0.0f, 0.0f)))));
}
+TEST_F(CursorInputMapperUnitTest, PointerAccelerationDisabled) {
+ mReaderConfiguration.mousePointerAccelerationEnabled = false;
+ mReaderConfiguration.mousePointerSpeed = 3;
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ std::list<NotifyArgs> reconfigureArgs;
+
+ reconfigureArgs += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::POINTER_SPEED);
+
+ std::vector<AccelerationCurveSegment> curve =
+ createFlatAccelerationCurve(mReaderConfiguration.mousePointerSpeed);
+ double baseGain = curve[0].baseGain;
+
+ std::list<NotifyArgs> motionArgs;
+ motionArgs += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ motionArgs += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ motionArgs += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ const float expectedRelX = 10 * baseGain;
+ const float expectedRelY = 20 * baseGain;
+ ASSERT_THAT(motionArgs,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(HOVER_MOVE),
+ WithRelativeMotion(expectedRelX, expectedRelY)))));
+}
+
TEST_F(CursorInputMapperUnitTest, ConfigureAccelerationWithAssociatedViewport) {
mPropertyMap.addProperty("cursor.mode", "pointer");
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index 7078e49..7fb8895 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -32,6 +32,17 @@
namespace android {
+namespace {
+
+template <typename T>
+static bool valuesMatch(T value1, T value2) {
+ if constexpr (std::is_floating_point_v<T>) {
+ return std::abs(value1 - value2) < EPSILON;
+ } else {
+ return value1 == value2;
+ }
+}
+
struct PointF {
float x;
float y;
@@ -42,6 +53,8 @@
return std::string("(") + std::to_string(p.x) + ", " + std::to_string(p.y) + ")";
}
+} // namespace
+
/// Source
class WithSourceMatcher {
public:
@@ -706,8 +719,8 @@
}
const PointerCoords& coords = event.pointerCoords[mPointerIndex];
- bool matches = mRelX == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X) &&
- mRelY == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ bool matches = valuesMatch(mRelX, coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X)) &&
+ valuesMatch(mRelY, coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
if (!matches) {
*os << "expected relative motion (" << mRelX << ", " << mRelY << ") at pointer index "
<< mPointerIndex << ", but got ("