diff options
author | Jeff Brown <jeffbrown@google.com> | 2011-04-19 15:37:32 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-04-19 15:37:32 -0700 |
commit | 3f14891fc9e764d97de07b109f066aedfff90c2e (patch) | |
tree | 7d026487ee719489cea2c1237901ef6cf325efe2 /services | |
parent | 05d30b14843bf7cf252873f4d0d39706878b28c2 (diff) | |
parent | 2352b978a3c94cd88f41d0d908f961333fdac1e9 (diff) | |
download | frameworks_base-3f14891fc9e764d97de07b109f066aedfff90c2e.zip frameworks_base-3f14891fc9e764d97de07b109f066aedfff90c2e.tar.gz frameworks_base-3f14891fc9e764d97de07b109f066aedfff90c2e.tar.bz2 |
Merge "Initial checkin of spot presentation for touchpad gestures."
Diffstat (limited to 'services')
-rw-r--r-- | services/input/Android.mk | 1 | ||||
-rw-r--r-- | services/input/InputReader.cpp | 762 | ||||
-rw-r--r-- | services/input/InputReader.h | 87 | ||||
-rw-r--r-- | services/input/PointerController.cpp | 373 | ||||
-rw-r--r-- | services/input/PointerController.h | 187 | ||||
-rw-r--r-- | services/input/SpotController.cpp | 45 | ||||
-rw-r--r-- | services/input/SpotController.h | 71 | ||||
-rw-r--r-- | services/input/SpriteController.cpp | 162 | ||||
-rw-r--r-- | services/input/SpriteController.h | 100 | ||||
-rw-r--r-- | services/input/tests/InputReader_test.cpp | 14 | ||||
-rw-r--r-- | services/java/com/android/server/wm/InputManager.java | 57 | ||||
-rw-r--r-- | services/jni/com_android_server_InputManager.cpp | 126 |
12 files changed, 1262 insertions, 723 deletions
diff --git a/services/input/Android.mk b/services/input/Android.mk index f9f8623f..836c081 100644 --- a/services/input/Android.mk +++ b/services/input/Android.mk @@ -23,7 +23,6 @@ LOCAL_SRC_FILES:= \ InputReader.cpp \ InputWindow.cpp \ PointerController.cpp \ - SpotController.cpp \ SpriteController.cpp LOCAL_SHARED_LIBRARIES := \ diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 6db445e..1bc1bd1 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -36,7 +36,6 @@ // Log debug messages about gesture detection. #define DEBUG_GESTURES 0 - #include "InputReader.h" #include <cutils/log.h> @@ -71,23 +70,47 @@ static const float DRAG_MIN_SWITCH_SPEED = 50.0f; // pixels per second // Tap gesture delay time. // The time between down and up must be less than this to be considered a tap. -static const nsecs_t TAP_INTERVAL = 100 * 1000000; // 100 ms +static const nsecs_t TAP_INTERVAL = 150 * 1000000; // 150 ms // The distance in pixels that the pointer is allowed to move from initial down // to up and still be called a tap. static const float TAP_SLOP = 5.0f; // 5 pixels -// The transition from INDETERMINATE_MULTITOUCH to SWIPE or FREEFORM gesture mode is made when -// all of the pointers have traveled this number of pixels from the start point. -static const float MULTITOUCH_MIN_TRAVEL = 5.0f; +// Time after the first touch points go down to settle on an initial centroid. +// This is intended to be enough time to handle cases where the user puts down two +// fingers at almost but not quite exactly the same time. +static const nsecs_t MULTITOUCH_SETTLE_INTERVAL = 100 * 1000000; // 100ms + +// The transition from PRESS to SWIPE or FREEFORM gesture mode is made when +// both of the pointers are moving at least this fast. +static const float MULTITOUCH_MIN_SPEED = 150.0f; // pixels per second -// The transition from INDETERMINATE_MULTITOUCH to SWIPE gesture mode can only occur when the +// The transition from PRESS to SWIPE gesture mode can only occur when the // cosine of the angle between the two vectors is greater than or equal to than this value // which indicates that the vectors are oriented in the same direction. // When the vectors are oriented in the exactly same direction, the cosine is 1.0. // (In exactly opposite directions, the cosine is -1.0.) static const float SWIPE_TRANSITION_ANGLE_COSINE = 0.5f; // cosine of 45 degrees +// The transition from PRESS to SWIPE gesture mode can only occur when the +// fingers are no more than this far apart relative to the diagonal size of +// the touch pad. For example, a ratio of 0.5 means that the fingers must be +// no more than half the diagonal size of the touch pad apart. +static const float SWIPE_MAX_WIDTH_RATIO = 0.333f; // 1/3 + +// The gesture movement speed factor relative to the size of the display. +// Movement speed applies when the fingers are moving in the same direction. +// Without acceleration, a full swipe of the touch pad diagonal in movement mode +// will cover this portion of the display diagonal. +static const float GESTURE_MOVEMENT_SPEED_RATIO = 0.8f; + +// The gesture zoom speed factor relative to the size of the display. +// Zoom speed applies when the fingers are mostly moving relative to each other +// to execute a scale gesture or similar. +// Without acceleration, a full swipe of the touch pad diagonal in zoom mode +// will cover this portion of the display diagonal. +static const float GESTURE_ZOOM_SPEED_RATIO = 0.3f; + // --- Static Functions --- @@ -112,14 +135,8 @@ inline static float avg(float x, float y) { return (x + y) / 2; } -inline static float pythag(float x, float y) { - return sqrtf(x * x + y * y); -} - -inline static int32_t distanceSquared(int32_t x1, int32_t y1, int32_t x2, int32_t y2) { - int32_t dx = x1 - x2; - int32_t dy = y1 - y2; - return dx * dx + dy * dy; +inline static float distance(float x1, float y1, float x2, float y2) { + return hypotf(x1 - x2, y1 - y2); } inline static int32_t signExtendNybble(int32_t value) { @@ -224,6 +241,33 @@ static int32_t calculateEdgeFlagsUsingPointerBounds( return edgeFlags; } +static void clampPositionUsingPointerBounds( + const sp<PointerControllerInterface>& pointerController, float* x, float* y) { + float minX, minY, maxX, maxY; + if (pointerController->getBounds(&minX, &minY, &maxX, &maxY)) { + if (*x < minX) { + *x = minX; + } else if (*x > maxX) { + *x = maxX; + } + if (*y < minY) { + *y = minY; + } else if (*y > maxY) { + *y = maxY; + } + } +} + +static float calculateCommonVector(float a, float b) { + if (a > 0 && b > 0) { + return a < b ? a : b; + } else if (a < 0 && b < 0) { + return a > b ? a : b; + } else { + return 0; + } +} + // --- InputReader --- @@ -1553,10 +1597,32 @@ void CursorInputMapper::sync(nsecs_t when) { motionEventEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; + if (mHaveVWheel && (fields & Accumulator::FIELD_REL_WHEEL)) { + vscroll = mAccumulator.relWheel; + } else { + vscroll = 0; + } + if (mHaveHWheel && (fields & Accumulator::FIELD_REL_HWHEEL)) { + hscroll = mAccumulator.relHWheel; + } else { + hscroll = 0; + } + if (mPointerController != NULL) { - mPointerController->move(deltaX, deltaY); - if (buttonsChanged) { - mPointerController->setButtonState(mLocked.buttonState); + if (deltaX != 0 || deltaY != 0 || vscroll != 0 || hscroll != 0 + || buttonsChanged) { + mPointerController->setPresentation( + PointerControllerInterface::PRESENTATION_POINTER); + + if (deltaX != 0 || deltaY != 0) { + mPointerController->move(deltaX, deltaY); + } + + if (buttonsChanged) { + mPointerController->setButtonState(mLocked.buttonState); + } + + mPointerController->unfade(); } float x, y; @@ -1574,20 +1640,6 @@ void CursorInputMapper::sync(nsecs_t when) { } pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); - - if (mHaveVWheel && (fields & Accumulator::FIELD_REL_WHEEL)) { - vscroll = mAccumulator.relWheel; - } else { - vscroll = 0; - } - if (mHaveHWheel && (fields & Accumulator::FIELD_REL_HWHEEL)) { - hscroll = mAccumulator.relHWheel; - } else { - hscroll = 0; - } - if (hscroll != 0 || vscroll != 0) { - mPointerController->unfade(); - } } // release lock // Moving an external trackball or mouse should wake the device. @@ -1751,8 +1803,8 @@ void TouchInputMapper::dump(String8& dump) { mLocked.pointerGestureXZoomScale); dump.appendFormat(INDENT4 "YZoomScale: %0.3f\n", mLocked.pointerGestureYZoomScale); - dump.appendFormat(INDENT4 "MaxSwipeWidthSquared: %d\n", - mLocked.pointerGestureMaxSwipeWidthSquared); + dump.appendFormat(INDENT4 "MaxSwipeWidth: %f\n", + mLocked.pointerGestureMaxSwipeWidth); } } // release lock } @@ -1825,6 +1877,10 @@ void TouchInputMapper::configureParameters() { mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents(); mParameters.virtualKeyQuietTime = getPolicy()->getVirtualKeyQuietTime(); + // TODO: Make this configurable. + //mParameters.gestureMode = Parameters::GESTURE_MODE_POINTER; + mParameters.gestureMode = Parameters::GESTURE_MODE_SPOTS; + if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { // The device is a cursor device with a touch pad attached. @@ -1983,7 +2039,7 @@ bool TouchInputMapper::configureSurfaceLocked() { mLocked.geometricScale = avg(mLocked.xScale, mLocked.yScale); // Size of diagonal axis. - float diagonalSize = pythag(width, height); + float diagonalSize = hypotf(width, height); // TouchMajor and TouchMinor factors. if (mCalibration.touchSizeCalibration != Calibration::TOUCH_SIZE_CALIBRATION_NONE) { @@ -2178,30 +2234,39 @@ bool TouchInputMapper::configureSurfaceLocked() { if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { int32_t rawWidth = mRawAxes.x.maxValue - mRawAxes.x.minValue + 1; int32_t rawHeight = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1; + float rawDiagonal = hypotf(rawWidth, rawHeight); + float displayDiagonal = hypotf(mLocked.associatedDisplayWidth, + mLocked.associatedDisplayHeight); - // Scale movements such that one whole swipe of the touch pad covers a portion - // of the display along whichever axis of the touch pad is longer. + // Scale movements such that one whole swipe of the touch pad covers a + // given area relative to the diagonal size of the display. // Assume that the touch pad has a square aspect ratio such that movements in // X and Y of the same number of raw units cover the same physical distance. const float scaleFactor = 0.8f; - mLocked.pointerGestureXMovementScale = rawWidth > rawHeight - ? scaleFactor * float(mLocked.associatedDisplayWidth) / rawWidth - : scaleFactor * float(mLocked.associatedDisplayHeight) / rawHeight; + mLocked.pointerGestureXMovementScale = GESTURE_MOVEMENT_SPEED_RATIO + * displayDiagonal / rawDiagonal; mLocked.pointerGestureYMovementScale = mLocked.pointerGestureXMovementScale; // Scale zooms to cover a smaller range of the display than movements do. // This value determines the area around the pointer that is affected by freeform // pointer gestures. - mLocked.pointerGestureXZoomScale = mLocked.pointerGestureXMovementScale * 0.4f; - mLocked.pointerGestureYZoomScale = mLocked.pointerGestureYMovementScale * 0.4f; + mLocked.pointerGestureXZoomScale = GESTURE_ZOOM_SPEED_RATIO + * displayDiagonal / rawDiagonal; + mLocked.pointerGestureYZoomScale = mLocked.pointerGestureXZoomScale; + + // Max width between pointers to detect a swipe gesture is more than some fraction + // of the diagonal axis of the touch pad. Touches that are wider than this are + // translated into freeform gestures. + mLocked.pointerGestureMaxSwipeWidth = SWIPE_MAX_WIDTH_RATIO * rawDiagonal; - // Max width between pointers to detect a swipe gesture is 3/4 of the short - // axis of the touch pad. Touches that are wider than this are translated - // into freeform gestures. - mLocked.pointerGestureMaxSwipeWidthSquared = min(rawWidth, rawHeight) * 3 / 4; - mLocked.pointerGestureMaxSwipeWidthSquared *= - mLocked.pointerGestureMaxSwipeWidthSquared; + // Reset the current pointer gesture. + mPointerGesture.reset(); + + // Remove any current spots. + if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerController->clearSpots(); + } } } @@ -2628,6 +2693,11 @@ void TouchInputMapper::reset() { { // acquire lock AutoMutex _l(mLock); initializeLocked(); + + if (mPointerController != NULL + && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerController->clearSpots(); + } } // release lock InputMapper::reset(); @@ -3070,7 +3140,7 @@ void TouchInputMapper::prepareTouches(int32_t* outEdgeFlags, int32_t c2 = signExtendNybble(in.orientation & 0x0f); if (c1 != 0 || c2 != 0) { orientation = atan2f(c1, c2) * 0.5f; - float scale = 1.0f + pythag(c1, c2) / 16.0f; + float scale = 1.0f + hypotf(c1, c2) / 16.0f; touchMajor *= scale; touchMinor /= scale; toolMajor *= scale; @@ -3155,22 +3225,35 @@ void TouchInputMapper::prepareTouches(int32_t* outEdgeFlags, } void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags) { + // Switch pointer presentation. + mPointerController->setPresentation( + mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS + ? PointerControllerInterface::PRESENTATION_SPOT + : PointerControllerInterface::PRESENTATION_POINTER); + // Update current gesture coordinates. bool cancelPreviousGesture, finishPreviousGesture; preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture); + // Show the pointer if needed. + if (mPointerGesture.currentGestureMode != PointerGesture::NEUTRAL + && mPointerGesture.currentGestureMode != PointerGesture::QUIET) { + mPointerController->unfade(); + } + // Send events! uint32_t metaState = getContext()->getGlobalMetaState(); // Update last coordinates of pointers that have moved so that we observe the new // pointer positions at the same time as other pointers that have just gone up. bool down = mPointerGesture.currentGestureMode == PointerGesture::CLICK_OR_DRAG + || mPointerGesture.currentGestureMode == PointerGesture::PRESS || mPointerGesture.currentGestureMode == PointerGesture::SWIPE || mPointerGesture.currentGestureMode == PointerGesture::FREEFORM; bool moveNeeded = false; if (down && !cancelPreviousGesture && !finishPreviousGesture - && mPointerGesture.lastGesturePointerCount != 0 - && mPointerGesture.currentGesturePointerCount != 0) { + && !mPointerGesture.lastGestureIdBits.isEmpty() + && !mPointerGesture.currentGestureIdBits.isEmpty()) { BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value & mPointerGesture.lastGestureIdBits.value); moveNeeded = updateMovedPointerCoords( @@ -3284,11 +3367,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag // Update state. mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode; if (!down) { - mPointerGesture.lastGesturePointerCount = 0; mPointerGesture.lastGestureIdBits.clear(); } else { - uint32_t currentGesturePointerCount = mPointerGesture.currentGesturePointerCount; - mPointerGesture.lastGesturePointerCount = currentGesturePointerCount; mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits; for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty(); ) { uint32_t id = idBits.firstMarkedBit(); @@ -3328,77 +3408,51 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, // Choose an arbitrary pointer that just went down, if there is one. // Otherwise choose an arbitrary remaining pointer. // This guarantees we always have an active touch id when there is at least one pointer. - // We always switch to the newest pointer down because that's usually where the user's - // attention is focused. - int32_t activeTouchId; - BitSet32 downTouchIdBits(mCurrentTouch.idBits.value & ~mLastTouch.idBits.value); - if (!downTouchIdBits.isEmpty()) { - activeTouchId = mPointerGesture.activeTouchId = downTouchIdBits.firstMarkedBit(); - } else { - activeTouchId = mPointerGesture.activeTouchId; - if (activeTouchId < 0 || !mCurrentTouch.idBits.hasBit(activeTouchId)) { - if (!mCurrentTouch.idBits.isEmpty()) { - activeTouchId = mPointerGesture.activeTouchId = - mCurrentTouch.idBits.firstMarkedBit(); - } else { - activeTouchId = mPointerGesture.activeTouchId = -1; - } + // We keep the same active touch id for as long as possible. + bool activeTouchChanged = false; + int32_t lastActiveTouchId = mPointerGesture.activeTouchId; + int32_t activeTouchId = lastActiveTouchId; + if (activeTouchId < 0) { + if (!mCurrentTouch.idBits.isEmpty()) { + activeTouchChanged = true; + activeTouchId = mPointerGesture.activeTouchId = mCurrentTouch.idBits.firstMarkedBit(); + mPointerGesture.firstTouchTime = when; + } + } else if (!mCurrentTouch.idBits.hasBit(activeTouchId)) { + activeTouchChanged = true; + if (!mCurrentTouch.idBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = mCurrentTouch.idBits.firstMarkedBit(); + } else { + activeTouchId = mPointerGesture.activeTouchId = -1; } } - // Update the touch origin data to track where each finger originally went down. - if (mCurrentTouch.pointerCount == 0 || mPointerGesture.touchOrigin.pointerCount == 0) { - // Fast path when all fingers have gone up or down. - mPointerGesture.touchOrigin.copyFrom(mCurrentTouch); + // Determine whether we are in quiet time. + bool isQuietTime = false; + if (activeTouchId < 0) { + mPointerGesture.resetQuietTime(); } else { - // Slow path when only some fingers have gone up or down. - for (BitSet32 idBits(mPointerGesture.touchOrigin.idBits.value - & ~mCurrentTouch.idBits.value); !idBits.isEmpty(); ) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - mPointerGesture.touchOrigin.idBits.clearBit(id); - uint32_t index = mPointerGesture.touchOrigin.idToIndex[id]; - uint32_t count = --mPointerGesture.touchOrigin.pointerCount; - while (index < count) { - mPointerGesture.touchOrigin.pointers[index] = - mPointerGesture.touchOrigin.pointers[index + 1]; - uint32_t movedId = mPointerGesture.touchOrigin.pointers[index].id; - mPointerGesture.touchOrigin.idToIndex[movedId] = index; - index += 1; + isQuietTime = when < mPointerGesture.quietTime + QUIET_INTERVAL; + if (!isQuietTime) { + if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS + || mPointerGesture.lastGestureMode == PointerGesture::SWIPE + || mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) + && mCurrentTouch.pointerCount < 2) { + // Enter quiet time when exiting swipe or freeform state. + // This is to prevent accidentally entering the hover state and flinging the + // pointer when finishing a swipe and there is still one pointer left onscreen. + isQuietTime = true; + } else if (mPointerGesture.lastGestureMode == PointerGesture::CLICK_OR_DRAG + && mCurrentTouch.pointerCount >= 2 + && !isPointerDown(mCurrentTouch.buttonState)) { + // Enter quiet time when releasing the button and there are still two or more + // fingers down. This may indicate that one finger was used to press the button + // but it has not gone up yet. + isQuietTime = true; + } + if (isQuietTime) { + mPointerGesture.quietTime = when; } - } - for (BitSet32 idBits(mCurrentTouch.idBits.value - & ~mPointerGesture.touchOrigin.idBits.value); !idBits.isEmpty(); ) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - mPointerGesture.touchOrigin.idBits.markBit(id); - uint32_t index = mPointerGesture.touchOrigin.pointerCount++; - mPointerGesture.touchOrigin.pointers[index] = - mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]]; - mPointerGesture.touchOrigin.idToIndex[id] = index; - } - } - - // Determine whether we are in quiet time. - bool isQuietTime = when < mPointerGesture.quietTime + QUIET_INTERVAL; - if (!isQuietTime) { - if ((mPointerGesture.lastGestureMode == PointerGesture::SWIPE - || mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) - && mCurrentTouch.pointerCount < 2) { - // Enter quiet time when exiting swipe or freeform state. - // This is to prevent accidentally entering the hover state and flinging the - // pointer when finishing a swipe and there is still one pointer left onscreen. - isQuietTime = true; - } else if (mPointerGesture.lastGestureMode == PointerGesture::CLICK_OR_DRAG - && mCurrentTouch.pointerCount >= 2 - && !isPointerDown(mCurrentTouch.buttonState)) { - // Enter quiet time when releasing the button and there are still two or more - // fingers down. This may indicate that one finger was used to press the button - // but it has not gone up yet. - isQuietTime = true; - } - if (isQuietTime) { - mPointerGesture.quietTime = when; } } @@ -3413,10 +3467,17 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.activeGestureId = -1; mPointerGesture.currentGestureMode = PointerGesture::QUIET; - mPointerGesture.currentGesturePointerCount = 0; mPointerGesture.currentGestureIdBits.clear(); + + mPointerController->setButtonState(0); + + if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL; + mPointerGesture.spotIdBits.clear(); + moveSpotsLocked(); + } } else if (isPointerDown(mCurrentTouch.buttonState)) { - // Case 2: Button is pressed. (DRAG) + // Case 2: Button is pressed. (CLICK_OR_DRAG) // The pointer follows the active touch point. // Emit DOWN, MOVE, UP events at the pointer location. // @@ -3449,7 +3510,7 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, uint32_t id = mCurrentTouch.pointers[i].id; float vx, vy; if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { - float speed = pythag(vx, vy); + float speed = hypotf(vx, vy); if (speed > bestSpeed) { bestId = id; bestSpeed = speed; @@ -3458,6 +3519,7 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, } if (bestId >= 0 && bestId != activeTouchId) { mPointerGesture.activeTouchId = activeTouchId = bestId; + activeTouchChanged = true; #if DEBUG_GESTURES LOGD("Gestures: CLICK_OR_DRAG switched pointers, " "bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed); @@ -3474,6 +3536,10 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, * mLocked.pointerGestureXMovementScale; float deltaY = (currentPointer.y - lastPointer.y) * mLocked.pointerGestureYMovementScale; + + // Move the pointer using a relative motion. + // When using spots, the click will occur at the position of the anchor + // spot and all other spots will move there. mPointerController->move(deltaX, deltaY); } } @@ -3482,7 +3548,6 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerController->getPosition(&x, &y); mPointerGesture.currentGestureMode = PointerGesture::CLICK_OR_DRAG; - mPointerGesture.currentGesturePointerCount = 1; mPointerGesture.currentGestureIdBits.clear(); mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; @@ -3490,26 +3555,49 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + + mPointerController->setButtonState(BUTTON_STATE_PRIMARY); + + if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + if (activeTouchId >= 0) { + // Collapse all spots into one point at the pointer location. + mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_DRAG; + mPointerGesture.spotIdBits.clear(); + for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) { + uint32_t id = mCurrentTouch.pointers[i].id; + mPointerGesture.spotIdBits.markBit(id); + mPointerGesture.spotIdToIndex[id] = i; + mPointerGesture.spotCoords[i] = mPointerGesture.currentGestureCoords[0]; + } + } else { + // No fingers. Generate a spot at the pointer location so the + // anchor appears to be pressed. + mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_CLICK; + mPointerGesture.spotIdBits.clear(); + mPointerGesture.spotIdBits.markBit(0); + mPointerGesture.spotIdToIndex[0] = 0; + mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0]; + } + moveSpotsLocked(); + } } else if (mCurrentTouch.pointerCount == 0) { // Case 3. No fingers down and button is not pressed. (NEUTRAL) *outFinishPreviousGesture = true; - // Watch for taps coming out of HOVER or INDETERMINATE_MULTITOUCH mode. + // Watch for taps coming out of HOVER mode. bool tapped = false; if (mPointerGesture.lastGestureMode == PointerGesture::HOVER - || mPointerGesture.lastGestureMode - == PointerGesture::INDETERMINATE_MULTITOUCH) { + && mLastTouch.pointerCount == 1) { if (when <= mPointerGesture.tapTime + TAP_INTERVAL) { float x, y; mPointerController->getPosition(&x, &y); - if (fabs(x - mPointerGesture.initialPointerX) <= TAP_SLOP - && fabs(y - mPointerGesture.initialPointerY) <= TAP_SLOP) { + if (fabs(x - mPointerGesture.tapX) <= TAP_SLOP + && fabs(y - mPointerGesture.tapY) <= TAP_SLOP) { #if DEBUG_GESTURES LOGD("Gestures: TAP"); #endif mPointerGesture.activeGestureId = 0; mPointerGesture.currentGestureMode = PointerGesture::TAP; - mPointerGesture.currentGesturePointerCount = 1; mPointerGesture.currentGestureIdBits.clear(); mPointerGesture.currentGestureIdBits.markBit( mPointerGesture.activeGestureId); @@ -3517,17 +3605,30 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.activeGestureId] = 0; mPointerGesture.currentGestureCoords[0].clear(); mPointerGesture.currentGestureCoords[0].setAxisValue( - AMOTION_EVENT_AXIS_X, mPointerGesture.initialPointerX); + AMOTION_EVENT_AXIS_X, mPointerGesture.tapX); mPointerGesture.currentGestureCoords[0].setAxisValue( - AMOTION_EVENT_AXIS_Y, mPointerGesture.initialPointerY); + AMOTION_EVENT_AXIS_Y, mPointerGesture.tapY); mPointerGesture.currentGestureCoords[0].setAxisValue( AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + + mPointerController->setButtonState(BUTTON_STATE_PRIMARY); + mPointerController->setButtonState(0); + + if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_TAP; + mPointerGesture.spotIdBits.clear(); + mPointerGesture.spotIdBits.markBit(lastActiveTouchId); + mPointerGesture.spotIdToIndex[lastActiveTouchId] = 0; + mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0]; + moveSpotsLocked(); + } + tapped = true; } else { #if DEBUG_GESTURES LOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", - x - mPointerGesture.initialPointerX, - y - mPointerGesture.initialPointerY); + x - mPointerGesture.tapX, + y - mPointerGesture.tapY); #endif } } else { @@ -3537,14 +3638,22 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, #endif } } + if (!tapped) { #if DEBUG_GESTURES LOGD("Gestures: NEUTRAL"); #endif mPointerGesture.activeGestureId = -1; mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; - mPointerGesture.currentGesturePointerCount = 0; mPointerGesture.currentGestureIdBits.clear(); + + mPointerController->setButtonState(0); + + if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL; + mPointerGesture.spotIdBits.clear(); + moveSpotsLocked(); + } } } else if (mCurrentTouch.pointerCount == 1) { // Case 4. Exactly one finger down, button is not pressed. (HOVER) @@ -3565,6 +3674,9 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, * mLocked.pointerGestureXMovementScale; float deltaY = (currentPointer.y - lastPointer.y) * mLocked.pointerGestureYMovementScale; + + // Move the pointer using a relative motion. + // When using spots, the hover will occur at the position of the anchor spot. mPointerController->move(deltaX, deltaY); } @@ -3575,7 +3687,6 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerController->getPosition(&x, &y); mPointerGesture.currentGestureMode = PointerGesture::HOVER; - mPointerGesture.currentGesturePointerCount = 1; mPointerGesture.currentGestureIdBits.clear(); mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; @@ -3586,159 +3697,268 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) { mPointerGesture.tapTime = when; - mPointerGesture.initialPointerX = x; - mPointerGesture.initialPointerY = y; + mPointerGesture.tapX = x; + mPointerGesture.tapY = y; + } + + mPointerController->setButtonState(0); + + if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_HOVER; + mPointerGesture.spotIdBits.clear(); + mPointerGesture.spotIdBits.markBit(activeTouchId); + mPointerGesture.spotIdToIndex[activeTouchId] = 0; + mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0]; + moveSpotsLocked(); } } else { - // Case 5. At least two fingers down, button is not pressed. (SWIPE or FREEFORM - // or INDETERMINATE_MULTITOUCH) - // Initially we watch and wait for something interesting to happen so as to - // avoid making a spurious guess as to the nature of the gesture. For example, - // the fingers may be in transition to some other state such as pressing or - // releasing the button or we may be performing a two finger tap. + // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM) + // We need to provide feedback for each finger that goes down so we cannot wait + // for the fingers to move before deciding what to do. + // + // The ambiguous case is deciding what to do when there are two fingers down but they + // have not moved enough to determine whether they are part of a drag or part of a + // freeform gesture, or just a press or long-press at the pointer location. + // + // When there are two fingers we start with the PRESS hypothesis and we generate a + // down at the pointer location. // - // Fix the centroid of the figure when the gesture actually starts. - // We do not recalculate the centroid at any other time during the gesture because - // it would affect the relationship of the touch points relative to the pointer location. + // When the two fingers move enough or when additional fingers are added, we make + // a decision to transition into SWIPE or FREEFORM mode accordingly. LOG_ASSERT(activeTouchId >= 0); - uint32_t currentTouchPointerCount = mCurrentTouch.pointerCount; - if (currentTouchPointerCount > MAX_POINTERS) { - currentTouchPointerCount = MAX_POINTERS; - } - - if (mPointerGesture.lastGestureMode != PointerGesture::INDETERMINATE_MULTITOUCH + bool needReference = false; + bool settled = when >= mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL; + if (mPointerGesture.lastGestureMode != PointerGesture::PRESS && mPointerGesture.lastGestureMode != PointerGesture::SWIPE && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { - mPointerGesture.currentGestureMode = PointerGesture::INDETERMINATE_MULTITOUCH; - *outFinishPreviousGesture = true; - mPointerGesture.activeGestureId = -1; - - // Remember the initial pointer location. - // Everything we do will be relative to this location. - mPointerController->getPosition(&mPointerGesture.initialPointerX, - &mPointerGesture.initialPointerY); + mPointerGesture.currentGestureMode = PointerGesture::PRESS; + mPointerGesture.activeGestureId = 0; - // Track taps. - if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) { - mPointerGesture.tapTime = when; + if (settled && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS + && mLastTouch.idBits.hasBit(mPointerGesture.activeTouchId)) { + // The spot is already visible and has settled, use it as the reference point + // for the gesture. Other spots will be positioned relative to this one. +#if DEBUG_GESTURES + LOGD("Gestures: Using active spot as reference for MULTITOUCH, " + "settle time expired %0.3fms ago", + (when - mPointerGesture.firstTouchTime - MULTITOUCH_SETTLE_INTERVAL) + * 0.000001f); +#endif + const PointerData& d = mLastTouch.pointers[mLastTouch.idToIndex[ + mPointerGesture.activeTouchId]]; + mPointerGesture.referenceTouchX = d.x; + mPointerGesture.referenceTouchY = d.y; + const PointerCoords& c = mPointerGesture.spotCoords[mPointerGesture.spotIdToIndex[ + mPointerGesture.activeTouchId]]; + mPointerGesture.referenceGestureX = c.getAxisValue(AMOTION_EVENT_AXIS_X); + mPointerGesture.referenceGestureY = c.getAxisValue(AMOTION_EVENT_AXIS_Y); + } else { +#if DEBUG_GESTURES + LOGD("Gestures: Using centroid as reference for MULTITOUCH, " + "settle time remaining %0.3fms", + (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when) + * 0.000001f); +#endif + needReference = true; } - - // Reset the touch origin to be relative to exactly where the fingers are now - // in case they have moved some distance away as part of a previous gesture. - // We want to know how far the fingers have traveled since we started considering - // a multitouch gesture. - mPointerGesture.touchOrigin.copyFrom(mCurrentTouch); + } else if (!settled && mCurrentTouch.pointerCount > mLastTouch.pointerCount) { + // Additional pointers have gone down but not yet settled. + // Reset the gesture. +#if DEBUG_GESTURES + LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, " + "settle time remaining %0.3fms", + (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when) + * 0.000001f); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::PRESS; + mPointerGesture.activeGestureId = 0; } else { + // Continue previous gesture. mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; } - if (mPointerGesture.currentGestureMode == PointerGesture::INDETERMINATE_MULTITOUCH) { - // Wait for the pointers to start moving before doing anything. - bool decideNow = true; - for (uint32_t i = 0; i < currentTouchPointerCount; i++) { - const PointerData& current = mCurrentTouch.pointers[i]; - const PointerData& origin = mPointerGesture.touchOrigin.pointers[ - mPointerGesture.touchOrigin.idToIndex[current.id]]; - float distance = pythag( - (current.x - origin.x) * mLocked.pointerGestureXZoomScale, - (current.y - origin.y) * mLocked.pointerGestureYZoomScale); - if (distance < MULTITOUCH_MIN_TRAVEL) { - decideNow = false; - break; - } - } + if (needReference) { + // Use the centroid and pointer location as the reference points for the gesture. + mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX, + &mPointerGesture.referenceTouchY); + mPointerController->getPosition(&mPointerGesture.referenceGestureX, + &mPointerGesture.referenceGestureY); + } - if (decideNow) { + if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) { + float d; + if (mCurrentTouch.pointerCount > 2) { + // There are more than two pointers, switch to FREEFORM. +#if DEBUG_GESTURES + LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", + mCurrentTouch.pointerCount); +#endif + *outCancelPreviousGesture = true; mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - if (currentTouchPointerCount == 2 - && distanceSquared( - mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y, - mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y) - <= mLocked.pointerGestureMaxSwipeWidthSquared) { - const PointerData& current1 = mCurrentTouch.pointers[0]; - const PointerData& current2 = mCurrentTouch.pointers[1]; - const PointerData& origin1 = mPointerGesture.touchOrigin.pointers[ - mPointerGesture.touchOrigin.idToIndex[current1.id]]; - const PointerData& origin2 = mPointerGesture.touchOrigin.pointers[ - mPointerGesture.touchOrigin.idToIndex[current2.id]]; - - float x1 = (current1.x - origin1.x) * mLocked.pointerGestureXZoomScale; - float y1 = (current1.y - origin1.y) * mLocked.pointerGestureYZoomScale; - float x2 = (current2.x - origin2.x) * mLocked.pointerGestureXZoomScale; - float y2 = (current2.y - origin2.y) * mLocked.pointerGestureYZoomScale; - float magnitude1 = pythag(x1, y1); - float magnitude2 = pythag(x2, y2); - - // Calculate the dot product of the vectors. + } else if (((d = distance( + mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y, + mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y)) + > mLocked.pointerGestureMaxSwipeWidth)) { + // There are two pointers but they are too far apart, switch to FREEFORM. +#if DEBUG_GESTURES + LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", + d, mLocked.pointerGestureMaxSwipeWidth); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else { + // There are two pointers. Wait for both pointers to start moving + // before deciding whether this is a SWIPE or FREEFORM gesture. + uint32_t id1 = mCurrentTouch.pointers[0].id; + uint32_t id2 = mCurrentTouch.pointers[1].id; + + float vx1, vy1, vx2, vy2; + mPointerGesture.velocityTracker.getVelocity(id1, &vx1, &vy1); + mPointerGesture.velocityTracker.getVelocity(id2, &vx2, &vy2); + + float speed1 = hypotf(vx1, vy1); + float speed2 = hypotf(vx2, vy2); + if (speed1 >= MULTITOUCH_MIN_SPEED && speed2 >= MULTITOUCH_MIN_SPEED) { + // Calculate the dot product of the velocity vectors. // When the vectors are oriented in approximately the same direction, // the angle betweeen them is near zero and the cosine of the angle // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2). - // We know that the magnitude is at least MULTITOUCH_MIN_TRAVEL because - // we checked it above. - float dot = x1 * x2 + y1 * y2; - float cosine = dot / (magnitude1 * magnitude2); // denominator always > 0 - if (cosine > SWIPE_TRANSITION_ANGLE_COSINE) { + float dot = vx1 * vx2 + vy1 * vy2; + float cosine = dot / (speed1 * speed2); // denominator always > 0 + if (cosine >= SWIPE_TRANSITION_ANGLE_COSINE) { + // Pointers are moving in the same direction. Switch to SWIPE. +#if DEBUG_GESTURES + LOGD("Gestures: PRESS transitioned to SWIPE, " + "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, " + "cosine %0.3f >= %0.3f", + speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED, + cosine, SWIPE_TRANSITION_ANGLE_COSINE); +#endif mPointerGesture.currentGestureMode = PointerGesture::SWIPE; + } else { + // Pointers are moving in different directions. Switch to FREEFORM. +#if DEBUG_GESTURES + LOGD("Gestures: PRESS transitioned to FREEFORM, " + "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, " + "cosine %0.3f < %0.3f", + speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED, + cosine, SWIPE_TRANSITION_ANGLE_COSINE); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; } } - - // Remember the initial centroid for the duration of the gesture. - mPointerGesture.initialCentroidX = 0; - mPointerGesture.initialCentroidY = 0; - for (uint32_t i = 0; i < currentTouchPointerCount; i++) { - const PointerData& touch = mCurrentTouch.pointers[i]; - mPointerGesture.initialCentroidX += touch.x; - mPointerGesture.initialCentroidY += touch.y; - } - mPointerGesture.initialCentroidX /= int32_t(currentTouchPointerCount); - mPointerGesture.initialCentroidY /= int32_t(currentTouchPointerCount); - - mPointerGesture.activeGestureId = 0; } } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { - // Switch to FREEFORM if additional pointers go down. - if (currentTouchPointerCount > 2) { + // Switch from SWIPE to FREEFORM if additional pointers go down. + // Cancel previous gesture. + if (mCurrentTouch.pointerCount > 2) { +#if DEBUG_GESTURES + LOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2", + mCurrentTouch.pointerCount); +#endif *outCancelPreviousGesture = true; mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; } } - if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // Move the reference points based on the overall group motion of the fingers. + // The objective is to calculate a vector delta that is common to the movement + // of all fingers. + BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value); + if (!commonIdBits.isEmpty()) { + float commonDeltaX = 0, commonDeltaY = 0; + for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) { + bool first = (idBits == commonIdBits); + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + + const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]]; + const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]]; + float deltaX = cpd.x - lpd.x; + float deltaY = cpd.y - lpd.y; + + if (first) { + commonDeltaX = deltaX; + commonDeltaY = deltaY; + } else { + commonDeltaX = calculateCommonVector(commonDeltaX, deltaX); + commonDeltaY = calculateCommonVector(commonDeltaY, deltaY); + } + } + + mPointerGesture.referenceTouchX += commonDeltaX; + mPointerGesture.referenceTouchY += commonDeltaY; + mPointerGesture.referenceGestureX += + commonDeltaX * mLocked.pointerGestureXMovementScale; + mPointerGesture.referenceGestureY += + commonDeltaY * mLocked.pointerGestureYMovementScale; + clampPositionUsingPointerBounds(mPointerController, + &mPointerGesture.referenceGestureX, + &mPointerGesture.referenceGestureY); + } + + // Report gestures. + if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) { + // PRESS mode. +#if DEBUG_GESTURES + LOGD("Gestures: PRESS activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, mCurrentTouch.pointerCount); +#endif + LOG_ASSERT(mPointerGesture.activeGestureId >= 0); + + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.referenceGestureX); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.referenceGestureY); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + + mPointerController->setButtonState(BUTTON_STATE_PRIMARY); + + if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_PRESS; + } + } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { // SWIPE mode. #if DEBUG_GESTURES LOGD("Gestures: SWIPE activeTouchId=%d," "activeGestureId=%d, currentTouchPointerCount=%d", - activeTouchId, mPointerGesture.activeGestureId, currentTouchPointerCount); + activeTouchId, mPointerGesture.activeGestureId, mCurrentTouch.pointerCount); #endif LOG_ASSERT(mPointerGesture.activeGestureId >= 0); - float x = (mCurrentTouch.pointers[0].x + mCurrentTouch.pointers[1].x - - mPointerGesture.initialCentroidX * 2) * 0.5f - * mLocked.pointerGestureXMovementScale + mPointerGesture.initialPointerX; - float y = (mCurrentTouch.pointers[0].y + mCurrentTouch.pointers[1].y - - mPointerGesture.initialCentroidY * 2) * 0.5f - * mLocked.pointerGestureYMovementScale + mPointerGesture.initialPointerY; - - mPointerGesture.currentGesturePointerCount = 1; mPointerGesture.currentGestureIdBits.clear(); mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.referenceGestureX); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.referenceGestureY); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + + mPointerController->setButtonState(0); // touch is not actually following the pointer + + if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_SWIPE; + } } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { // FREEFORM mode. #if DEBUG_GESTURES LOGD("Gestures: FREEFORM activeTouchId=%d," "activeGestureId=%d, currentTouchPointerCount=%d", - activeTouchId, mPointerGesture.activeGestureId, currentTouchPointerCount); + activeTouchId, mPointerGesture.activeGestureId, mCurrentTouch.pointerCount); #endif LOG_ASSERT(mPointerGesture.activeGestureId >= 0); - mPointerGesture.currentGesturePointerCount = currentTouchPointerCount; mPointerGesture.currentGestureIdBits.clear(); BitSet32 mappedTouchIdBits; @@ -3782,7 +4002,7 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.activeGestureId); #endif - for (uint32_t i = 0; i < currentTouchPointerCount; i++) { + for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) { uint32_t touchId = mCurrentTouch.pointers[i].id; uint32_t gestureId; if (!mappedTouchIdBits.hasBit(touchId)) { @@ -3805,10 +4025,10 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureIdBits.markBit(gestureId); mPointerGesture.currentGestureIdToIndex[gestureId] = i; - float x = (mCurrentTouch.pointers[i].x - mPointerGesture.initialCentroidX) - * mLocked.pointerGestureXZoomScale + mPointerGesture.initialPointerX; - float y = (mCurrentTouch.pointers[i].y - mPointerGesture.initialCentroidY) - * mLocked.pointerGestureYZoomScale + mPointerGesture.initialPointerY; + float x = (mCurrentTouch.pointers[i].x - mPointerGesture.referenceTouchX) + * mLocked.pointerGestureXZoomScale + mPointerGesture.referenceGestureX; + float y = (mCurrentTouch.pointers[i].y - mPointerGesture.referenceTouchY) + * mLocked.pointerGestureYZoomScale + mPointerGesture.referenceGestureY; mPointerGesture.currentGestureCoords[i].clear(); mPointerGesture.currentGestureCoords[i].setAxisValue( @@ -3827,30 +4047,45 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, "activeGestureId=%d", mPointerGesture.activeGestureId); #endif } - } else { - // INDETERMINATE_MULTITOUCH mode. - // Do nothing. -#if DEBUG_GESTURES - LOGD("Gestures: INDETERMINATE_MULTITOUCH"); -#endif + + mPointerController->setButtonState(0); // touch is not actually following the pointer + + if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_FREEFORM; + } } - } - // Unfade the pointer if the user is doing anything with the touch pad. - mPointerController->setButtonState(mCurrentTouch.buttonState); - if (mCurrentTouch.buttonState || mCurrentTouch.pointerCount != 0) { - mPointerController->unfade(); + // Update spot locations for PRESS, SWIPE and FREEFORM. + // We use the same calculation as we do to calculate the gesture pointers + // for FREEFORM so that the spots smoothly track gestures. + if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerGesture.spotIdBits.clear(); + for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) { + uint32_t id = mCurrentTouch.pointers[i].id; + mPointerGesture.spotIdBits.markBit(id); + mPointerGesture.spotIdToIndex[id] = i; + + float x = (mCurrentTouch.pointers[i].x - mPointerGesture.referenceTouchX) + * mLocked.pointerGestureXZoomScale + mPointerGesture.referenceGestureX; + float y = (mCurrentTouch.pointers[i].y - mPointerGesture.referenceTouchY) + * mLocked.pointerGestureYZoomScale + mPointerGesture.referenceGestureY; + + mPointerGesture.spotCoords[i].clear(); + mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } + moveSpotsLocked(); + } } #if DEBUG_GESTURES LOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " - "currentGestureMode=%d, currentGesturePointerCount=%d, currentGestureIdBits=0x%08x, " - "lastGestureMode=%d, lastGesturePointerCount=%d, lastGestureIdBits=0x%08x", + "currentGestureMode=%d, currentGestureIdBits=0x%08x, " + "lastGestureMode=%d, lastGestureIdBits=0x%08x", toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), - mPointerGesture.currentGestureMode, mPointerGesture.currentGesturePointerCount, - mPointerGesture.currentGestureIdBits.value, - mPointerGesture.lastGestureMode, mPointerGesture.lastGesturePointerCount, - mPointerGesture.lastGestureIdBits.value); + mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value, + mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value); for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty(); ) { uint32_t id = idBits.firstMarkedBit(); idBits.clearBit(id); @@ -3874,6 +4109,11 @@ void TouchInputMapper::preparePointerGestures(nsecs_t when, #endif } +void TouchInputMapper::moveSpotsLocked() { + mPointerController->setSpots(mPointerGesture.spotGesture, + mPointerGesture.spotCoords, mPointerGesture.spotIdToIndex, mPointerGesture.spotIdBits); +} + void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, int32_t flags, uint32_t metaState, int32_t edgeFlags, const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 9ed1391..9b2f4d2 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -20,7 +20,6 @@ #include "EventHub.h" #include "InputDispatcher.h" #include "PointerController.h" -#include "SpotController.h" #include <ui/Input.h> #include <ui/DisplayInfo.h> @@ -90,9 +89,6 @@ public: /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */ virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0; - - /* Gets a spot controller associated with the specified touch pad device. */ - virtual sp<SpotControllerInterface> obtainSpotController(int32_t deviceId) = 0; }; @@ -648,6 +644,20 @@ protected: idBits.clear(); buttonState = 0; } + + void getCentroid(float* outX, float* outY) { + float x = 0, y = 0; + if (pointerCount != 0) { + for (uint32_t i = 0; i < pointerCount; i++) { + x += pointers[i].x; + y += pointers[i].y; + } + x /= pointerCount; + y /= pointerCount; + } + *outX = x; + *outY = y; + } }; // Input sources supported by the device. @@ -670,6 +680,12 @@ protected: bool useJumpyTouchFilter; bool useAveragingTouchFilter; nsecs_t virtualKeyQuietTime; + + enum GestureMode { + GESTURE_MODE_POINTER, + GESTURE_MODE_SPOTS, + }; + GestureMode gestureMode; } mParameters; // Immutable calibration parameters in parsed form. @@ -841,8 +857,8 @@ protected: float pointerGestureXZoomScale; float pointerGestureYZoomScale; - // The maximum swipe width squared. - int32_t pointerGestureMaxSwipeWidthSquared; + // The maximum swipe width. + float pointerGestureMaxSwipeWidth; } mLocked; virtual void configureParameters(); @@ -929,28 +945,32 @@ private: // Emits HOVER_MOVE events at the pointer location. HOVER, - // More than two fingers involved but they haven't moved enough for us - // to figure out what is intended. - INDETERMINATE_MULTITOUCH, + // Exactly two fingers but neither have moved enough to clearly indicate + // whether a swipe or freeform gesture was intended. We consider the + // pointer to be pressed so this enables clicking or long-pressing on buttons. + // Pointer does not move. + // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate. + PRESS, // Exactly two fingers moving in the same direction, button is not pressed. // Pointer does not move. // Emits DOWN, MOVE and UP events with a single pointer coordinate that // follows the midpoint between both fingers. - // The centroid is fixed when entering this state. SWIPE, // Two or more fingers moving in arbitrary directions, button is not pressed. // Pointer does not move. // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow // each finger individually relative to the initial centroid of the finger. - // The centroid is fixed when entering this state. FREEFORM, // Waiting for quiet time to end before starting the next gesture. QUIET, }; + // Time the first finger went down. + nsecs_t firstTouchTime; + // The active pointer id from the raw touch data. int32_t activeTouchId; // -1 if none @@ -959,32 +979,20 @@ private: // Pointer coords and ids for the current and previous pointer gesture. Mode currentGestureMode; - uint32_t currentGesturePointerCount; BitSet32 currentGestureIdBits; uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; PointerCoords currentGestureCoords[MAX_POINTERS]; Mode lastGestureMode; - uint32_t lastGesturePointerCount; BitSet32 lastGestureIdBits; uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; PointerCoords lastGestureCoords[MAX_POINTERS]; - // Tracks for all pointers originally went down. - TouchData touchOrigin; - - // Describes how touch ids are mapped to gesture ids for freeform gestures. - uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1]; - - // Initial centroid of the movement. - // Used to calculate how far the touch pointers have moved since the gesture started. - int32_t initialCentroidX; - int32_t initialCentroidY; - - // Initial pointer location. - // Used to track where the pointer was when the gesture started. - float initialPointerX; - float initialPointerY; + // Pointer coords and ids for the current spots. + PointerControllerInterface::SpotGesture spotGesture; + BitSet32 spotIdBits; // same set of ids as touch ids + uint32_t spotIdToIndex[MAX_POINTER_ID + 1]; + PointerCoords spotCoords[MAX_POINTERS]; // Time the pointer gesture last went down. nsecs_t downTime; @@ -992,26 +1000,34 @@ private: // Time we started waiting for a tap gesture. nsecs_t tapTime; + // Location of initial tap. + float tapX, tapY; + // Time we started waiting for quiescence. nsecs_t quietTime; + // Reference points for multitouch gestures. + float referenceTouchX; // reference touch X/Y coordinates in surface units + float referenceTouchY; + float referenceGestureX; // reference gesture X/Y coordinates in pixels + float referenceGestureY; + + // Describes how touch ids are mapped to gesture ids for freeform gestures. + uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1]; + // A velocity tracker for determining whether to switch active pointers during drags. VelocityTracker velocityTracker; void reset() { + firstTouchTime = LLONG_MIN; activeTouchId = -1; activeGestureId = -1; currentGestureMode = NEUTRAL; - currentGesturePointerCount = 0; currentGestureIdBits.clear(); lastGestureMode = NEUTRAL; - lastGesturePointerCount = 0; lastGestureIdBits.clear(); - touchOrigin.clear(); - initialCentroidX = 0; - initialCentroidY = 0; - initialPointerX = 0; - initialPointerY = 0; + spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL; + spotIdBits.clear(); downTime = 0; velocityTracker.clear(); resetTapTime(); @@ -1035,6 +1051,7 @@ private: void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags); void preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, bool* outFinishPreviousGesture); + void moveSpotsLocked(); // Dispatches a motion event. // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp index 15effb7..ffef720 100644 --- a/services/input/PointerController.cpp +++ b/services/input/PointerController.cpp @@ -36,40 +36,49 @@ namespace android { // --- PointerController --- // Time to wait before starting the fade when the pointer is inactive. -static const nsecs_t INACTIVITY_FADE_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds -static const nsecs_t INACTIVITY_FADE_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds +static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds +static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds -// Time to spend fading out the pointer completely. -static const nsecs_t FADE_DURATION = 500 * 1000000LL; // 500 ms +// Time to wait between animation frames. +static const nsecs_t ANIMATION_FRAME_INTERVAL = 1000000000LL / 60; + +// Time to spend fading out the spot completely. +static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms -// Time to wait between frames. -static const nsecs_t FADE_FRAME_INTERVAL = 1000000000LL / 60; +// Time to spend fading out the pointer completely. +static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms -// Amount to subtract from alpha per frame. -static const float FADE_DECAY_PER_FRAME = float(FADE_FRAME_INTERVAL) / FADE_DURATION; +// --- PointerController --- -PointerController::PointerController(const sp<Looper>& looper, - const sp<SpriteController>& spriteController) : - mLooper(looper), mSpriteController(spriteController) { +PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy, + const sp<Looper>& looper, const sp<SpriteController>& spriteController) : + mPolicy(policy), mLooper(looper), mSpriteController(spriteController) { mHandler = new WeakMessageHandler(this); AutoMutex _l(mLock); + mLocked.animationPending = false; + mLocked.displayWidth = -1; mLocked.displayHeight = -1; mLocked.displayOrientation = DISPLAY_ORIENTATION_0; + mLocked.presentation = PRESENTATION_POINTER; + mLocked.presentationChanged = false; + + mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL; + + mLocked.pointerIsFading = true; // keep the pointer initially faded mLocked.pointerX = 0; mLocked.pointerY = 0; - mLocked.buttonState = 0; - - mLocked.fadeAlpha = 1; - mLocked.inactivityFadeDelay = INACTIVITY_FADE_DELAY_NORMAL; + mLocked.pointerAlpha = 0.0f; + mLocked.pointerSprite = mSpriteController->createSprite(); + mLocked.pointerIconChanged = false; - mLocked.visible = false; + mLocked.buttonState = 0; - mLocked.sprite = mSpriteController->createSprite(); + loadResources(); } PointerController::~PointerController() { @@ -77,7 +86,13 @@ PointerController::~PointerController() { AutoMutex _l(mLock); - mLocked.sprite.clear(); + mLocked.pointerSprite.clear(); + + for (size_t i = 0; i < mLocked.spots.size(); i++) { + delete mLocked.spots.itemAt(i); + } + mLocked.spots.clear(); + mLocked.recycledSprites.clear(); } bool PointerController::getBounds(float* outMinX, float* outMinY, @@ -130,8 +145,6 @@ void PointerController::setButtonState(uint32_t buttonState) { if (mLocked.buttonState != buttonState) { mLocked.buttonState = buttonState; - unfadeBeforeUpdateLocked(); - updateLocked(); } } @@ -167,8 +180,7 @@ void PointerController::setPositionLocked(float x, float y) { } else { mLocked.pointerY = y; } - unfadeBeforeUpdateLocked(); - updateLocked(); + updatePointerLocked(); } } @@ -182,32 +194,105 @@ void PointerController::getPosition(float* outX, float* outY) const { void PointerController::fade() { AutoMutex _l(mLock); - startFadeLocked(); + sendImmediateInactivityTimeoutLocked(); } void PointerController::unfade() { AutoMutex _l(mLock); - if (unfadeBeforeUpdateLocked()) { - updateLocked(); + // Always reset the inactivity timer. + resetInactivityTimeoutLocked(); + + // Unfade immediately if needed. + if (mLocked.pointerIsFading) { + mLocked.pointerIsFading = false; + mLocked.pointerAlpha = 1.0f; + updatePointerLocked(); } } -void PointerController::setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay) { +void PointerController::setPresentation(Presentation presentation) { AutoMutex _l(mLock); - if (mLocked.inactivityFadeDelay != inactivityFadeDelay) { - mLocked.inactivityFadeDelay = inactivityFadeDelay; - startInactivityFadeDelayLocked(); + if (mLocked.presentation != presentation) { + mLocked.presentation = presentation; + mLocked.presentationChanged = true; + + if (presentation != PRESENTATION_SPOT) { + fadeOutAndReleaseAllSpotsLocked(); + } + + updatePointerLocked(); } } -void PointerController::updateLocked() { - mLocked.sprite->openTransaction(); - mLocked.sprite->setPosition(mLocked.pointerX, mLocked.pointerY); - mLocked.sprite->setAlpha(mLocked.fadeAlpha); - mLocked.sprite->setVisible(mLocked.visible); - mLocked.sprite->closeTransaction(); +void PointerController::setSpots(SpotGesture spotGesture, + const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { +#if DEBUG_POINTER_UPDATES + LOGD("setSpots: spotGesture=%d", spotGesture); + for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + const PointerCoords& c = spotCoords[spotIdToIndex[id]]; + LOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id, + c.getAxisValue(AMOTION_EVENT_AXIS_X), + c.getAxisValue(AMOTION_EVENT_AXIS_Y), + c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } +#endif + + AutoMutex _l(mLock); + + mSpriteController->openTransaction(); + + // Add or move spots for fingers that are down. + for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + + const PointerCoords& c = spotCoords[spotIdToIndex[id]]; + const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0 + ? mResources.spotTouch : mResources.spotHover; + float x = c.getAxisValue(AMOTION_EVENT_AXIS_X); + float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y); + + Spot* spot = getSpotLocked(id); + if (!spot) { + spot = createAndAddSpotLocked(id); + } + + spot->updateSprite(&icon, x, y); + } + + // Remove spots for fingers that went up. + for (size_t i = 0; i < mLocked.spots.size(); i++) { + Spot* spot = mLocked.spots.itemAt(i); + if (spot->id != Spot::INVALID_ID + && !spotIdBits.hasBit(spot->id)) { + fadeOutAndReleaseSpotLocked(spot); + } + } + + mSpriteController->closeTransaction(); +} + +void PointerController::clearSpots() { +#if DEBUG_POINTER_UPDATES + LOGD("clearSpots"); +#endif + + AutoMutex _l(mLock); + + fadeOutAndReleaseAllSpotsLocked(); +} + +void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) { + AutoMutex _l(mLock); + + if (mLocked.inactivityTimeout != inactivityTimeout) { + mLocked.inactivityTimeout = inactivityTimeout; + resetInactivityTimeoutLocked(); + } } void PointerController::setDisplaySize(int32_t width, int32_t height) { @@ -226,7 +311,8 @@ void PointerController::setDisplaySize(int32_t width, int32_t height) { mLocked.pointerY = 0; } - updateLocked(); + fadeOutAndReleaseAllSpotsLocked(); + updatePointerLocked(); } } @@ -283,74 +369,217 @@ void PointerController::setDisplayOrientation(int32_t orientation) { mLocked.pointerY = y - 0.5f; mLocked.displayOrientation = orientation; - updateLocked(); + updatePointerLocked(); } } -void PointerController::setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) { +void PointerController::setPointerIcon(const SpriteIcon& icon) { AutoMutex _l(mLock); - mLocked.sprite->setBitmap(bitmap, hotSpotX, hotSpotY); + mLocked.pointerIcon = icon.copy(); + mLocked.pointerIconChanged = true; + + updatePointerLocked(); } void PointerController::handleMessage(const Message& message) { switch (message.what) { - case MSG_FADE_STEP: { - AutoMutex _l(mLock); - fadeStepLocked(); + case MSG_ANIMATE: + doAnimate(); + break; + case MSG_INACTIVITY_TIMEOUT: + doInactivityTimeout(); break; } +} + +void PointerController::doAnimate() { + AutoMutex _l(mLock); + + bool keepAnimating = false; + mLocked.animationPending = false; + nsecs_t frameDelay = systemTime(SYSTEM_TIME_MONOTONIC) - mLocked.animationTime; + + // Animate pointer fade. + if (mLocked.pointerIsFading) { + mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION; + if (mLocked.pointerAlpha <= 0) { + mLocked.pointerAlpha = 0; + } else { + keepAnimating = true; + } + updatePointerLocked(); + } + + // Animate spots that are fading out and being removed. + for (size_t i = 0; i < mLocked.spots.size(); i++) { + Spot* spot = mLocked.spots.itemAt(i); + if (spot->id == Spot::INVALID_ID) { + spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; + if (spot->alpha <= 0) { + mLocked.spots.removeAt(i--); + releaseSpotLocked(spot); + } else { + spot->sprite->setAlpha(spot->alpha); + keepAnimating = true; + } + } + } + + if (keepAnimating) { + startAnimationLocked(); } } -bool PointerController::unfadeBeforeUpdateLocked() { - sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked()); +void PointerController::doInactivityTimeout() { + AutoMutex _l(mLock); - if (isFadingLocked()) { - mLocked.visible = true; - mLocked.fadeAlpha = 1; - return true; // update required to effect the unfade + if (!mLocked.pointerIsFading) { + mLocked.pointerIsFading = true; + startAnimationLocked(); } - return false; // update not required } -void PointerController::startFadeLocked() { - if (!isFadingLocked()) { - sendFadeStepMessageDelayedLocked(0); +void PointerController::startAnimationLocked() { + if (!mLocked.animationPending) { + mLocked.animationPending = true; + mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); + mLooper->sendMessageDelayed(ANIMATION_FRAME_INTERVAL, mHandler, Message(MSG_ANIMATE)); } } -void PointerController::startInactivityFadeDelayLocked() { - if (!isFadingLocked()) { - sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked()); +void PointerController::resetInactivityTimeoutLocked() { + mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); + + nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT + ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; + mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT); +} + +void PointerController::sendImmediateInactivityTimeoutLocked() { + mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); + mLooper->sendMessage(mHandler, MSG_INACTIVITY_TIMEOUT); +} + +void PointerController::updatePointerLocked() { + mSpriteController->openTransaction(); + + mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER); + mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY); + + if (mLocked.pointerAlpha > 0) { + mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha); + mLocked.pointerSprite->setVisible(true); + } else { + mLocked.pointerSprite->setVisible(false); + } + + if (mLocked.pointerIconChanged || mLocked.presentationChanged) { + mLocked.pointerSprite->setIcon(mLocked.presentation == PRESENTATION_POINTER + ? mLocked.pointerIcon : mResources.spotAnchor); + mLocked.pointerIconChanged = false; + mLocked.presentationChanged = false; } + + mSpriteController->closeTransaction(); } -void PointerController::fadeStepLocked() { - if (mLocked.visible) { - mLocked.fadeAlpha -= FADE_DECAY_PER_FRAME; - if (mLocked.fadeAlpha < 0) { - mLocked.fadeAlpha = 0; - mLocked.visible = false; - } else { - sendFadeStepMessageDelayedLocked(FADE_FRAME_INTERVAL); +PointerController::Spot* PointerController::getSpotLocked(uint32_t id) { + for (size_t i = 0; i < mLocked.spots.size(); i++) { + Spot* spot = mLocked.spots.itemAt(i); + if (spot->id == id) { + return spot; + } + } + return NULL; +} + +PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) { + // Remove spots until we have fewer than MAX_SPOTS remaining. + while (mLocked.spots.size() >= MAX_SPOTS) { + Spot* spot = removeFirstFadingSpotLocked(); + if (!spot) { + spot = mLocked.spots.itemAt(0); + mLocked.spots.removeAt(0); + } + releaseSpotLocked(spot); + } + + // Obtain a sprite from the recycled pool. + sp<Sprite> sprite; + if (! mLocked.recycledSprites.isEmpty()) { + sprite = mLocked.recycledSprites.top(); + mLocked.recycledSprites.pop(); + } else { + sprite = mSpriteController->createSprite(); + } + + // Return the new spot. + Spot* spot = new Spot(id, sprite); + mLocked.spots.push(spot); + return spot; +} + +PointerController::Spot* PointerController::removeFirstFadingSpotLocked() { + for (size_t i = 0; i < mLocked.spots.size(); i++) { + Spot* spot = mLocked.spots.itemAt(i); + if (spot->id == Spot::INVALID_ID) { + mLocked.spots.removeAt(i); + return spot; } - updateLocked(); + } + return NULL; +} + +void PointerController::releaseSpotLocked(Spot* spot) { + spot->sprite->clearIcon(); + + if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { + mLocked.recycledSprites.push(spot->sprite); + } + + delete spot; +} + +void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) { + if (spot->id != Spot::INVALID_ID) { + spot->id = Spot::INVALID_ID; + startAnimationLocked(); } } -bool PointerController::isFadingLocked() { - return !mLocked.visible || mLocked.fadeAlpha != 1; +void PointerController::fadeOutAndReleaseAllSpotsLocked() { + for (size_t i = 0; i < mLocked.spots.size(); i++) { + Spot* spot = mLocked.spots.itemAt(i); + fadeOutAndReleaseSpotLocked(spot); + } } -nsecs_t PointerController::getInactivityFadeDelayTimeLocked() { - return mLocked.inactivityFadeDelay == INACTIVITY_FADE_DELAY_SHORT - ? INACTIVITY_FADE_DELAY_TIME_SHORT : INACTIVITY_FADE_DELAY_TIME_NORMAL; +void PointerController::loadResources() { + mPolicy->loadPointerResources(&mResources); } -void PointerController::sendFadeStepMessageDelayedLocked(nsecs_t delayTime) { - mLooper->removeMessages(mHandler, MSG_FADE_STEP); - mLooper->sendMessageDelayed(delayTime, mHandler, Message(MSG_FADE_STEP)); + +// --- PointerController::Spot --- + +void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) { + sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); + sprite->setAlpha(alpha); + sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); + sprite->setPosition(x, y); + + this->x = x; + this->y = y; + + if (icon != lastIcon) { + lastIcon = icon; + if (icon) { + sprite->setIcon(*icon); + sprite->setVisible(true); + } else { + sprite->setVisible(false); + } + } } } // namespace android diff --git a/services/input/PointerController.h b/services/input/PointerController.h index d467a5a..afd6371 100644 --- a/services/input/PointerController.h +++ b/services/input/PointerController.h @@ -30,7 +30,10 @@ namespace android { /** - * Interface for tracking a single (mouse) pointer. + * Interface for tracking a mouse / touch pad pointer and touch pad spots. + * + * The spots are sprites on screen that visually represent the positions of + * fingers * * The pointer controller is responsible for providing synchronization and for tracking * display orientation changes if needed. @@ -64,8 +67,95 @@ public: /* Fades the pointer out now. */ virtual void fade() = 0; - /* Makes the pointer visible if it has faded out. */ + /* Makes the pointer visible if it has faded out. + * The pointer never unfades itself automatically. This method must be called + * by the client whenever the pointer is moved or a button is pressed and it + * wants to ensure that the pointer becomes visible again. */ virtual void unfade() = 0; + + enum Presentation { + // Show the mouse pointer. + PRESENTATION_POINTER, + // Show spots and a spot anchor in place of the mouse pointer. + PRESENTATION_SPOT, + }; + + /* Sets the mode of the pointer controller. */ + virtual void setPresentation(Presentation presentation) = 0; + + // Describes the current gesture. + enum SpotGesture { + // No gesture. + // Do not display any spots. + SPOT_GESTURE_NEUTRAL, + // Tap at current location. + // Briefly display one spot at the tapped location. + SPOT_GESTURE_TAP, + // Button pressed but no finger is down. + // Display spot at pressed location. + SPOT_GESTURE_BUTTON_CLICK, + // Button pressed and a finger is down. + // Display spot at pressed location. + SPOT_GESTURE_BUTTON_DRAG, + // One finger down and hovering. + // Display spot at the hovered location. + SPOT_GESTURE_HOVER, + // Two fingers down but not sure in which direction they are moving so we consider + // it a press at the pointer location. + // Display two spots near the pointer location. + SPOT_GESTURE_PRESS, + // Two fingers down and moving in same direction. + // Display two spots near the pointer location. + SPOT_GESTURE_SWIPE, + // Two or more fingers down and moving in arbitrary directions. + // Display two or more spots near the pointer location, one for each finger. + SPOT_GESTURE_FREEFORM, + }; + + /* Sets the spots for the current gesture. + * The spots are not subject to the inactivity timeout like the pointer + * itself it since they are expected to remain visible for so long as + * the fingers are on the touch pad. + * + * The values of the AMOTION_EVENT_AXIS_PRESSURE axis is significant. + * For spotCoords, pressure != 0 indicates that the spot's location is being + * pressed (not hovering). + */ + virtual void setSpots(SpotGesture spotGesture, + const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits) = 0; + + /* Removes all spots. */ + virtual void clearSpots() = 0; +}; + + +/* + * Pointer resources. + */ +struct PointerResources { + SpriteIcon spotHover; + SpriteIcon spotTouch; + SpriteIcon spotAnchor; +}; + + +/* + * Pointer controller policy interface. + * + * The pointer controller policy is used by the pointer controller to interact with + * the Window Manager and other system components. + * + * The actual implementation is partially supported by callbacks into the DVM + * via JNI. This interface is also mocked in the unit tests. + */ +class PointerControllerPolicyInterface : public virtual RefBase { +protected: + PointerControllerPolicyInterface() { } + virtual ~PointerControllerPolicyInterface() { } + +public: + virtual void loadPointerResources(PointerResources* outResources) = 0; }; @@ -79,12 +169,13 @@ protected: virtual ~PointerController(); public: - enum InactivityFadeDelay { - INACTIVITY_FADE_DELAY_NORMAL = 0, - INACTIVITY_FADE_DELAY_SHORT = 1, + enum InactivityTimeout { + INACTIVITY_TIMEOUT_NORMAL = 0, + INACTIVITY_TIMEOUT_SHORT = 1, }; - PointerController(const sp<Looper>& looper, const sp<SpriteController>& spriteController); + PointerController(const sp<PointerControllerPolicyInterface>& policy, + const sp<Looper>& looper, const sp<SpriteController>& spriteController); virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; @@ -96,51 +187,101 @@ public: virtual void fade(); virtual void unfade(); + virtual void setPresentation(Presentation presentation); + virtual void setSpots(SpotGesture spotGesture, + const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits); + virtual void clearSpots(); + void setDisplaySize(int32_t width, int32_t height); void setDisplayOrientation(int32_t orientation); - void setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY); - void setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay); + void setPointerIcon(const SpriteIcon& icon); + void setInactivityTimeout(InactivityTimeout inactivityTimeout); private: + static const size_t MAX_RECYCLED_SPRITES = 12; + static const size_t MAX_SPOTS = 12; + enum { - MSG_FADE_STEP = 0, + MSG_ANIMATE, + MSG_INACTIVITY_TIMEOUT, + }; + + struct Spot { + static const uint32_t INVALID_ID = 0xffffffff; + + uint32_t id; + sp<Sprite> sprite; + float alpha; + float scale; + float x, y; + + inline Spot(uint32_t id, const sp<Sprite>& sprite) + : id(id), sprite(sprite), alpha(1.0f), scale(1.0f), + x(0.0f), y(0.0f), lastIcon(NULL) { } + + void updateSprite(const SpriteIcon* icon, float x, float y); + + private: + const SpriteIcon* lastIcon; }; mutable Mutex mLock; + sp<PointerControllerPolicyInterface> mPolicy; sp<Looper> mLooper; sp<SpriteController> mSpriteController; sp<WeakMessageHandler> mHandler; + PointerResources mResources; + struct Locked { + bool animationPending; + nsecs_t animationTime; + int32_t displayWidth; int32_t displayHeight; int32_t displayOrientation; + InactivityTimeout inactivityTimeout; + + Presentation presentation; + bool presentationChanged; + + bool pointerIsFading; float pointerX; float pointerY; - uint32_t buttonState; + float pointerAlpha; + sp<Sprite> pointerSprite; + SpriteIcon pointerIcon; + bool pointerIconChanged; - float fadeAlpha; - InactivityFadeDelay inactivityFadeDelay; - - bool visible; + uint32_t buttonState; - sp<Sprite> sprite; + Vector<Spot*> spots; + Vector<sp<Sprite> > recycledSprites; } mLocked; bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; void setPositionLocked(float x, float y); - void updateLocked(); void handleMessage(const Message& message); - bool unfadeBeforeUpdateLocked(); - void startFadeLocked(); - void startInactivityFadeDelayLocked(); - void fadeStepLocked(); - bool isFadingLocked(); - nsecs_t getInactivityFadeDelayTimeLocked(); - void sendFadeStepMessageDelayedLocked(nsecs_t delayTime); + void doAnimate(); + void doInactivityTimeout(); + + void startAnimationLocked(); + + void resetInactivityTimeoutLocked(); + void sendImmediateInactivityTimeoutLocked(); + void updatePointerLocked(); + + Spot* getSpotLocked(uint32_t id); + Spot* createAndAddSpotLocked(uint32_t id); + Spot* removeFirstFadingSpotLocked(); + void releaseSpotLocked(Spot* spot); + void fadeOutAndReleaseSpotLocked(Spot* spot); + void fadeOutAndReleaseAllSpotsLocked(); + + void loadResources(); }; } // namespace android diff --git a/services/input/SpotController.cpp b/services/input/SpotController.cpp deleted file mode 100644 index dffad81..0000000 --- a/services/input/SpotController.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "SpotController" - -//#define LOG_NDEBUG 0 - -// Log debug messages about spot updates -#define DEBUG_SPOT_UPDATES 0 - -#include "SpotController.h" - -#include <cutils/log.h> - -namespace android { - -// --- SpotController --- - -SpotController::SpotController(const sp<Looper>& looper, - const sp<SpriteController>& spriteController) : - mLooper(looper), mSpriteController(spriteController) { - mHandler = new WeakMessageHandler(this); -} - -SpotController::~SpotController() { - mLooper->removeMessages(mHandler); -} - -void SpotController:: handleMessage(const Message& message) { -} - -} // namespace android diff --git a/services/input/SpotController.h b/services/input/SpotController.h deleted file mode 100644 index 1d091d7..0000000 --- a/services/input/SpotController.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_SPOT_CONTROLLER_H -#define _UI_SPOT_CONTROLLER_H - -#include "SpriteController.h" - -#include <utils/RefBase.h> -#include <utils/Looper.h> - -#include <SkBitmap.h> - -namespace android { - -/* - * Interface for displaying spots on screen that visually represent the positions - * of fingers on a touch pad. - * - * The spot controller is responsible for providing synchronization and for tracking - * display orientation changes if needed. - */ -class SpotControllerInterface : public virtual RefBase { -protected: - SpotControllerInterface() { } - virtual ~SpotControllerInterface() { } - -public: - -}; - - -/* - * Sprite-based spot controller implementation. - */ -class SpotController : public SpotControllerInterface, public MessageHandler { -protected: - virtual ~SpotController(); - -public: - SpotController(const sp<Looper>& looper, const sp<SpriteController>& spriteController); - -private: - mutable Mutex mLock; - - sp<Looper> mLooper; - sp<SpriteController> mSpriteController; - sp<WeakMessageHandler> mHandler; - - struct Locked { - } mLocked; - - void handleMessage(const Message& message); -}; - -} // namespace android - -#endif // _UI_SPOT_CONTROLLER_H diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp index c6d4390..2fd1f0a 100644 --- a/services/input/SpriteController.cpp +++ b/services/input/SpriteController.cpp @@ -36,6 +36,9 @@ namespace android { SpriteController::SpriteController(const sp<Looper>& looper, int32_t overlayLayer) : mLooper(looper), mOverlayLayer(overlayLayer) { mHandler = new WeakMessageHandler(this); + + mLocked.transactionNestingCount = 0; + mLocked.deferredSpriteUpdate = false; } SpriteController::~SpriteController() { @@ -51,17 +54,40 @@ sp<Sprite> SpriteController::createSprite() { return new SpriteImpl(this); } +void SpriteController::openTransaction() { + AutoMutex _l(mLock); + + mLocked.transactionNestingCount += 1; +} + +void SpriteController::closeTransaction() { + AutoMutex _l(mLock); + + LOG_ALWAYS_FATAL_IF(mLocked.transactionNestingCount == 0, + "Sprite closeTransaction() called but there is no open sprite transaction"); + + mLocked.transactionNestingCount -= 1; + if (mLocked.transactionNestingCount == 0 && mLocked.deferredSpriteUpdate) { + mLocked.deferredSpriteUpdate = false; + mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES)); + } +} + void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) { - bool wasEmpty = mInvalidatedSprites.isEmpty(); - mInvalidatedSprites.push(sprite); + bool wasEmpty = mLocked.invalidatedSprites.isEmpty(); + mLocked.invalidatedSprites.push(sprite); if (wasEmpty) { - mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES)); + if (mLocked.transactionNestingCount != 0) { + mLocked.deferredSpriteUpdate = true; + } else { + mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES)); + } } } void SpriteController::disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl) { - bool wasEmpty = mDisposedSurfaces.isEmpty(); - mDisposedSurfaces.push(surfaceControl); + bool wasEmpty = mLocked.disposedSurfaces.isEmpty(); + mLocked.disposedSurfaces.push(surfaceControl); if (wasEmpty) { mLooper->sendMessage(mHandler, Message(MSG_DISPOSE_SURFACES)); } @@ -89,14 +115,14 @@ void SpriteController::doUpdateSprites() { { // acquire lock AutoMutex _l(mLock); - numSprites = mInvalidatedSprites.size(); + numSprites = mLocked.invalidatedSprites.size(); for (size_t i = 0; i < numSprites; i++) { - const sp<SpriteImpl>& sprite = mInvalidatedSprites.itemAt(i); + const sp<SpriteImpl>& sprite = mLocked.invalidatedSprites.itemAt(i); updates.push(SpriteUpdate(sprite, sprite->getStateLocked())); sprite->resetDirtyLocked(); } - mInvalidatedSprites.clear(); + mLocked.invalidatedSprites.clear(); } // release lock // Create missing surfaces. @@ -105,8 +131,8 @@ void SpriteController::doUpdateSprites() { SpriteUpdate& update = updates.editItemAt(i); if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) { - update.state.surfaceWidth = update.state.bitmap.width(); - update.state.surfaceHeight = update.state.bitmap.height(); + update.state.surfaceWidth = update.state.icon.bitmap.width(); + update.state.surfaceHeight = update.state.icon.bitmap.height(); update.state.surfaceDrawn = false; update.state.surfaceVisible = false; update.state.surfaceControl = obtainSurface( @@ -123,8 +149,8 @@ void SpriteController::doUpdateSprites() { SpriteUpdate& update = updates.editItemAt(i); if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) { - int32_t desiredWidth = update.state.bitmap.width(); - int32_t desiredHeight = update.state.bitmap.height(); + int32_t desiredWidth = update.state.icon.bitmap.width(); + int32_t desiredHeight = update.state.icon.bitmap.height(); if (update.state.surfaceWidth < desiredWidth || update.state.surfaceHeight < desiredHeight) { if (!haveGlobalTransaction) { @@ -187,16 +213,16 @@ void SpriteController::doUpdateSprites() { SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); - surfaceCanvas.drawBitmap(update.state.bitmap, 0, 0, &paint); + surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint); - if (surfaceInfo.w > uint32_t(update.state.bitmap.width())) { + if (surfaceInfo.w > uint32_t(update.state.icon.bitmap.width())) { paint.setColor(0); // transparent fill color - surfaceCanvas.drawRectCoords(update.state.bitmap.width(), 0, - surfaceInfo.w, update.state.bitmap.height(), paint); + surfaceCanvas.drawRectCoords(update.state.icon.bitmap.width(), 0, + surfaceInfo.w, update.state.icon.bitmap.height(), paint); } - if (surfaceInfo.h > uint32_t(update.state.bitmap.height())) { + if (surfaceInfo.h > uint32_t(update.state.icon.bitmap.height())) { paint.setColor(0); // transparent fill color - surfaceCanvas.drawRectCoords(0, update.state.bitmap.height(), + surfaceCanvas.drawRectCoords(0, update.state.icon.bitmap.height(), surfaceInfo.w, surfaceInfo.h, paint); } @@ -246,8 +272,8 @@ void SpriteController::doUpdateSprites() { && (becomingVisible || (update.state.dirty & (DIRTY_POSITION | DIRTY_HOTSPOT)))) { status = update.state.surfaceControl->setPosition( - update.state.positionX - update.state.hotSpotX, - update.state.positionY - update.state.hotSpotY); + update.state.positionX - update.state.icon.hotSpotX, + update.state.positionY - update.state.icon.hotSpotY); if (status) { LOGE("Error %d setting sprite surface position.", status); } @@ -329,8 +355,10 @@ void SpriteController::doDisposeSurfaces() { // Collect disposed surfaces. Vector<sp<SurfaceControl> > disposedSurfaces; { // acquire lock - disposedSurfaces = mDisposedSurfaces; - mDisposedSurfaces.clear(); + AutoMutex _l(mLock); + + disposedSurfaces = mLocked.disposedSurfaces; + mLocked.disposedSurfaces.clear(); } // release lock // Release the last reference to each surface outside of the lock. @@ -349,7 +377,8 @@ sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface( getpid(), String8("Sprite"), 0, width, height, PIXEL_FORMAT_RGBA_8888); - if (surfaceControl == NULL) { + if (surfaceControl == NULL || !surfaceControl->isValid() + || !surfaceControl->getSurface()->isValid()) { LOGE("Error creating sprite surface."); return NULL; } @@ -360,7 +389,7 @@ sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height // --- SpriteController::SpriteImpl --- SpriteController::SpriteImpl::SpriteImpl(const sp<SpriteController> controller) : - mController(controller), mTransactionNestingCount(0) { + mController(controller) { } SpriteController::SpriteImpl::~SpriteImpl() { @@ -368,27 +397,33 @@ SpriteController::SpriteImpl::~SpriteImpl() { // Let the controller take care of deleting the last reference to sprite // surfaces so that we do not block the caller on an IPC here. - if (mState.surfaceControl != NULL) { - mController->disposeSurfaceLocked(mState.surfaceControl); - mState.surfaceControl.clear(); + if (mLocked.state.surfaceControl != NULL) { + mController->disposeSurfaceLocked(mLocked.state.surfaceControl); + mLocked.state.surfaceControl.clear(); } } -void SpriteController::SpriteImpl::setBitmap(const SkBitmap* bitmap, - float hotSpotX, float hotSpotY) { +void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) { AutoMutex _l(mController->mLock); - if (bitmap) { - bitmap->copyTo(&mState.bitmap, SkBitmap::kARGB_8888_Config); + uint32_t dirty; + if (icon.isValid()) { + icon.bitmap.copyTo(&mLocked.state.icon.bitmap, SkBitmap::kARGB_8888_Config); + + if (!mLocked.state.icon.isValid() + || mLocked.state.icon.hotSpotX != icon.hotSpotX + || mLocked.state.icon.hotSpotY != icon.hotSpotY) { + mLocked.state.icon.hotSpotX = icon.hotSpotX; + mLocked.state.icon.hotSpotY = icon.hotSpotY; + dirty = DIRTY_BITMAP | DIRTY_HOTSPOT; + } else { + dirty = DIRTY_BITMAP; + } + } else if (mLocked.state.icon.isValid()) { + mLocked.state.icon.bitmap.reset(); + dirty = DIRTY_BITMAP | DIRTY_HOTSPOT; } else { - mState.bitmap.reset(); - } - - uint32_t dirty = DIRTY_BITMAP; - if (mState.hotSpotX != hotSpotX || mState.hotSpotY != hotSpotY) { - mState.hotSpotX = hotSpotX; - mState.hotSpotY = hotSpotY; - dirty |= DIRTY_HOTSPOT; + return; // setting to invalid icon and already invalid so nothing to do } invalidateLocked(dirty); @@ -397,8 +432,8 @@ void SpriteController::SpriteImpl::setBitmap(const SkBitmap* bitmap, void SpriteController::SpriteImpl::setVisible(bool visible) { AutoMutex _l(mController->mLock); - if (mState.visible != visible) { - mState.visible = visible; + if (mLocked.state.visible != visible) { + mLocked.state.visible = visible; invalidateLocked(DIRTY_VISIBILITY); } } @@ -406,9 +441,9 @@ void SpriteController::SpriteImpl::setVisible(bool visible) { void SpriteController::SpriteImpl::setPosition(float x, float y) { AutoMutex _l(mController->mLock); - if (mState.positionX != x || mState.positionY != y) { - mState.positionX = x; - mState.positionY = y; + if (mLocked.state.positionX != x || mLocked.state.positionY != y) { + mLocked.state.positionX = x; + mLocked.state.positionY = y; invalidateLocked(DIRTY_POSITION); } } @@ -416,8 +451,8 @@ void SpriteController::SpriteImpl::setPosition(float x, float y) { void SpriteController::SpriteImpl::setLayer(int32_t layer) { AutoMutex _l(mController->mLock); - if (mState.layer != layer) { - mState.layer = layer; + if (mLocked.state.layer != layer) { + mLocked.state.layer = layer; invalidateLocked(DIRTY_LAYER); } } @@ -425,8 +460,8 @@ void SpriteController::SpriteImpl::setLayer(int32_t layer) { void SpriteController::SpriteImpl::setAlpha(float alpha) { AutoMutex _l(mController->mLock); - if (mState.alpha != alpha) { - mState.alpha = alpha; + if (mLocked.state.alpha != alpha) { + mLocked.state.alpha = alpha; invalidateLocked(DIRTY_ALPHA); } } @@ -435,38 +470,19 @@ void SpriteController::SpriteImpl::setTransformationMatrix( const SpriteTransformationMatrix& matrix) { AutoMutex _l(mController->mLock); - if (mState.transformationMatrix != matrix) { - mState.transformationMatrix = matrix; + if (mLocked.state.transformationMatrix != matrix) { + mLocked.state.transformationMatrix = matrix; invalidateLocked(DIRTY_TRANSFORMATION_MATRIX); } } -void SpriteController::SpriteImpl::openTransaction() { - AutoMutex _l(mController->mLock); - - mTransactionNestingCount += 1; -} - -void SpriteController::SpriteImpl::closeTransaction() { - AutoMutex _l(mController->mLock); - - LOG_ALWAYS_FATAL_IF(mTransactionNestingCount == 0, - "Sprite closeTransaction() called but there is no open sprite transaction"); +void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) { + bool wasDirty = mLocked.state.dirty; + mLocked.state.dirty |= dirty; - mTransactionNestingCount -= 1; - if (mTransactionNestingCount == 0 && mState.dirty) { + if (!wasDirty) { mController->invalidateSpriteLocked(this); } } -void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) { - if (mTransactionNestingCount > 0) { - bool wasDirty = mState.dirty; - mState.dirty |= dirty; - if (!wasDirty) { - mController->invalidateSpriteLocked(this); - } - } -} - } // namespace android diff --git a/services/input/SpriteController.h b/services/input/SpriteController.h index 27afb5e..50ae8a5 100644 --- a/services/input/SpriteController.h +++ b/services/input/SpriteController.h @@ -33,6 +33,8 @@ namespace android { */ struct SpriteTransformationMatrix { inline SpriteTransformationMatrix() : dsdx(1.0f), dtdx(0.0f), dsdy(0.0f), dtdy(1.0f) { } + inline SpriteTransformationMatrix(float dsdx, float dtdx, float dsdy, float dtdy) : + dsdx(dsdx), dtdx(dtdx), dsdy(dsdy), dtdy(dtdy) { } float dsdx; float dtdx; @@ -52,6 +54,35 @@ struct SpriteTransformationMatrix { }; /* + * Icon that a sprite displays, including its hotspot. + */ +struct SpriteIcon { + inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { } + inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) : + bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { } + + SkBitmap bitmap; + float hotSpotX; + float hotSpotY; + + inline SpriteIcon copy() const { + SkBitmap bitmapCopy; + bitmap.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config); + return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY); + } + + inline void reset() { + bitmap.reset(); + hotSpotX = 0; + hotSpotY = 0; + } + + inline bool isValid() const { + return !bitmap.isNull() && !bitmap.empty(); + } +}; + +/* * A sprite is a simple graphical object that is displayed on-screen above other layers. * The basic sprite class is an interface. * The implementation is provided by the sprite controller. @@ -62,9 +93,21 @@ protected: virtual ~Sprite() { } public: + enum { + // The base layer for pointer sprites. + BASE_LAYER_POINTER = 0, // reserve space for 1 pointer + + // The base layer for spot sprites. + BASE_LAYER_SPOT = 1, // reserve space for MAX_POINTER_ID spots + }; + /* Sets the bitmap that is drawn by the sprite. * The sprite retains a copy of the bitmap for subsequent rendering. */ - virtual void setBitmap(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) = 0; + virtual void setIcon(const SpriteIcon& icon) = 0; + + inline void clearIcon() { + setIcon(SpriteIcon()); + } /* Sets whether the sprite is visible. */ virtual void setVisible(bool visible) = 0; @@ -81,14 +124,6 @@ public: /* Sets the sprite transformation matrix. */ virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0; - - /* Opens or closes a transaction to perform a batch of sprite updates as part of - * a single operation such as setPosition and setAlpha. It is not necessary to - * open a transaction when updating a single property. - * Calls to openTransaction() nest and must be matched by an equal number - * of calls to closeTransaction(). */ - virtual void openTransaction() = 0; - virtual void closeTransaction() = 0; }; /* @@ -112,6 +147,14 @@ public: /* Creates a new sprite, initially invisible. */ sp<Sprite> createSprite(); + /* Opens or closes a transaction to perform a batch of sprite updates as part of + * a single operation such as setPosition and setAlpha. It is not necessary to + * open a transaction when updating a single property. + * Calls to openTransaction() nest and must be matched by an equal number + * of calls to closeTransaction(). */ + void openTransaction(); + void closeTransaction(); + private: enum { MSG_UPDATE_SPRITES, @@ -135,16 +178,14 @@ private: * Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */ struct SpriteState { inline SpriteState() : - dirty(0), hotSpotX(0), hotSpotY(0), visible(false), + dirty(0), visible(false), positionX(0), positionY(0), layer(0), alpha(1.0f), surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) { } uint32_t dirty; - SkBitmap bitmap; - float hotSpotX; - float hotSpotY; + SpriteIcon icon; bool visible; float positionX; float positionY; @@ -159,7 +200,7 @@ private: bool surfaceVisible; inline bool wantSurfaceVisible() const { - return visible && alpha > 0.0f && !bitmap.isNull() && !bitmap.empty(); + return visible && alpha > 0.0f && icon.isValid(); } }; @@ -177,37 +218,36 @@ private: public: SpriteImpl(const sp<SpriteController> controller); - virtual void setBitmap(const SkBitmap* bitmap, float hotSpotX, float hotSpotY); + virtual void setIcon(const SpriteIcon& icon); virtual void setVisible(bool visible); virtual void setPosition(float x, float y); virtual void setLayer(int32_t layer); virtual void setAlpha(float alpha); virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix); - virtual void openTransaction(); - virtual void closeTransaction(); inline const SpriteState& getStateLocked() const { - return mState; + return mLocked.state; } inline void resetDirtyLocked() { - mState.dirty = 0; + mLocked.state.dirty = 0; } inline void setSurfaceLocked(const sp<SurfaceControl>& surfaceControl, int32_t width, int32_t height, bool drawn, bool visible) { - mState.surfaceControl = surfaceControl; - mState.surfaceWidth = width; - mState.surfaceHeight = height; - mState.surfaceDrawn = drawn; - mState.surfaceVisible = visible; + mLocked.state.surfaceControl = surfaceControl; + mLocked.state.surfaceWidth = width; + mLocked.state.surfaceHeight = height; + mLocked.state.surfaceDrawn = drawn; + mLocked.state.surfaceVisible = visible; } private: sp<SpriteController> mController; - SpriteState mState; // guarded by mController->mLock - uint32_t mTransactionNestingCount; // guarded by mController->mLock + struct Locked { + SpriteState state; + } mLocked; // guarded by mController->mLock void invalidateLocked(uint32_t dirty); }; @@ -232,8 +272,12 @@ private: sp<SurfaceComposerClient> mSurfaceComposerClient; - Vector<sp<SpriteImpl> > mInvalidatedSprites; // guarded by mLock - Vector<sp<SurfaceControl> > mDisposedSurfaces; // guarded by mLock + struct Locked { + Vector<sp<SpriteImpl> > invalidatedSprites; + Vector<sp<SurfaceControl> > disposedSurfaces; + uint32_t transactionNestingCount; + bool deferredSpriteUpdate; + } mLocked; // guarded by mLock void invalidateSpriteLocked(const sp<SpriteImpl>& sprite); void disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl); diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index ba8ca9c..54bb9d7 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -97,6 +97,16 @@ private: virtual void unfade() { } + + virtual void setPresentation(Presentation presentation) { + } + + virtual void setSpots(SpotGesture spotGesture, + const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { + } + + virtual void clearSpots() { + } }; @@ -192,10 +202,6 @@ private: virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) { return mPointerControllers.valueFor(deviceId); } - - virtual sp<SpotControllerInterface> obtainSpotController(int32_t device) { - return NULL; - } }; diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java index b0978a3..69bde41 100644 --- a/services/java/com/android/server/wm/InputManager.java +++ b/services/java/com/android/server/wm/InputManager.java @@ -23,12 +23,6 @@ import org.xmlpull.v1.XmlPullParser; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Configuration; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; import android.os.Environment; import android.os.Looper; import android.os.MessageQueue; @@ -39,6 +33,7 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; import android.view.KeyEvent; +import android.view.PointerIcon; import android.view.Surface; import android.view.ViewConfiguration; import android.view.WindowManager; @@ -63,7 +58,8 @@ public class InputManager { private final Context mContext; private final WindowManagerService mWindowManagerService; - private static native void nativeInit(Callbacks callbacks, MessageQueue messageQueue); + private static native void nativeInit(Context context, + Callbacks callbacks, MessageQueue messageQueue); private static native void nativeStart(); private static native void nativeSetDisplaySize(int displayId, int width, int height); private static native void nativeSetDisplayOrientation(int displayId, int rotation); @@ -133,7 +129,7 @@ public class InputManager { Looper looper = windowManagerService.mH.getLooper(); Slog.i(TAG, "Initializing input manager"); - nativeInit(mCallbacks, looper.getQueue()); + nativeInit(mContext, mCallbacks, looper.getQueue()); } public void start() { @@ -435,48 +431,6 @@ public class InputManager { } } - private static final class PointerIcon { - public Bitmap bitmap; - public float hotSpotX; - public float hotSpotY; - - public static PointerIcon load(Resources resources, int resourceId) { - PointerIcon icon = new PointerIcon(); - - XmlResourceParser parser = resources.getXml(resourceId); - final int bitmapRes; - try { - XmlUtils.beginDocument(parser, "pointer-icon"); - - TypedArray a = resources.obtainAttributes( - parser, com.android.internal.R.styleable.PointerIcon); - bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0); - icon.hotSpotX = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0); - icon.hotSpotY = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0); - a.recycle(); - } catch (Exception ex) { - Slog.e(TAG, "Exception parsing pointer icon resource.", ex); - return null; - } finally { - parser.close(); - } - - if (bitmapRes == 0) { - Slog.e(TAG, "<pointer-icon> is missing bitmap attribute"); - return null; - } - - Drawable drawable = resources.getDrawable(bitmapRes); - if (!(drawable instanceof BitmapDrawable)) { - Slog.e(TAG, "<pointer-icon> bitmap attribute must refer to a bitmap drawable"); - return null; - } - - icon.bitmap = ((BitmapDrawable)drawable).getBitmap(); - return icon; - } - } - /* * Callbacks from native. */ @@ -641,8 +595,7 @@ public class InputManager { @SuppressWarnings("unused") public PointerIcon getPointerIcon() { - return PointerIcon.load(mContext.getResources(), - com.android.internal.R.drawable.pointer_arrow_icon); + return PointerIcon.getDefaultIcon(mContext); } } } diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index aaa305e..1f10d9c 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -36,13 +36,13 @@ #include <input/InputManager.h> #include <input/PointerController.h> -#include <input/SpotController.h> #include <input/SpriteController.h> #include <android_os_MessageQueue.h> #include <android_view_KeyEvent.h> #include <android_view_MotionEvent.h> #include <android_view_InputChannel.h> +#include <android_view_PointerIcon.h> #include <android/graphics/GraphicsJNI.h> #include "com_android_server_PowerManagerService.h" @@ -101,12 +101,6 @@ static struct { jfieldID navigation; } gConfigurationClassInfo; -static struct { - jfieldID bitmap; - jfieldID hotSpotX; - jfieldID hotSpotY; -} gPointerIconClassInfo; - // --- Global functions --- @@ -128,17 +122,30 @@ static jobject getInputWindowHandleObjLocalRef(JNIEnv* env, getInputWindowHandleObjLocalRef(env); } +static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t style, + SpriteIcon* outSpriteIcon) { + PointerIcon pointerIcon; + status_t status = android_view_PointerIcon_loadSystemIcon(env, + contextObj, style, &pointerIcon); + if (!status) { + pointerIcon.bitmap.copyTo(&outSpriteIcon->bitmap, SkBitmap::kARGB_8888_Config); + outSpriteIcon->hotSpotX = pointerIcon.hotSpotX; + outSpriteIcon->hotSpotY = pointerIcon.hotSpotY; + } +} + // --- NativeInputManager --- class NativeInputManager : public virtual RefBase, public virtual InputReaderPolicyInterface, - public virtual InputDispatcherPolicyInterface { + public virtual InputDispatcherPolicyInterface, + public virtual PointerControllerPolicyInterface { protected: virtual ~NativeInputManager(); public: - NativeInputManager(jobject callbacksObj, const sp<Looper>& looper); + NativeInputManager(jobject contextObj, jobject callbacksObj, const sp<Looper>& looper); inline sp<InputManager> getInputManager() const { return mInputManager; } @@ -165,7 +172,6 @@ public: virtual nsecs_t getVirtualKeyQuietTime(); virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames); virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId); - virtual sp<SpotControllerInterface> obtainSpotController(int32_t deviceId); /* --- InputDispatcherPolicyInterface implementation --- */ @@ -189,9 +195,14 @@ public: virtual bool checkInjectEventsPermissionNonReentrant( int32_t injectorPid, int32_t injectorUid); + /* --- PointerControllerPolicyInterface implementation --- */ + + virtual void loadPointerResources(PointerResources* outResources); + private: sp<InputManager> mInputManager; + jobject mContextObj; jobject mCallbacksObj; sp<Looper> mLooper; @@ -223,7 +234,7 @@ private: wp<PointerController> pointerController; } mLocked; - void updateInactivityFadeDelayLocked(const sp<PointerController>& controller); + void updateInactivityTimeoutLocked(const sp<PointerController>& controller); void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags); void ensureSpriteControllerLocked(); @@ -240,13 +251,15 @@ private: -NativeInputManager::NativeInputManager(jobject callbacksObj, const sp<Looper>& looper) : +NativeInputManager::NativeInputManager(jobject contextObj, + jobject callbacksObj, const sp<Looper>& looper) : mLooper(looper), mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1), mKeyRepeatTimeout(-1), mKeyRepeatDelay(-1), mMaxEventsPerSecond(-1) { JNIEnv* env = jniEnv(); + mContextObj = env->NewGlobalRef(contextObj); mCallbacksObj = env->NewGlobalRef(callbacksObj); { @@ -265,6 +278,7 @@ NativeInputManager::NativeInputManager(jobject callbacksObj, const sp<Looper>& l NativeInputManager::~NativeInputManager() { JNIEnv* env = jniEnv(); + env->DeleteGlobalRef(mContextObj); env->DeleteGlobalRef(mCallbacksObj); } @@ -288,9 +302,13 @@ bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const c void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_t height) { if (displayId == 0) { - AutoMutex _l(mLock); + { // acquire lock + AutoMutex _l(mLock); + + if (mLocked.displayWidth == width && mLocked.displayHeight == height) { + return; + } - if (mLocked.displayWidth != width || mLocked.displayHeight != height) { mLocked.displayWidth = width; mLocked.displayHeight = height; @@ -298,7 +316,7 @@ void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_ if (controller != NULL) { controller->setDisplaySize(width, height); } - } + } // release lock } } @@ -428,40 +446,33 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32 if (controller == NULL) { ensureSpriteControllerLocked(); - controller = new PointerController(mLooper, mLocked.spriteController); + controller = new PointerController(this, mLooper, mLocked.spriteController); mLocked.pointerController = controller; controller->setDisplaySize(mLocked.displayWidth, mLocked.displayHeight); controller->setDisplayOrientation(mLocked.displayOrientation); JNIEnv* env = jniEnv(); - jobject iconObj = env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getPointerIcon); - if (!checkAndClearExceptionFromCallback(env, "getPointerIcon") && iconObj) { - jfloat iconHotSpotX = env->GetFloatField(iconObj, gPointerIconClassInfo.hotSpotX); - jfloat iconHotSpotY = env->GetFloatField(iconObj, gPointerIconClassInfo.hotSpotY); - jobject iconBitmapObj = env->GetObjectField(iconObj, gPointerIconClassInfo.bitmap); - if (iconBitmapObj) { - SkBitmap* iconBitmap = GraphicsJNI::getNativeBitmap(env, iconBitmapObj); - if (iconBitmap) { - controller->setPointerIcon(iconBitmap, iconHotSpotX, iconHotSpotY); - } - env->DeleteLocalRef(iconBitmapObj); + jobject pointerIconObj = env->CallObjectMethod(mCallbacksObj, + gCallbacksClassInfo.getPointerIcon); + if (!checkAndClearExceptionFromCallback(env, "getPointerIcon")) { + PointerIcon pointerIcon; + status_t status = android_view_PointerIcon_load(env, pointerIconObj, + mContextObj, &pointerIcon); + if (!status && !pointerIcon.isNullIcon()) { + controller->setPointerIcon(SpriteIcon(pointerIcon.bitmap, + pointerIcon.hotSpotX, pointerIcon.hotSpotY)); + } else { + controller->setPointerIcon(SpriteIcon()); } - env->DeleteLocalRef(iconObj); + env->DeleteLocalRef(pointerIconObj); } - updateInactivityFadeDelayLocked(controller); + updateInactivityTimeoutLocked(controller); } return controller; } -sp<SpotControllerInterface> NativeInputManager::obtainSpotController(int32_t deviceId) { - AutoMutex _l(mLock); - - ensureSpriteControllerLocked(); - return new SpotController(mLooper, mLocked.spriteController); -} - void NativeInputManager::ensureSpriteControllerLocked() { if (mLocked.spriteController == NULL) { JNIEnv* env = jniEnv(); @@ -642,16 +653,16 @@ void NativeInputManager::setSystemUiVisibility(int32_t visibility) { sp<PointerController> controller = mLocked.pointerController.promote(); if (controller != NULL) { - updateInactivityFadeDelayLocked(controller); + updateInactivityTimeoutLocked(controller); } } } -void NativeInputManager::updateInactivityFadeDelayLocked(const sp<PointerController>& controller) { +void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller) { bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN; - controller->setInactivityFadeDelay(lightsOut - ? PointerController::INACTIVITY_FADE_DELAY_SHORT - : PointerController::INACTIVITY_FADE_DELAY_NORMAL); + controller->setInactivityTimeout(lightsOut + ? PointerController::INACTIVITY_TIMEOUT_SHORT + : PointerController::INACTIVITY_TIMEOUT_NORMAL); } bool NativeInputManager::isScreenOn() { @@ -884,6 +895,17 @@ bool NativeInputManager::checkInjectEventsPermissionNonReentrant( return result; } +void NativeInputManager::loadPointerResources(PointerResources* outResources) { + JNIEnv* env = jniEnv(); + + loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_HOVER, + &outResources->spotHover); + loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_TOUCH, + &outResources->spotTouch); + loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_ANCHOR, + &outResources->spotAnchor); +} + // ---------------------------------------------------------------------------- @@ -899,10 +921,10 @@ static bool checkInputManagerUnitialized(JNIEnv* env) { } static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz, - jobject callbacks, jobject messageQueueObj) { + jobject contextObj, jobject callbacksObj, jobject messageQueueObj) { if (gNativeInputManager == NULL) { sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj); - gNativeInputManager = new NativeInputManager(callbacks, looper); + gNativeInputManager = new NativeInputManager(contextObj, callbacksObj, looper); } else { LOGE("Input manager already initialized."); jniThrowRuntimeException(env, "Input manager already initialized."); @@ -1246,7 +1268,8 @@ static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) static JNINativeMethod gInputManagerMethods[] = { /* name, signature, funcPtr */ - { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V", + { "nativeInit", "(Landroid/content/Context;" + "Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V", (void*) android_server_InputManager_nativeInit }, { "nativeStart", "()V", (void*) android_server_InputManager_nativeStart }, @@ -1372,7 +1395,7 @@ int register_android_server_InputManager(JNIEnv* env) { "getPointerLayer", "()I"); GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, clazz, - "getPointerIcon", "()Lcom/android/server/wm/InputManager$PointerIcon;"); + "getPointerIcon", "()Landroid/view/PointerIcon;"); // KeyEvent @@ -1421,19 +1444,6 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gConfigurationClassInfo.navigation, clazz, "navigation", "I"); - // PointerIcon - - FIND_CLASS(clazz, "com/android/server/wm/InputManager$PointerIcon"); - - GET_FIELD_ID(gPointerIconClassInfo.bitmap, clazz, - "bitmap", "Landroid/graphics/Bitmap;"); - - GET_FIELD_ID(gPointerIconClassInfo.hotSpotX, clazz, - "hotSpotX", "F"); - - GET_FIELD_ID(gPointerIconClassInfo.hotSpotY, clazz, - "hotSpotY", "F"); - return 0; } |