summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2011-04-12 22:39:53 -0700
committerJeff Brown <jeffbrown@android.com>2011-05-25 14:37:17 -0700
commit86ea1f5f521981d075aef56f11693e4f3bc32fdb (patch)
tree72e1e44513d3e9e204a4fbd2855a079822e949eb /services
parenta6dbfdd3a858aac52cc87f80f91e8eef7d613605 (diff)
downloadframeworks_base-86ea1f5f521981d075aef56f11693e4f3bc32fdb.zip
frameworks_base-86ea1f5f521981d075aef56f11693e4f3bc32fdb.tar.gz
frameworks_base-86ea1f5f521981d075aef56f11693e4f3bc32fdb.tar.bz2
Initial checkin of spot presentation for touchpad gestures. (DO NOT MERGE)
Added a new PointerIcon API (hidden for now) for loading pointer icons. Fixed a starvation problem in the native Looper's sendMessage implementation which caused new messages to be posted ahead of old messages sent with sendMessageDelayed. Redesigned the touch pad gestures to be defined in terms of more fluid finger / spot movements. The objective is to reinforce the natural mapping between fingers and spots which means there must not be any discontinuities in spot motion relative to the fingers. Removed the SpotController stub and folded its responsibilities into PointerController. Change-Id: Ib647dbd7a57a7f30dd9c6e2c260df51d7bbdd18e
Diffstat (limited to 'services')
-rw-r--r--services/input/Android.mk1
-rw-r--r--services/input/InputReader.cpp764
-rw-r--r--services/input/InputReader.h87
-rw-r--r--services/input/PointerController.cpp373
-rw-r--r--services/input/PointerController.h187
-rw-r--r--services/input/SpotController.cpp45
-rw-r--r--services/input/SpotController.h71
-rw-r--r--services/input/SpriteController.cpp162
-rw-r--r--services/input/SpriteController.h100
-rw-r--r--services/input/tests/InputReader_test.cpp14
-rw-r--r--services/java/com/android/server/wm/InputManager.java57
-rw-r--r--services/jni/com_android_server_InputManager.cpp128
12 files changed, 1263 insertions, 726 deletions
diff --git a/services/input/Android.mk b/services/input/Android.mk
index 58b5318..20b45cb 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 94753bf..5994380 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.
@@ -1742,8 +1794,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
}
@@ -1816,6 +1868,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.
@@ -1974,7 +2030,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) {
@@ -2169,30 +2225,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();
+ }
}
}
@@ -2619,6 +2684,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();
@@ -3061,7 +3131,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;
@@ -3146,22 +3216,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(
@@ -3275,11 +3358,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();
@@ -3319,77 +3399,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;
}
}
@@ -3404,10 +3458,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.
//
@@ -3440,7 +3501,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;
@@ -3449,6 +3510,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);
@@ -3465,6 +3527,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);
}
}
@@ -3473,7 +3539,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;
@@ -3481,26 +3546,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);
@@ -3508,17 +3596,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 {
@@ -3528,14 +3629,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)
@@ -3556,6 +3665,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);
}
@@ -3566,7 +3678,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;
@@ -3577,159 +3688,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;
}
- } 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.
- //
- // 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.
- assert(activeTouchId >= 0);
- uint32_t currentTouchPointerCount = mCurrentTouch.pointerCount;
- if (currentTouchPointerCount > MAX_POINTERS) {
- currentTouchPointerCount = MAX_POINTERS;
+ 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. (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.
+ //
+ // 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);
- 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;
+ } 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;
- 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 {
+ // 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
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
assert(mPointerGesture.activeGestureId >= 0);
- mPointerGesture.currentGesturePointerCount = currentTouchPointerCount;
mPointerGesture.currentGestureIdBits.clear();
BitSet32 mappedTouchIdBits;
@@ -3773,7 +3993,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)) {
@@ -3796,10 +4016,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(
@@ -3818,30 +4038,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);
@@ -3865,6 +4100,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 6feb2c7..1596e54 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 ca1da95..becd44a 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;
@@ -62,7 +57,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);
@@ -125,7 +121,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() {
@@ -370,48 +366,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.
*/
@@ -564,8 +518,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 7985fab..dde4776 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"
@@ -104,14 +104,6 @@ static struct {
jfieldID navigation;
} gConfigurationClassInfo;
-static struct {
- jclass clazz;
-
- jfieldID bitmap;
- jfieldID hotSpotX;
- jfieldID hotSpotY;
-} gPointerIconClassInfo;
-
// --- Global functions ---
@@ -133,17 +125,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; }
@@ -170,7 +175,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 --- */
@@ -193,9 +197,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;
@@ -227,7 +236,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();
@@ -244,13 +253,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);
{
@@ -269,6 +280,7 @@ NativeInputManager::NativeInputManager(jobject callbacksObj, const sp<Looper>& l
NativeInputManager::~NativeInputManager() {
JNIEnv* env = jniEnv();
+ env->DeleteGlobalRef(mContextObj);
env->DeleteGlobalRef(mCallbacksObj);
}
@@ -292,9 +304,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;
@@ -302,7 +318,7 @@ void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_
if (controller != NULL) {
controller->setDisplaySize(width, height);
}
- }
+ } // release lock
}
}
@@ -432,40 +448,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();
@@ -646,16 +655,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() {
@@ -856,6 +865,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);
+}
+
// ----------------------------------------------------------------------------
@@ -871,10 +891,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.");
@@ -1207,7 +1227,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 },
@@ -1328,7 +1349,7 @@ int register_android_server_InputManager(JNIEnv* env) {
"getPointerLayer", "()I");
GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, gCallbacksClassInfo.clazz,
- "getPointerIcon", "()Lcom/android/server/wm/InputManager$PointerIcon;");
+ "getPointerIcon", "()Landroid/view/PointerIcon;");
// KeyEvent
@@ -1373,19 +1394,6 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz,
"navigation", "I");
- // PointerIcon
-
- FIND_CLASS(gPointerIconClassInfo.clazz, "com/android/server/wm/InputManager$PointerIcon");
-
- GET_FIELD_ID(gPointerIconClassInfo.bitmap, gPointerIconClassInfo.clazz,
- "bitmap", "Landroid/graphics/Bitmap;");
-
- GET_FIELD_ID(gPointerIconClassInfo.hotSpotX, gPointerIconClassInfo.clazz,
- "hotSpotX", "F");
-
- GET_FIELD_ID(gPointerIconClassInfo.hotSpotY, gPointerIconClassInfo.clazz,
- "hotSpotY", "F");
-
return 0;
}