diff --git a/include/ui/Input.h b/include/ui/Input.h index fb6152e9f..d603441a4 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -627,6 +627,87 @@ private: int32_t mActivePointerId; }; + +/* + * Specifies parameters that govern pointer or wheel acceleration. + */ +struct VelocityControlParameters { + // A scale factor that is multiplied with the raw velocity deltas + // prior to applying any other velocity control factors. The scale + // factor should be used to adapt the input device resolution + // (eg. counts per inch) to the output device resolution (eg. pixels per inch). + // + // Must be a positive value. + // Default is 1.0 (no scaling). + float scale; + + // The scaled speed at which acceleration begins to be applied. + // This value establishes the upper bound of a low speed regime for + // small precise motions that are performed without any acceleration. + // + // Must be a non-negative value. + // Default is 0.0 (no low threshold). + float lowThreshold; + + // The scaled speed at which maximum acceleration is applied. + // The difference between highThreshold and lowThreshold controls + // the range of speeds over which the acceleration factor is interpolated. + // The wider the range, the smoother the acceleration. + // + // Must be a non-negative value greater than or equal to lowThreshold. + // Default is 0.0 (no high threshold). + float highThreshold; + + // The acceleration factor. + // When the speed is above the low speed threshold, the velocity will scaled + // by an interpolated value between 1.0 and this amount. + // + // Must be a positive greater than or equal to 1.0. + // Default is 1.0 (no acceleration). + float acceleration; + + VelocityControlParameters() : + scale(1.0f), lowThreshold(0.0f), highThreshold(0.0f), acceleration(1.0f) { + } + + VelocityControlParameters(float scale, float lowThreshold, + float highThreshold, float acceleration) : + scale(scale), lowThreshold(lowThreshold), + highThreshold(highThreshold), acceleration(acceleration) { + } +}; + +/* + * Implements mouse pointer and wheel speed control and acceleration. + */ +class VelocityControl { +public: + VelocityControl(); + + /* Sets the various parameters. */ + void setParameters(const VelocityControlParameters& parameters); + + /* Resets the current movement counters to zero. + * This has the effect of nullifying any acceleration. */ + void reset(); + + /* Translates a raw movement delta into an appropriately + * scaled / accelerated delta based on the current velocity. */ + void move(nsecs_t eventTime, float* deltaX, float* deltaY); + +private: + // If no movements are received within this amount of time, + // we assume the movement has stopped and reset the movement counters. + static const nsecs_t STOP_TIME = 500 * 1000000; // 500 ms + + VelocityControlParameters mParameters; + + nsecs_t mLastMovementTime; + VelocityTracker::Position mRawPosition; + VelocityTracker mVelocityTracker; +}; + + /* * Describes the characteristics and capabilities of an input device. */ diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index 684c332d1..50b75d504 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -13,6 +13,10 @@ // Log debug messages about velocity tracking. #define DEBUG_VELOCITY 0 +// Log debug messages about acceleration. +#define DEBUG_ACCELERATION 0 + + #include #include #include @@ -20,6 +24,7 @@ #include #include +#include #ifdef HAVE_ANDROID_OS #include @@ -670,6 +675,11 @@ bool MotionEvent::isTouchEvent(int32_t source, int32_t action) { // --- VelocityTracker --- +const uint32_t VelocityTracker::HISTORY_SIZE; +const nsecs_t VelocityTracker::MAX_AGE; +const nsecs_t VelocityTracker::MIN_WINDOW; +const nsecs_t VelocityTracker::MIN_DURATION; + VelocityTracker::VelocityTracker() { clear(); } @@ -879,6 +889,85 @@ bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const } +// --- VelocityControl --- + +const nsecs_t VelocityControl::STOP_TIME; + +VelocityControl::VelocityControl() { + reset(); +} + +void VelocityControl::setParameters(const VelocityControlParameters& parameters) { + mParameters = parameters; + reset(); +} + +void VelocityControl::reset() { + mLastMovementTime = LLONG_MIN; + mRawPosition.x = 0; + mRawPosition.y = 0; + mVelocityTracker.clear(); +} + +void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { + if ((deltaX && *deltaX) || (deltaY && *deltaY)) { + if (eventTime >= mLastMovementTime + STOP_TIME) { +#if DEBUG_ACCELERATION + LOGD("VelocityControl: stopped, last movement was %0.3fms ago", + (eventTime - mLastMovementTime) * 0.000001f); +#endif + reset(); + } + + mLastMovementTime = eventTime; + if (deltaX) { + mRawPosition.x += *deltaX; + } + if (deltaY) { + mRawPosition.y += *deltaY; + } + mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition); + + float vx, vy; + float scale = mParameters.scale; + if (mVelocityTracker.getVelocity(0, &vx, &vy)) { + float speed = hypotf(vx, vy) * scale; + if (speed >= mParameters.highThreshold) { + // Apply full acceleration above the high speed threshold. + scale *= mParameters.acceleration; + } else if (speed > mParameters.lowThreshold) { + // Linearly interpolate the acceleration to apply between the low and high + // speed thresholds. + scale *= 1 + (speed - mParameters.lowThreshold) + / (mParameters.highThreshold - mParameters.lowThreshold) + * (mParameters.acceleration - 1); + } + +#if DEBUG_ACCELERATION + LOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): " + "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", + mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, + mParameters.acceleration, + vx, vy, speed, scale / mParameters.scale); +#endif + } else { +#if DEBUG_ACCELERATION + LOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity", + mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, + mParameters.acceleration); +#endif + } + + if (deltaX) { + *deltaX *= scale; + } + if (deltaY) { + *deltaY *= scale; + } + } +} + + // --- InputDeviceInfo --- InputDeviceInfo::InputDeviceInfo() {