diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/ui/Android.mk | 1 | ||||
-rw-r--r-- | libs/ui/EventHub.cpp | 233 | ||||
-rw-r--r-- | libs/ui/Input.cpp | 59 | ||||
-rw-r--r-- | libs/ui/InputDevice.cpp | 729 | ||||
-rw-r--r-- | libs/ui/InputManager.cpp | 33 | ||||
-rw-r--r-- | libs/ui/InputReader.cpp | 2942 |
6 files changed, 2243 insertions, 1754 deletions
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index 4243bbf..9f49348 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -12,7 +12,6 @@ LOCAL_SRC_FILES:= \ KeyLayoutMap.cpp \ KeyCharacterMap.cpp \ Input.cpp \ - InputDevice.cpp \ InputDispatcher.cpp \ InputManager.cpp \ InputReader.cpp \ diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index 33dd373..124f7b3 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -137,9 +137,14 @@ uint32_t EventHub::getDeviceClasses(int32_t deviceId) const return device->classes; } -int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, - int* outMaxValue, int* outFlat, int* outFuzz) const -{ +status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const { + outAxisInfo->valid = false; + outAxisInfo->minValue = 0; + outAxisInfo->maxValue = 0; + outAxisInfo->flat = 0; + outAxisInfo->fuzz = 0; + AutoMutex _l(mLock); device_t* device = getDevice(deviceId); if (device == NULL) return -1; @@ -147,38 +152,28 @@ int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, struct input_absinfo info; if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) { - LOGE("Error reading absolute controller %d for device %s fd %d\n", + LOGW("Error reading absolute controller %d for device %s fd %d\n", axis, device->name.string(), mFDs[id_to_index(device->id)].fd); - return -1; + return -errno; } - *outMinValue = info.minimum; - *outMaxValue = info.maximum; - *outFlat = info.flat; - *outFuzz = info.fuzz; - return 0; + + if (info.minimum != info.maximum) { + outAxisInfo->valid = true; + outAxisInfo->minValue = info.minimum; + outAxisInfo->maxValue = info.maximum; + outAxisInfo->flat = info.flat; + outAxisInfo->fuzz = info.fuzz; + } + return OK; } -int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t scanCode) const { +int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { if (scanCode >= 0 && scanCode <= KEY_MAX) { AutoMutex _l(mLock); - if (deviceId == -1) { - for (int i = 0; i < mNumDevicesById; i++) { - device_t* device = mDevicesById[i].device; - if (device != NULL && (device->classes & deviceClasses) != 0) { - int32_t result = getScanCodeStateLocked(device, scanCode); - if (result >= AKEY_STATE_DOWN) { - return result; - } - } - } - return AKEY_STATE_UP; - } else { - device_t* device = getDevice(deviceId); - if (device != NULL) { - return getScanCodeStateLocked(device, scanCode); - } + device_t* device = getDevice(deviceId); + if (device != NULL) { + return getScanCodeStateLocked(device, scanCode); } } return AKEY_STATE_UNKNOWN; @@ -194,25 +189,12 @@ int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) con return AKEY_STATE_UNKNOWN; } -int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t keyCode) const { +int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { + AutoMutex _l(mLock); - if (deviceId == -1) { - for (int i = 0; i < mNumDevicesById; i++) { - device_t* device = mDevicesById[i].device; - if (device != NULL && (device->classes & deviceClasses) != 0) { - int32_t result = getKeyCodeStateLocked(device, keyCode); - if (result >= AKEY_STATE_DOWN) { - return result; - } - } - } - return AKEY_STATE_UP; - } else { - device_t* device = getDevice(deviceId); - if (device != NULL) { - return getKeyCodeStateLocked(device, keyCode); - } + device_t* device = getDevice(deviceId); + if (device != NULL) { + return getKeyCodeStateLocked(device, keyCode); } return AKEY_STATE_UNKNOWN; } @@ -243,24 +225,15 @@ int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const return AKEY_STATE_UNKNOWN; } -int32_t EventHub::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const { +int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { #ifdef EV_SW if (sw >= 0 && sw <= SW_MAX) { AutoMutex _l(mLock); - if (deviceId == -1) { - deviceId = mSwitches[sw]; - if (deviceId == 0) { - return AKEY_STATE_UNKNOWN; - } - } - device_t* device = getDevice(deviceId); - if (device == NULL) { - return AKEY_STATE_UNKNOWN; + if (device != NULL) { + return getSwitchStateLocked(device, sw); } - - return getSwitchStateLocked(device, sw); } #endif return AKEY_STATE_UNKNOWN; @@ -276,6 +249,42 @@ int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const { return AKEY_STATE_UNKNOWN; } +bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) const { + AutoMutex _l(mLock); + + device_t* device = getDevice(deviceId); + if (device != NULL) { + return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags); + } + return false; +} + +bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) const { + if (device->layoutMap == NULL || device->keyBitmask == NULL) { + return false; + } + + Vector<int32_t> scanCodes; + for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { + scanCodes.clear(); + + status_t err = device->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes); + if (! err) { + // check the possible scan codes identified by the layout map against the + // map of codes actually emitted by the driver + for (size_t sc = 0; sc < scanCodes.size(); sc++) { + if (test_bit(scanCodes[sc], device->keyBitmask)) { + outFlags[codeIndex] = 1; + break; + } + } + } + } + return true; +} + status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const { @@ -324,17 +333,15 @@ EventHub::device_t* EventHub::getDevice(int32_t deviceId) const return NULL; } -bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, - int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, - int32_t* outValue, nsecs_t* outWhen) +bool EventHub::getEvent(RawEvent* outEvent) { - *outDeviceId = 0; - *outType = 0; - *outScancode = 0; - *outKeycode = 0; - *outFlags = 0; - *outValue = 0; - *outWhen = 0; + outEvent->deviceId = 0; + outEvent->type = 0; + outEvent->scanCode = 0; + outEvent->keyCode = 0; + outEvent->flags = 0; + outEvent->value = 0; + outEvent->when = 0; status_t err; @@ -359,20 +366,27 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, LOGV("Reporting device closed: id=0x%x, name=%s\n", device->id, device->path.string()); mClosingDevices = device->next; - *outDeviceId = device->id; - if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; - *outType = DEVICE_REMOVED; + if (device->id == mFirstKeyboardId) { + outEvent->deviceId = 0; + } else { + outEvent->deviceId = device->id; + } + outEvent->type = DEVICE_REMOVED; delete device; return true; } + if (mOpeningDevices != NULL) { device_t* device = mOpeningDevices; LOGV("Reporting device opened: id=0x%x, name=%s\n", device->id, device->path.string()); mOpeningDevices = device->next; - *outDeviceId = device->id; - if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; - *outType = DEVICE_ADDED; + if (device->id == mFirstKeyboardId) { + outEvent->deviceId = 0; + } else { + outEvent->deviceId = device->id; + } + outEvent->type = DEVICE_ADDED; return true; } @@ -399,27 +413,36 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, if(mFDs[i].revents & POLLIN) { res = read(mFDs[i].fd, &iev, sizeof(iev)); if (res == sizeof(iev)) { + device_t* device = mDevices[i]; LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", - mDevices[i]->path.string(), + device->path.string(), (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); - *outDeviceId = mDevices[i]->id; - if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; - *outType = iev.type; - *outScancode = iev.code; + if (device->id == mFirstKeyboardId) { + outEvent->deviceId = 0; + } else { + outEvent->deviceId = device->id; + } + outEvent->type = iev.type; + outEvent->scanCode = iev.code; if (iev.type == EV_KEY) { - err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags); - LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n", - iev.code, *outKeycode, *outFlags, err); + err = device->layoutMap->map(iev.code, + & outEvent->keyCode, & outEvent->flags); + LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", + iev.code, outEvent->keyCode, outEvent->flags, err); if (err != 0) { - *outKeycode = AKEYCODE_UNKNOWN; - *outFlags = 0; + outEvent->keyCode = AKEYCODE_UNKNOWN; + outEvent->flags = 0; } } else { - *outKeycode = iev.code; + outEvent->keyCode = iev.code; } - *outValue = iev.value; - *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec); + outEvent->value = iev.value; + + // Use an event timestamp in the same timebase as + // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis() + // as expected by the rest of the system. + outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); return true; } else { if (res<0) { @@ -479,37 +502,6 @@ bool EventHub::openPlatformInput(void) return true; } -/* - * Inspect the known devices to determine whether physical keys exist for the given - * framework-domain key codes. - */ -bool EventHub::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { - for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { - outFlags[codeIndex] = 0; - - // check each available hardware device for support for this keycode - Vector<int32_t> scanCodes; - for (int n = 0; (n < mFDCount) && (outFlags[codeIndex] == 0); n++) { - if (mDevices[n]) { - status_t err = mDevices[n]->layoutMap->findScancodes( - keyCodes[codeIndex], &scanCodes); - if (!err) { - // check the possible scan codes identified by the layout map against the - // map of codes actually emitted by the driver - for (size_t sc = 0; sc < scanCodes.size(); sc++) { - if (test_bit(scanCodes[sc], mDevices[n]->keyBitmask)) { - outFlags[codeIndex] = 1; - break; - } - } - } - } - } - } - - return true; -} - // ---------------------------------------------------------------------------- static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) { @@ -715,16 +707,21 @@ int EventHub::open_device(const char *deviceName) // figure out the switches this device reports uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)]; memset(sw_bitmask, 0, sizeof(sw_bitmask)); + bool hasSwitches = false; if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) { for (int i=0; i<EV_SW; i++) { //LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask)); if (test_bit(i, sw_bitmask)) { + hasSwitches = true; if (mSwitches[i] == 0) { mSwitches[i] = device->id; } } } } + if (hasSwitches) { + device->classes |= INPUT_DEVICE_CLASS_SWITCH; + } #endif if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) { diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index 5253c72..5fbaf09 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -168,4 +168,63 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) { mYOffset += yOffset; } +// class InputDeviceInfo + +InputDeviceInfo::InputDeviceInfo() { + initialize(-1, String8("uninitialized device info")); +} + +InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : + mId(other.mId), mName(other.mName), mSources(other.mSources), + mKeyboardType(other.mKeyboardType), + mMotionRanges(other.mMotionRanges) { +} + +InputDeviceInfo::~InputDeviceInfo() { +} + +void InputDeviceInfo::initialize(int32_t id, const String8& name) { + mId = id; + mName = name; + mSources = 0; + mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; + mMotionRanges.clear(); +} + +const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t rangeType) const { + ssize_t index = mMotionRanges.indexOfKey(rangeType); + return index >= 0 ? & mMotionRanges.valueAt(index) : NULL; +} + +void InputDeviceInfo::addSource(uint32_t source) { + mSources |= source; +} + +void InputDeviceInfo::addMotionRange(int32_t rangeType, float min, float max, + float flat, float fuzz) { + MotionRange range = { min, max, flat, fuzz }; + addMotionRange(rangeType, range); +} + +void InputDeviceInfo::addMotionRange(int32_t rangeType, const MotionRange& range) { + mMotionRanges.add(rangeType, range); +} + +// class InputDeviceProxy + +InputDeviceProxy::InputDeviceProxy() { +} + +InputDeviceProxy::~InputDeviceProxy() { +} + +void InputDeviceProxy::getDeviceIds(Vector<int32_t>& outIds) { + // TODO use Binder +} + +sp<InputDeviceProxy> InputDeviceProxy::getDevice(int32_t id) { + // TODO use Binder + return NULL; +} + } // namespace android diff --git a/libs/ui/InputDevice.cpp b/libs/ui/InputDevice.cpp deleted file mode 100644 index b2a4d6c..0000000 --- a/libs/ui/InputDevice.cpp +++ /dev/null @@ -1,729 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// -// The input reader. -// -#define LOG_TAG "InputDevice" - -//#define LOG_NDEBUG 0 - -// Log debug messages for each raw event received from the EventHub. -#define DEBUG_RAW_EVENTS 0 - -// Log debug messages about touch screen filtering hacks. -#define DEBUG_HACKS 0 - -// Log debug messages about virtual key processing. -#define DEBUG_VIRTUAL_KEYS 0 - -// Log debug messages about pointers. -#define DEBUG_POINTERS 0 - -// Log debug messages about pointer assignment calculations. -#define DEBUG_POINTER_ASSIGNMENT 0 - -#include <cutils/log.h> -#include <ui/InputDevice.h> - -#include <stddef.h> -#include <unistd.h> -#include <errno.h> -#include <limits.h> - -/* Slop distance for jumpy pointer detection. - * The vertical range of the screen divided by this is our epsilon value. */ -#define JUMPY_EPSILON_DIVISOR 212 - -/* Number of jumpy points to drop for touchscreens that need it. */ -#define JUMPY_TRANSITION_DROPS 3 -#define JUMPY_DROP_LIMIT 3 - -/* Maximum squared distance for averaging. - * If moving farther than this, turn of averaging to avoid lag in response. */ -#define AVERAGING_DISTANCE_LIMIT (75 * 75) - - -namespace android { - -// --- Static Functions --- - -template<typename T> -inline static T abs(const T& value) { - return value < 0 ? - value : value; -} - -template<typename T> -inline static T min(const T& a, const T& b) { - return a < b ? a : b; -} - -template<typename T> -inline static void swap(T& a, T& b) { - T temp = a; - a = b; - b = temp; -} - - -// --- InputDevice --- - -InputDevice::InputDevice(int32_t id, uint32_t classes, String8 name) : - id(id), classes(classes), name(name), ignored(false) { -} - -void InputDevice::reset() { - if (isKeyboard()) { - keyboard.reset(); - } - - if (isTrackball()) { - trackball.reset(); - } - - if (isMultiTouchScreen()) { - multiTouchScreen.reset(); - } else if (isSingleTouchScreen()) { - singleTouchScreen.reset(); - } - - if (isTouchScreen()) { - touchScreen.reset(); - } -} - - -// --- InputDevice::TouchData --- - -void InputDevice::TouchData::copyFrom(const TouchData& other) { - pointerCount = other.pointerCount; - idBits = other.idBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointers[i] = other.pointers[i]; - idToIndex[i] = other.idToIndex[i]; - } -} - - -// --- InputDevice::KeyboardState --- - -void InputDevice::KeyboardState::reset() { - current.metaState = AMETA_NONE; - current.downTime = 0; -} - - -// --- InputDevice::TrackballState --- - -void InputDevice::TrackballState::reset() { - accumulator.clear(); - current.down = false; - current.downTime = 0; -} - - -// --- InputDevice::TouchScreenState --- - -void InputDevice::TouchScreenState::reset() { - lastTouch.clear(); - downTime = 0; - currentVirtualKey.status = CurrentVirtualKeyState::STATUS_UP; - - for (uint32_t i = 0; i < MAX_POINTERS; i++) { - averagingTouchFilter.historyStart[i] = 0; - averagingTouchFilter.historyEnd[i] = 0; - } - - jumpyTouchFilter.jumpyPointsDropped = 0; -} - -struct PointerDistanceHeapElement { - uint32_t currentPointerIndex : 8; - uint32_t lastPointerIndex : 8; - uint64_t distance : 48; // squared distance -}; - -void InputDevice::TouchScreenState::calculatePointerIds() { - uint32_t currentPointerCount = currentTouch.pointerCount; - uint32_t lastPointerCount = lastTouch.pointerCount; - - if (currentPointerCount == 0) { - // No pointers to assign. - currentTouch.idBits.clear(); - } else if (lastPointerCount == 0) { - // All pointers are new. - currentTouch.idBits.clear(); - for (uint32_t i = 0; i < currentPointerCount; i++) { - currentTouch.pointers[i].id = i; - currentTouch.idToIndex[i] = i; - currentTouch.idBits.markBit(i); - } - } else if (currentPointerCount == 1 && lastPointerCount == 1) { - // Only one pointer and no change in count so it must have the same id as before. - uint32_t id = lastTouch.pointers[0].id; - currentTouch.pointers[0].id = id; - currentTouch.idToIndex[id] = 0; - currentTouch.idBits.value = BitSet32::valueForBit(id); - } else { - // General case. - // We build a heap of squared euclidean distances between current and last pointers - // associated with the current and last pointer indices. Then, we find the best - // match (by distance) for each current pointer. - PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; - - uint32_t heapSize = 0; - for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; - currentPointerIndex++) { - for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; - lastPointerIndex++) { - int64_t deltaX = currentTouch.pointers[currentPointerIndex].x - - lastTouch.pointers[lastPointerIndex].x; - int64_t deltaY = currentTouch.pointers[currentPointerIndex].y - - lastTouch.pointers[lastPointerIndex].y; - - uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); - - // Insert new element into the heap (sift up). - heap[heapSize].currentPointerIndex = currentPointerIndex; - heap[heapSize].lastPointerIndex = lastPointerIndex; - heap[heapSize].distance = distance; - heapSize += 1; - } - } - - // Heapify - for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) { - startIndex -= 1; - for (uint32_t parentIndex = startIndex; ;) { - uint32_t childIndex = parentIndex * 2 + 1; - if (childIndex >= heapSize) { - break; - } - - if (childIndex + 1 < heapSize - && heap[childIndex + 1].distance < heap[childIndex].distance) { - childIndex += 1; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - } - -#if DEBUG_POINTER_ASSIGNMENT - LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", - i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, - heap[i].distance); - } -#endif - - // Pull matches out by increasing order of distance. - // To avoid reassigning pointers that have already been matched, the loop keeps track - // of which last and current pointers have been matched using the matchedXXXBits variables. - // It also tracks the used pointer id bits. - BitSet32 matchedLastBits(0); - BitSet32 matchedCurrentBits(0); - BitSet32 usedIdBits(0); - bool first = true; - for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) { - for (;;) { - if (first) { - // The first time through the loop, we just consume the root element of - // the heap (the one with smallest distance). - first = false; - } else { - // Previous iterations consumed the root element of the heap. - // Pop root element off of the heap (sift down). - heapSize -= 1; - assert(heapSize > 0); - - // Sift down. - heap[0] = heap[heapSize]; - for (uint32_t parentIndex = 0; ;) { - uint32_t childIndex = parentIndex * 2 + 1; - if (childIndex >= heapSize) { - break; - } - - if (childIndex + 1 < heapSize - && heap[childIndex + 1].distance < heap[childIndex].distance) { - childIndex += 1; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - -#if DEBUG_POINTER_ASSIGNMENT - LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", - i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, - heap[i].distance); - } -#endif - } - - uint32_t currentPointerIndex = heap[0].currentPointerIndex; - if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched - - uint32_t lastPointerIndex = heap[0].lastPointerIndex; - if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched - - matchedCurrentBits.markBit(currentPointerIndex); - matchedLastBits.markBit(lastPointerIndex); - - uint32_t id = lastTouch.pointers[lastPointerIndex].id; - currentTouch.pointers[currentPointerIndex].id = id; - currentTouch.idToIndex[id] = currentPointerIndex; - usedIdBits.markBit(id); - -#if DEBUG_POINTER_ASSIGNMENT - LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld", - lastPointerIndex, currentPointerIndex, id, heap[0].distance); -#endif - break; - } - } - - // Assign fresh ids to new pointers. - if (currentPointerCount > lastPointerCount) { - for (uint32_t i = currentPointerCount - lastPointerCount; ;) { - uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit(); - uint32_t id = usedIdBits.firstUnmarkedBit(); - - currentTouch.pointers[currentPointerIndex].id = id; - currentTouch.idToIndex[id] = currentPointerIndex; - usedIdBits.markBit(id); - -#if DEBUG_POINTER_ASSIGNMENT - LOGD("calculatePointerIds - assigned: cur=%d, id=%d", - currentPointerIndex, id); -#endif - - if (--i == 0) break; // done - matchedCurrentBits.markBit(currentPointerIndex); - } - } - - // Fix id bits. - currentTouch.idBits = usedIdBits; - } -} - -/* Special hack for devices that have bad screen data: if one of the - * points has moved more than a screen height from the last position, - * then drop it. */ -bool InputDevice::TouchScreenState::applyBadTouchFilter() { - // This hack requires valid axis parameters. - if (! parameters.yAxis.valid) { - return false; - } - - uint32_t pointerCount = currentTouch.pointerCount; - - // Nothing to do if there are no points. - if (pointerCount == 0) { - return false; - } - - // Don't do anything if a finger is going down or up. We run - // here before assigning pointer IDs, so there isn't a good - // way to do per-finger matching. - if (pointerCount != lastTouch.pointerCount) { - return false; - } - - // We consider a single movement across more than a 7/16 of - // the long size of the screen to be bad. This was a magic value - // determined by looking at the maximum distance it is feasible - // to actually move in one sample. - int32_t maxDeltaY = parameters.yAxis.range * 7 / 16; - - // XXX The original code in InputDevice.java included commented out - // code for testing the X axis. Note that when we drop a point - // we don't actually restore the old X either. Strange. - // The old code also tries to track when bad points were previously - // detected but it turns out that due to the placement of a "break" - // at the end of the loop, we never set mDroppedBadPoint to true - // so it is effectively dead code. - // Need to figure out if the old code is busted or just overcomplicated - // but working as intended. - - // Look through all new points and see if any are farther than - // acceptable from all previous points. - for (uint32_t i = pointerCount; i-- > 0; ) { - int32_t y = currentTouch.pointers[i].y; - int32_t closestY = INT_MAX; - int32_t closestDeltaY = 0; - -#if DEBUG_HACKS - LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y); -#endif - - for (uint32_t j = pointerCount; j-- > 0; ) { - int32_t lastY = lastTouch.pointers[j].y; - int32_t deltaY = abs(y - lastY); - -#if DEBUG_HACKS - LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d", - j, lastY, deltaY); -#endif - - if (deltaY < maxDeltaY) { - goto SkipSufficientlyClosePoint; - } - if (deltaY < closestDeltaY) { - closestDeltaY = deltaY; - closestY = lastY; - } - } - - // Must not have found a close enough match. -#if DEBUG_HACKS - LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d", - i, y, closestY, closestDeltaY, maxDeltaY); -#endif - - currentTouch.pointers[i].y = closestY; - return true; // XXX original code only corrects one point - - SkipSufficientlyClosePoint: ; - } - - // No change. - return false; -} - -/* Special hack for devices that have bad screen data: drop points where - * the coordinate value for one axis has jumped to the other pointer's location. - */ -bool InputDevice::TouchScreenState::applyJumpyTouchFilter() { - // This hack requires valid axis parameters. - if (! parameters.yAxis.valid) { - return false; - } - - uint32_t pointerCount = currentTouch.pointerCount; - if (lastTouch.pointerCount != pointerCount) { -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Different pointer count %d -> %d", - lastTouch.pointerCount, pointerCount); - for (uint32_t i = 0; i < pointerCount; i++) { - LOGD(" Pointer %d (%d, %d)", i, - currentTouch.pointers[i].x, currentTouch.pointers[i].y); - } -#endif - - if (jumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) { - if (lastTouch.pointerCount == 1 && pointerCount == 2) { - // Just drop the first few events going from 1 to 2 pointers. - // They're bad often enough that they're not worth considering. - currentTouch.pointerCount = 1; - jumpyTouchFilter.jumpyPointsDropped += 1; - -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Pointer 2 dropped"); -#endif - return true; - } else if (lastTouch.pointerCount == 2 && pointerCount == 1) { - // The event when we go from 2 -> 1 tends to be messed up too - currentTouch.pointerCount = 2; - currentTouch.pointers[0] = lastTouch.pointers[0]; - currentTouch.pointers[1] = lastTouch.pointers[1]; - jumpyTouchFilter.jumpyPointsDropped += 1; - -#if DEBUG_HACKS - for (int32_t i = 0; i < 2; i++) { - LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i, - currentTouch.pointers[i].x, currentTouch.pointers[i].y); - } -#endif - return true; - } - } - // Reset jumpy points dropped on other transitions or if limit exceeded. - jumpyTouchFilter.jumpyPointsDropped = 0; - -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Transition - drop limit reset"); -#endif - return false; - } - - // We have the same number of pointers as last time. - // A 'jumpy' point is one where the coordinate value for one axis - // has jumped to the other pointer's location. No need to do anything - // else if we only have one pointer. - if (pointerCount < 2) { - return false; - } - - if (jumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) { - int jumpyEpsilon = parameters.yAxis.range / JUMPY_EPSILON_DIVISOR; - - // We only replace the single worst jumpy point as characterized by pointer distance - // in a single axis. - int32_t badPointerIndex = -1; - int32_t badPointerReplacementIndex = -1; - int32_t badPointerDistance = INT_MIN; // distance to be corrected - - for (uint32_t i = pointerCount; i-- > 0; ) { - int32_t x = currentTouch.pointers[i].x; - int32_t y = currentTouch.pointers[i].y; - -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y); -#endif - - // Check if a touch point is too close to another's coordinates - bool dropX = false, dropY = false; - for (uint32_t j = 0; j < pointerCount; j++) { - if (i == j) { - continue; - } - - if (abs(x - currentTouch.pointers[j].x) <= jumpyEpsilon) { - dropX = true; - break; - } - - if (abs(y - currentTouch.pointers[j].y) <= jumpyEpsilon) { - dropY = true; - break; - } - } - if (! dropX && ! dropY) { - continue; // not jumpy - } - - // Find a replacement candidate by comparing with older points on the - // complementary (non-jumpy) axis. - int32_t distance = INT_MIN; // distance to be corrected - int32_t replacementIndex = -1; - - if (dropX) { - // X looks too close. Find an older replacement point with a close Y. - int32_t smallestDeltaY = INT_MAX; - for (uint32_t j = 0; j < pointerCount; j++) { - int32_t deltaY = abs(y - lastTouch.pointers[j].y); - if (deltaY < smallestDeltaY) { - smallestDeltaY = deltaY; - replacementIndex = j; - } - } - distance = abs(x - lastTouch.pointers[replacementIndex].x); - } else { - // Y looks too close. Find an older replacement point with a close X. - int32_t smallestDeltaX = INT_MAX; - for (uint32_t j = 0; j < pointerCount; j++) { - int32_t deltaX = abs(x - lastTouch.pointers[j].x); - if (deltaX < smallestDeltaX) { - smallestDeltaX = deltaX; - replacementIndex = j; - } - } - distance = abs(y - lastTouch.pointers[replacementIndex].y); - } - - // If replacing this pointer would correct a worse error than the previous ones - // considered, then use this replacement instead. - if (distance > badPointerDistance) { - badPointerIndex = i; - badPointerReplacementIndex = replacementIndex; - badPointerDistance = distance; - } - } - - // Correct the jumpy pointer if one was found. - if (badPointerIndex >= 0) { -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)", - badPointerIndex, - lastTouch.pointers[badPointerReplacementIndex].x, - lastTouch.pointers[badPointerReplacementIndex].y); -#endif - - currentTouch.pointers[badPointerIndex].x = - lastTouch.pointers[badPointerReplacementIndex].x; - currentTouch.pointers[badPointerIndex].y = - lastTouch.pointers[badPointerReplacementIndex].y; - jumpyTouchFilter.jumpyPointsDropped += 1; - return true; - } - } - - jumpyTouchFilter.jumpyPointsDropped = 0; - return false; -} - -/* Special hack for devices that have bad screen data: aggregate and - * compute averages of the coordinate data, to reduce the amount of - * jitter seen by applications. */ -void InputDevice::TouchScreenState::applyAveragingTouchFilter() { - for (uint32_t currentIndex = 0; currentIndex < currentTouch.pointerCount; currentIndex++) { - uint32_t id = currentTouch.pointers[currentIndex].id; - int32_t x = currentTouch.pointers[currentIndex].x; - int32_t y = currentTouch.pointers[currentIndex].y; - int32_t pressure = currentTouch.pointers[currentIndex].pressure; - - if (lastTouch.idBits.hasBit(id)) { - // Pointer was down before and is still down now. - // Compute average over history trace. - uint32_t start = averagingTouchFilter.historyStart[id]; - uint32_t end = averagingTouchFilter.historyEnd[id]; - - int64_t deltaX = x - averagingTouchFilter.historyData[end].pointers[id].x; - int64_t deltaY = y - averagingTouchFilter.historyData[end].pointers[id].y; - uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); - -#if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld", - id, distance); -#endif - - if (distance < AVERAGING_DISTANCE_LIMIT) { - // Increment end index in preparation for recording new historical data. - end += 1; - if (end > AVERAGING_HISTORY_SIZE) { - end = 0; - } - - // If the end index has looped back to the start index then we have filled - // the historical trace up to the desired size so we drop the historical - // data at the start of the trace. - if (end == start) { - start += 1; - if (start > AVERAGING_HISTORY_SIZE) { - start = 0; - } - } - - // Add the raw data to the historical trace. - averagingTouchFilter.historyStart[id] = start; - averagingTouchFilter.historyEnd[id] = end; - averagingTouchFilter.historyData[end].pointers[id].x = x; - averagingTouchFilter.historyData[end].pointers[id].y = y; - averagingTouchFilter.historyData[end].pointers[id].pressure = pressure; - - // Average over all historical positions in the trace by total pressure. - int32_t averagedX = 0; - int32_t averagedY = 0; - int32_t totalPressure = 0; - for (;;) { - int32_t historicalX = averagingTouchFilter.historyData[start].pointers[id].x; - int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].y; - int32_t historicalPressure = averagingTouchFilter.historyData[start] - .pointers[id].pressure; - - averagedX += historicalX * historicalPressure; - averagedY += historicalY * historicalPressure; - totalPressure += historicalPressure; - - if (start == end) { - break; - } - - start += 1; - if (start > AVERAGING_HISTORY_SIZE) { - start = 0; - } - } - - averagedX /= totalPressure; - averagedY /= totalPressure; - -#if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - " - "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure, - averagedX, averagedY); -#endif - - currentTouch.pointers[currentIndex].x = averagedX; - currentTouch.pointers[currentIndex].y = averagedY; - } else { -#if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id); -#endif - } - } else { -#if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id); -#endif - } - - // Reset pointer history. - averagingTouchFilter.historyStart[id] = 0; - averagingTouchFilter.historyEnd[id] = 0; - averagingTouchFilter.historyData[0].pointers[id].x = x; - averagingTouchFilter.historyData[0].pointers[id].y = y; - averagingTouchFilter.historyData[0].pointers[id].pressure = pressure; - } -} - -bool InputDevice::TouchScreenState::isPointInsideDisplay(int32_t x, int32_t y) const { - if (! parameters.xAxis.valid || ! parameters.yAxis.valid) { - // Assume all points on a touch screen without valid axis parameters are - // inside the display. - return true; - } - - return x >= parameters.xAxis.minValue - && x <= parameters.xAxis.maxValue - && y >= parameters.yAxis.minValue - && y <= parameters.yAxis.maxValue; -} - -const InputDevice::VirtualKey* InputDevice::TouchScreenState::findVirtualKeyHit() const { - int32_t x = currentTouch.pointers[0].x; - int32_t y = currentTouch.pointers[0].y; - for (size_t i = 0; i < virtualKeys.size(); i++) { - const InputDevice::VirtualKey& virtualKey = virtualKeys[i]; - -#if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " - "left=%d, top=%d, right=%d, bottom=%d", - x, y, - virtualKey.keyCode, virtualKey.scanCode, - virtualKey.hitLeft, virtualKey.hitTop, - virtualKey.hitRight, virtualKey.hitBottom); -#endif - - if (virtualKey.isHit(x, y)) { - return & virtualKey; - } - } - - return NULL; -} - - -// --- InputDevice::SingleTouchScreenState --- - -void InputDevice::SingleTouchScreenState::reset() { - accumulator.clear(); - current.down = false; - current.x = 0; - current.y = 0; - current.pressure = 0; - current.size = 0; -} - - -// --- InputDevice::MultiTouchScreenState --- - -void InputDevice::MultiTouchScreenState::reset() { - accumulator.clear(); -} - -} // namespace android diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp index e1d15a4..bf23479 100644 --- a/libs/ui/InputManager.cpp +++ b/libs/ui/InputManager.cpp @@ -89,26 +89,35 @@ void InputManager::preemptInputDispatch() { mDispatcher->preemptInputDispatch(); } -void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const { - mReader->getCurrentInputConfiguration(outConfiguration); +void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) { + mReader->getInputConfiguration(outConfiguration); } -int32_t InputManager::getScanCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t scanCode) const { - return mReader->getCurrentScanCodeState(deviceId, deviceClasses, scanCode); +status_t InputManager::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) { + return mReader->getInputDeviceInfo(deviceId, outDeviceInfo); } -int32_t InputManager::getKeyCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t keyCode) const { - return mReader->getCurrentKeyCodeState(deviceId, deviceClasses, keyCode); +void InputManager::getInputDeviceIds(Vector<int32_t>& outDeviceIds) { + mReader->getInputDeviceIds(outDeviceIds); } -int32_t InputManager::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const { - return mReader->getCurrentSwitchState(deviceId, deviceClasses, sw); +int32_t InputManager::getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode) { + return mReader->getScanCodeState(deviceId, sourceMask, scanCode); } -bool InputManager::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { - return mReader->hasKeys(numCodes, keyCodes, outFlags); +int32_t InputManager::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode) { + return mReader->getKeyCodeState(deviceId, sourceMask, keyCode); +} + +int32_t InputManager::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) { + return mReader->getSwitchState(deviceId, sourceMask, sw); +} + +bool InputManager::hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { + return mReader->hasKeys(deviceId, sourceMask, numCodes, keyCodes, outFlags); } } // namespace android diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index 30e391f..c5183e4 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -31,10 +31,6 @@ #include <limits.h> #include <math.h> -/** Amount that trackball needs to move in order to generate a key event. */ -#define TRACKBALL_MOVEMENT_THRESHOLD 6 - - namespace android { // --- Static Functions --- @@ -115,17 +111,21 @@ int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { return keyCode; } +static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { + return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0; +} + // --- InputReader --- InputReader::InputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputDispatcherInterface>& dispatcher) : - mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher) { + mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher), + mGlobalMetaState(0) { configureExcludedDevices(); - resetGlobalMetaState(); - resetDisplayProperties(); - updateExportedVirtualKeyState(); + updateGlobalMetaState(); + updateInputConfiguration(); } InputReader::~InputReader() { @@ -136,12 +136,7 @@ InputReader::~InputReader() { void InputReader::loopOnce() { RawEvent rawEvent; - mEventHub->getEvent(& rawEvent.deviceId, & rawEvent.type, & rawEvent.scanCode, - & rawEvent.keyCode, & rawEvent.flags, & rawEvent.value, & rawEvent.when); - - // Replace the event timestamp so it is in same timebase as java.lang.System.nanoTime() - // and android.os.SystemClock.uptimeMillis() as expected by the rest of the system. - rawEvent.when = systemTime(SYSTEM_TIME_MONOTONIC); + mEventHub->getEvent(& rawEvent); #if DEBUG_RAW_EVENTS LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d", @@ -155,621 +150,1371 @@ void InputReader::loopOnce() { void InputReader::process(const RawEvent* rawEvent) { switch (rawEvent->type) { case EventHubInterface::DEVICE_ADDED: - handleDeviceAdded(rawEvent); + addDevice(rawEvent->when, rawEvent->deviceId); break; case EventHubInterface::DEVICE_REMOVED: - handleDeviceRemoved(rawEvent); + removeDevice(rawEvent->when, rawEvent->deviceId); break; - case EV_SYN: - handleSync(rawEvent); + default: + consumeEvent(rawEvent); break; + } +} - case EV_KEY: - handleKey(rawEvent); - break; +void InputReader::addDevice(nsecs_t when, int32_t deviceId) { + String8 name = mEventHub->getDeviceName(deviceId); + uint32_t classes = mEventHub->getDeviceClasses(deviceId); - case EV_REL: - handleRelativeMotion(rawEvent); - break; + InputDevice* device = createDevice(deviceId, name, classes); + device->configure(); - case EV_ABS: - handleAbsoluteMotion(rawEvent); - break; + bool added = false; + { // acquire device registry writer lock + RWLock::AutoWLock _wl(mDeviceRegistryLock); - case EV_SW: - handleSwitch(rawEvent); - break; + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + mDevices.add(deviceId, device); + added = true; + } + } // release device registry writer lock + + if (! added) { + LOGW("Ignoring spurious device added event for deviceId %d.", deviceId); + delete device; + return; } + + if (device->isIgnored()) { + LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", + deviceId, name.string()); + } else { + LOGI("Device added: id=0x%x, name=%s, sources=%08x", + deviceId, name.string(), device->getSources()); + } + + handleConfigurationChanged(when); } -void InputReader::handleDeviceAdded(const RawEvent* rawEvent) { - InputDevice* device = getDevice(rawEvent->deviceId); - if (device) { - LOGW("Ignoring spurious device added event for deviceId %d.", rawEvent->deviceId); +void InputReader::removeDevice(nsecs_t when, int32_t deviceId) { + bool removed = false; + InputDevice* device = NULL; + { // acquire device registry writer lock + RWLock::AutoWLock _wl(mDeviceRegistryLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + device = mDevices.valueAt(deviceIndex); + mDevices.removeItemsAt(deviceIndex, 1); + removed = true; + } + } // release device registry writer lock + + if (! removed) { + LOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); return; } - addDevice(rawEvent->when, rawEvent->deviceId); + device->reset(); + + if (device->isIgnored()) { + LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)", + device->getId(), device->getName().string()); + } else { + LOGI("Device removed: id=0x%x, name=%s, sources=%08x", + device->getId(), device->getName().string(), device->getSources()); + } + + delete device; + + handleConfigurationChanged(when); } -void InputReader::handleDeviceRemoved(const RawEvent* rawEvent) { - InputDevice* device = getDevice(rawEvent->deviceId); - if (! device) { - LOGW("Ignoring spurious device removed event for deviceId %d.", rawEvent->deviceId); - return; +InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) { + InputDevice* device = new InputDevice(this, deviceId, name); + + const int32_t associatedDisplayId = 0; // FIXME: hardcoded for current single-display devices + + // Switch-like devices. + if (classes & INPUT_DEVICE_CLASS_SWITCH) { + device->addMapper(new SwitchInputMapper(device)); } - removeDevice(rawEvent->when, device); + // Keyboard-like devices. + uint32_t keyboardSources = 0; + int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; + if (classes & INPUT_DEVICE_CLASS_KEYBOARD) { + keyboardSources |= AINPUT_SOURCE_KEYBOARD; + } + if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { + keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; + } + if (classes & INPUT_DEVICE_CLASS_DPAD) { + keyboardSources |= AINPUT_SOURCE_DPAD; + } + if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { + keyboardSources |= AINPUT_SOURCE_GAMEPAD; + } + + if (keyboardSources != 0) { + device->addMapper(new KeyboardInputMapper(device, + associatedDisplayId, keyboardSources, keyboardType)); + } + + // Trackball-like devices. + if (classes & INPUT_DEVICE_CLASS_TRACKBALL) { + device->addMapper(new TrackballInputMapper(device, associatedDisplayId)); + } + + // Touchscreen-like devices. + if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT) { + device->addMapper(new MultiTouchInputMapper(device, associatedDisplayId)); + } else if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN) { + device->addMapper(new SingleTouchInputMapper(device, associatedDisplayId)); + } + + return device; } -void InputReader::handleSync(const RawEvent* rawEvent) { - InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId); - if (! device) return; +void InputReader::consumeEvent(const RawEvent* rawEvent) { + int32_t deviceId = rawEvent->deviceId; - if (rawEvent->scanCode == SYN_MT_REPORT) { - // MultiTouch Sync: The driver has returned all data for *one* of the pointers. - // We drop pointers with pressure <= 0 since that indicates they are not down. - if (device->isMultiTouchScreen()) { - uint32_t pointerIndex = device->multiTouchScreen.accumulator.pointerCount; + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); - if (device->multiTouchScreen.accumulator.pointers[pointerIndex].fields) { - if (pointerIndex == MAX_POINTERS) { - LOGW("MultiTouch device driver returned more than maximum of %d pointers.", - MAX_POINTERS); - } else { - pointerIndex += 1; - device->multiTouchScreen.accumulator.pointerCount = pointerIndex; + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + LOGW("Discarding event for unknown deviceId %d.", deviceId); + return; + } + + InputDevice* device = mDevices.valueAt(deviceIndex); + if (device->isIgnored()) { + //LOGD("Discarding event for ignored deviceId %d.", deviceId); + return; + } + + device->process(rawEvent); + } // release device registry reader lock +} + +void InputReader::handleConfigurationChanged(nsecs_t when) { + // Reset global meta state because it depends on the list of all configured devices. + updateGlobalMetaState(); + + // Update input configuration. + updateInputConfiguration(); + + // Enqueue configuration changed. + mDispatcher->notifyConfigurationChanged(when); +} + +void InputReader::configureExcludedDevices() { + Vector<String8> excludedDeviceNames; + mPolicy->getExcludedDeviceNames(excludedDeviceNames); + + for (size_t i = 0; i < excludedDeviceNames.size(); i++) { + mEventHub->addExcludedDevice(excludedDeviceNames[i]); + } +} + +void InputReader::updateGlobalMetaState() { + { // acquire state lock + AutoMutex _l(mStateLock); + + mGlobalMetaState = 0; + + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + mGlobalMetaState |= device->getMetaState(); + } + } // release device registry reader lock + } // release state lock +} + +int32_t InputReader::getGlobalMetaState() { + { // acquire state lock + AutoMutex _l(mStateLock); + + return mGlobalMetaState; + } // release state lock +} + +void InputReader::updateInputConfiguration() { + { // acquire state lock + AutoMutex _l(mStateLock); + + int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH; + int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS; + int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV; + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + InputDeviceInfo deviceInfo; + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + device->getDeviceInfo(& deviceInfo); + uint32_t sources = deviceInfo.getSources(); + + if ((sources & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) { + touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER; + } + if ((sources & AINPUT_SOURCE_TRACKBALL) == AINPUT_SOURCE_TRACKBALL) { + navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL; + } else if ((sources & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD) { + navigationConfig = InputConfiguration::NAVIGATION_DPAD; + } + if (deviceInfo.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) { + keyboardConfig = InputConfiguration::KEYBOARD_QWERTY; } } + } // release device registry reader lock + + mInputConfiguration.touchScreen = touchScreenConfig; + mInputConfiguration.keyboard = keyboardConfig; + mInputConfiguration.navigation = navigationConfig; + } // release state lock +} + +void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) { + { // acquire state lock + AutoMutex _l(mStateLock); + + *outConfiguration = mInputConfiguration; + } // release state lock +} + +status_t InputReader::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) { + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); - device->multiTouchScreen.accumulator.pointers[pointerIndex].clear(); + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + return NAME_NOT_FOUND; } - } else if (rawEvent->scanCode == SYN_REPORT) { - // General Sync: The driver has returned all data for the current event update. - if (device->isMultiTouchScreen()) { - if (device->multiTouchScreen.accumulator.isDirty()) { - onMultiTouchScreenStateChanged(rawEvent->when, device); - device->multiTouchScreen.accumulator.clear(); + + InputDevice* device = mDevices.valueAt(deviceIndex); + if (device->isIgnored()) { + return NAME_NOT_FOUND; + } + + device->getDeviceInfo(outDeviceInfo); + return OK; + } // release device registy reader lock +} + +void InputReader::getInputDeviceIds(Vector<int32_t>& outDeviceIds) { + outDeviceIds.clear(); + + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (! device->isIgnored()) { + outDeviceIds.add(device->getId()); } - } else if (device->isSingleTouchScreen()) { - if (device->singleTouchScreen.accumulator.isDirty()) { - onSingleTouchScreenStateChanged(rawEvent->when, device); - device->singleTouchScreen.accumulator.clear(); + } + } // release device registy reader lock +} + +int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode) { + return getState(deviceId, sourceMask, keyCode, & InputDevice::getKeyCodeState); +} + +int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode) { + return getState(deviceId, sourceMask, scanCode, & InputDevice::getScanCodeState); +} + +int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) { + return getState(deviceId, sourceMask, switchCode, & InputDevice::getSwitchState); +} + +int32_t InputReader::getState(int32_t deviceId, uint32_t sourceMask, int32_t code, + GetStateFunc getStateFunc) { + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + int32_t result = AKEY_STATE_UNKNOWN; + if (deviceId >= 0) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = (device->*getStateFunc)(sourceMask, code); + } + } + } else { + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = (device->*getStateFunc)(sourceMask, code); + if (result >= AKEY_STATE_DOWN) { + return result; + } + } } } + return result; + } // release device registy reader lock +} + +bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { + memset(outFlags, 0, numCodes); + return markSupportedKeyCodes(deviceId, sourceMask, numCodes, keyCodes, outFlags); +} - if (device->trackball.accumulator.isDirty()) { - onTrackballStateChanged(rawEvent->when, device); - device->trackball.accumulator.clear(); +bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + bool result = false; + if (deviceId >= 0) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = device->markSupportedKeyCodes(sourceMask, + numCodes, keyCodes, outFlags); + } + } + } else { + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result |= device->markSupportedKeyCodes(sourceMask, + numCodes, keyCodes, outFlags); + } + } } - } + return result; + } // release device registy reader lock } -void InputReader::handleKey(const RawEvent* rawEvent) { - InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId); - if (! device) return; - bool down = rawEvent->value != 0; - int32_t scanCode = rawEvent->scanCode; +// --- InputReaderThread --- - if (device->isSingleTouchScreen()) { - switch (rawEvent->scanCode) { - case BTN_TOUCH: - device->singleTouchScreen.accumulator.fields |= - InputDevice::SingleTouchScreenState::Accumulator::FIELD_BTN_TOUCH; - device->singleTouchScreen.accumulator.btnTouch = down; - return; - } +InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) : + Thread(/*canCallJava*/ true), mReader(reader) { +} + +InputReaderThread::~InputReaderThread() { +} + +bool InputReaderThread::threadLoop() { + mReader->loopOnce(); + return true; +} + + +// --- InputDevice --- + +InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) : + mContext(context), mId(id), mName(name), mSources(0) { +} + +InputDevice::~InputDevice() { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + delete mMappers[i]; } + mMappers.clear(); +} - if (device->isTrackball()) { - switch (rawEvent->scanCode) { - case BTN_MOUSE: - device->trackball.accumulator.fields |= - InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE; - device->trackball.accumulator.btnMouse = down; +void InputDevice::addMapper(InputMapper* mapper) { + mMappers.add(mapper); +} - // Process the trackball change now since we may not receive a sync immediately. - onTrackballStateChanged(rawEvent->when, device); - device->trackball.accumulator.clear(); - return; - } +void InputDevice::configure() { + mSources = 0; + + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->configure(); + mSources |= mapper->getSources(); + } +} + +void InputDevice::reset() { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->reset(); } +} - if (device->isKeyboard()) { - int32_t keyCode = rawEvent->keyCode; - onKey(rawEvent->when, device, down, keyCode, scanCode, rawEvent->flags); +void InputDevice::process(const RawEvent* rawEvent) { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->process(rawEvent); } } -void InputReader::handleRelativeMotion(const RawEvent* rawEvent) { - InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId); - if (! device) return; +void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { + outDeviceInfo->initialize(mId, mName); - if (device->isTrackball()) { - switch (rawEvent->scanCode) { - case REL_X: - device->trackball.accumulator.fields |= - InputDevice::TrackballState::Accumulator::FIELD_REL_X; - device->trackball.accumulator.relX = rawEvent->value; - break; - case REL_Y: - device->trackball.accumulator.fields |= - InputDevice::TrackballState::Accumulator::FIELD_REL_Y; - device->trackball.accumulator.relY = rawEvent->value; - break; - } + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->populateDeviceInfo(outDeviceInfo); } } -void InputReader::handleAbsoluteMotion(const RawEvent* rawEvent) { - InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId); - if (! device) return; +int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState); +} - if (device->isMultiTouchScreen()) { - uint32_t pointerIndex = device->multiTouchScreen.accumulator.pointerCount; - InputDevice::MultiTouchScreenState::Accumulator::Pointer* pointer = - & device->multiTouchScreen.accumulator.pointers[pointerIndex]; +int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getState(sourceMask, scanCode, & InputMapper::getScanCodeState); +} - switch (rawEvent->scanCode) { - case ABS_MT_POSITION_X: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_X; - pointer->absMTPositionX = rawEvent->value; - break; - case ABS_MT_POSITION_Y: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_Y; - pointer->absMTPositionY = rawEvent->value; - break; - case ABS_MT_TOUCH_MAJOR: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MAJOR; - pointer->absMTTouchMajor = rawEvent->value; - break; - case ABS_MT_TOUCH_MINOR: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MINOR; - pointer->absMTTouchMinor = rawEvent->value; - break; - case ABS_MT_WIDTH_MAJOR: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; - pointer->absMTWidthMajor = rawEvent->value; - break; - case ABS_MT_WIDTH_MINOR: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MINOR; - pointer->absMTWidthMinor = rawEvent->value; - break; - case ABS_MT_ORIENTATION: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_ORIENTATION; - pointer->absMTOrientation = rawEvent->value; - break; - case ABS_MT_TRACKING_ID: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TRACKING_ID; - pointer->absMTTrackingId = rawEvent->value; - break; +int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getState(sourceMask, switchCode, & InputMapper::getSwitchState); +} + +int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { + int32_t result = AKEY_STATE_UNKNOWN; + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + result = (mapper->*getStateFunc)(sourceMask, code); + if (result >= AKEY_STATE_DOWN) { + return result; + } } - } else if (device->isSingleTouchScreen()) { - switch (rawEvent->scanCode) { - case ABS_X: - device->singleTouchScreen.accumulator.fields |= - InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X; - device->singleTouchScreen.accumulator.absX = rawEvent->value; - break; - case ABS_Y: - device->singleTouchScreen.accumulator.fields |= - InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y; - device->singleTouchScreen.accumulator.absY = rawEvent->value; - break; - case ABS_PRESSURE: - device->singleTouchScreen.accumulator.fields |= - InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE; - device->singleTouchScreen.accumulator.absPressure = rawEvent->value; - break; - case ABS_TOOL_WIDTH: - device->singleTouchScreen.accumulator.fields |= - InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH; - device->singleTouchScreen.accumulator.absToolWidth = rawEvent->value; - break; + } + return result; +} + +bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + bool result = false; + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); } } + return result; +} + +int32_t InputDevice::getMetaState() { + int32_t result = 0; + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + result |= mapper->getMetaState(); + } + return result; +} + + +// --- InputMapper --- + +InputMapper::InputMapper(InputDevice* device) : + mDevice(device), mContext(device->getContext()) { +} + +InputMapper::~InputMapper() { +} + +void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { + info->addSource(getSources()); +} + +void InputMapper::configure() { +} + +void InputMapper::reset() { +} + +int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return AKEY_STATE_UNKNOWN; +} + +bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return false; +} + +int32_t InputMapper::getMetaState() { + return 0; +} + +bool InputMapper::applyStandardPolicyActions(nsecs_t when, int32_t policyActions) { + if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) { + getDispatcher()->notifyAppSwitchComing(when); + } + + return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH; } -void InputReader::handleSwitch(const RawEvent* rawEvent) { - InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId); - if (! device) return; - onSwitch(rawEvent->when, device, rawEvent->scanCode, rawEvent->value); +// --- SwitchInputMapper --- + +SwitchInputMapper::SwitchInputMapper(InputDevice* device) : + InputMapper(device) { } -void InputReader::onKey(nsecs_t when, InputDevice* device, - bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) { - /* Refresh display properties so we can rotate key codes according to display orientation */ +SwitchInputMapper::~SwitchInputMapper() { +} - if (! refreshDisplayProperties()) { - return; +uint32_t SwitchInputMapper::getSources() { + return 0; +} + +void SwitchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_SW: + processSwitch(rawEvent->when, rawEvent->scanCode, rawEvent->value); + break; } +} - /* Update device state */ +void SwitchInputMapper::processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) { + uint32_t policyFlags = 0; + int32_t policyActions = getPolicy()->interceptSwitch( + when, switchCode, switchValue, policyFlags); - int32_t oldMetaState = device->keyboard.current.metaState; - int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState); - if (oldMetaState != newMetaState) { - device->keyboard.current.metaState = newMetaState; - resetGlobalMetaState(); + applyStandardPolicyActions(when, policyActions); +} + +int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getEventHub()->getSwitchState(getDeviceId(), switchCode); +} + + +// --- KeyboardInputMapper --- + +KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, + uint32_t sources, int32_t keyboardType) : + InputMapper(device), mAssociatedDisplayId(associatedDisplayId), mSources(sources), + mKeyboardType(keyboardType) { + initialize(); +} + +KeyboardInputMapper::~KeyboardInputMapper() { +} + +void KeyboardInputMapper::initialize() { + mMetaState = AMETA_NONE; + mDownTime = 0; +} + +uint32_t KeyboardInputMapper::getSources() { + return mSources; +} + +void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->setKeyboardType(mKeyboardType); +} + +void KeyboardInputMapper::reset() { + // Synthesize key up event on reset if keys are currently down. + while (! mKeyDowns.isEmpty()) { + const KeyDown& keyDown = mKeyDowns.top(); + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + processKey(when, false, keyDown.keyCode, keyDown.scanCode, 0); + } + + InputMapper::reset(); + + // Reinitialize. + initialize(); + getContext()->updateGlobalMetaState(); +} + +void KeyboardInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: { + int32_t scanCode = rawEvent->scanCode; + if (isKeyboardOrGamepadKey(scanCode)) { + processKey(rawEvent->when, rawEvent->value != 0, rawEvent->keyCode, scanCode, + rawEvent->flags); + } + break; + } } +} - // FIXME if we send a down event about a rotated key press we should ensure that we send - // a corresponding up event about the rotated key press even if the orientation - // has changed in the meantime - keyCode = rotateKeyCode(keyCode, mDisplayOrientation); +bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { + return scanCode < BTN_MOUSE + || scanCode >= KEY_OK + || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI); +} +void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode, + uint32_t policyFlags) { if (down) { - device->keyboard.current.downTime = when; + // Rotate key codes according to orientation. + if (mAssociatedDisplayId >= 0) { + int32_t orientation; + if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { + return; + } + + keyCode = rotateKeyCode(keyCode, orientation); + } + + // Add key down. + ssize_t keyDownIndex = findKeyDown(scanCode); + if (keyDownIndex >= 0) { + // key repeat, be sure to use same keycode as before in case of rotation + keyCode = mKeyDowns.top().keyCode; + } else { + // key down + mKeyDowns.push(); + KeyDown& keyDown = mKeyDowns.editTop(); + keyDown.keyCode = keyCode; + keyDown.scanCode = scanCode; + } + } else { + // Remove key down. + ssize_t keyDownIndex = findKeyDown(scanCode); + if (keyDownIndex >= 0) { + // key up, be sure to use same keycode as before in case of rotation + keyCode = mKeyDowns.top().keyCode; + mKeyDowns.removeAt(size_t(keyDownIndex)); + } else { + // key was not actually down + LOGI("Dropping key up from device %s because the key was not down. " + "keyCode=%d, scanCode=%d", + getDeviceName().string(), keyCode, scanCode); + return; + } } - /* Apply policy */ + int32_t oldMetaState = mMetaState; + int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState); + if (oldMetaState != newMetaState) { + mMetaState = newMetaState; + getContext()->updateGlobalMetaState(); + } - int32_t policyActions = mPolicy->interceptKey(when, device->id, - down, keyCode, scanCode, policyFlags); + /* Apply policy. */ - if (! applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) { + int32_t policyActions = getPolicy()->interceptKey(when, + getDeviceId(), down, keyCode, scanCode, policyFlags); + + if (! applyStandardPolicyActions(when, policyActions)) { return; // event dropped } - /* Enqueue key event for dispatch */ + /* Enqueue key event for dispatch. */ int32_t keyEventAction; if (down) { - device->keyboard.current.downTime = when; + mDownTime = when; keyEventAction = AKEY_EVENT_ACTION_DOWN; } else { keyEventAction = AKEY_EVENT_ACTION_UP; } int32_t keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM; - if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) { + if (policyFlags & POLICY_FLAG_WOKE_HERE) { keyEventFlags = keyEventFlags | AKEY_EVENT_FLAG_WOKE_HERE; } - mDispatcher->notifyKey(when, device->id, AINPUT_SOURCE_KEYBOARD, policyFlags, + getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, - device->keyboard.current.metaState, - device->keyboard.current.downTime); + mMetaState, mDownTime); } -void InputReader::onSwitch(nsecs_t when, InputDevice* device, int32_t switchCode, - int32_t switchValue) { - int32_t policyActions = mPolicy->interceptSwitch(when, switchCode, switchValue); +ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { + size_t n = mKeyDowns.size(); + for (size_t i = 0; i < n; i++) { + if (mKeyDowns[i].scanCode == scanCode) { + return i; + } + } + return -1; +} - uint32_t policyFlags = 0; - applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags); +int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); } -void InputReader::onMultiTouchScreenStateChanged(nsecs_t when, - InputDevice* device) { - static const uint32_t REQUIRED_FIELDS = - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_X - | InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_Y - | InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MAJOR - | InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; +int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); +} - /* Refresh display properties so we can map touch screen coords into display coords */ +bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); +} - if (! refreshDisplayProperties()) { - return; +int32_t KeyboardInputMapper::getMetaState() { + return mMetaState; +} + + +// --- TrackballInputMapper --- + +TrackballInputMapper::TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId) : + InputMapper(device), mAssociatedDisplayId(associatedDisplayId) { + mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + + initialize(); +} + +TrackballInputMapper::~TrackballInputMapper() { +} + +uint32_t TrackballInputMapper::getSources() { + return AINPUT_SOURCE_TRACKBALL; +} + +void TrackballInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->addMotionRange(AINPUT_MOTION_RANGE_X, -1.0f, 1.0f, 0.0f, mXScale); + info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale); +} + +void TrackballInputMapper::initialize() { + mAccumulator.clear(); + + mDown = false; + mDownTime = 0; +} + +void TrackballInputMapper::reset() { + // Synthesize trackball button up event on reset if trackball button is currently down. + if (mDown) { + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE; + mAccumulator.btnMouse = false; + sync(when); } - /* Update device state */ + InputMapper::reset(); - InputDevice::MultiTouchScreenState* in = & device->multiTouchScreen; - InputDevice::TouchData* out = & device->touchScreen.currentTouch; + // Reinitialize. + initialize(); +} - uint32_t inCount = in->accumulator.pointerCount; - uint32_t outCount = 0; - bool havePointerIds = true; +void TrackballInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: + switch (rawEvent->scanCode) { + case BTN_MOUSE: + mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE; + mAccumulator.btnMouse = rawEvent->value != 0; - out->clear(); + sync(rawEvent->when); + mAccumulator.clear(); + break; + } + break; - for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) { - uint32_t fields = in->accumulator.pointers[inIndex].fields; + case EV_REL: + switch (rawEvent->scanCode) { + case REL_X: + mAccumulator.fields |= Accumulator::FIELD_REL_X; + mAccumulator.relX = rawEvent->value; + break; + case REL_Y: + mAccumulator.fields |= Accumulator::FIELD_REL_Y; + mAccumulator.relY = rawEvent->value; + break; + } + break; - if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) { -#if DEBUG_POINTERS - LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d", - inIndex, fields); - continue; -#endif + case EV_SYN: + switch (rawEvent->scanCode) { + case SYN_REPORT: + if (mAccumulator.isDirty()) { + sync(rawEvent->when); + mAccumulator.clear(); + } + break; } + break; + } +} - if (in->accumulator.pointers[inIndex].absMTTouchMajor <= 0) { - // Pointer is not down. Drop it. - continue; +void TrackballInputMapper::sync(nsecs_t when) { + /* Get display properties so for rotation based on display orientation. */ + + int32_t orientation; + if (mAssociatedDisplayId >= 0) { + if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { + return; } + } else { + orientation = InputReaderPolicyInterface::ROTATION_0; + } - out->pointers[outCount].x = in->accumulator.pointers[inIndex].absMTPositionX; - out->pointers[outCount].y = in->accumulator.pointers[inIndex].absMTPositionY; + /* Update saved trackball state */ - out->pointers[outCount].touchMajor = in->accumulator.pointers[inIndex].absMTTouchMajor; - out->pointers[outCount].touchMinor = (fields - & InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0 - ? in->accumulator.pointers[inIndex].absMTTouchMinor - : in->accumulator.pointers[inIndex].absMTTouchMajor; + uint32_t fields = mAccumulator.fields; + bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE; - out->pointers[outCount].toolMajor = in->accumulator.pointers[inIndex].absMTWidthMajor; - out->pointers[outCount].toolMinor = (fields - & InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0 - ? in->accumulator.pointers[inIndex].absMTWidthMinor - : in->accumulator.pointers[inIndex].absMTWidthMajor; + if (downChanged) { + if (mAccumulator.btnMouse) { + mDown = true; + mDownTime = when; + } else { + mDown = false; + } + } - out->pointers[outCount].orientation = (fields - & InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_ORIENTATION) != 0 - ? in->accumulator.pointers[inIndex].absMTOrientation : 0; + /* Apply policy */ - // Derive an approximation of pressure and size. - // FIXME assignment of pressure may be incorrect, probably better to let - // pressure = touch / width. Later on we pass width to MotionEvent as a size, which - // isn't quite right either. Should be using touch for that. - out->pointers[outCount].pressure = in->accumulator.pointers[inIndex].absMTTouchMajor; - out->pointers[outCount].size = in->accumulator.pointers[inIndex].absMTWidthMajor; + uint32_t policyFlags = 0; + int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags); - if (havePointerIds) { - if (fields & InputDevice::MultiTouchScreenState::Accumulator:: - FIELD_ABS_MT_TRACKING_ID) { - uint32_t id = uint32_t(in->accumulator.pointers[inIndex].absMTTrackingId); + if (! applyStandardPolicyActions(when, policyActions)) { + return; // event dropped + } - if (id > MAX_POINTER_ID) { -#if DEBUG_POINTERS - LOGD("Pointers: Ignoring driver provided pointer id %d because " - "it is larger than max supported id %d for optimizations", - id, MAX_POINTER_ID); -#endif - havePointerIds = false; - } - else { - out->pointers[outCount].id = id; - out->idToIndex[id] = outCount; - out->idBits.markBit(id); - } - } else { - havePointerIds = false; - } - } + /* Enqueue motion event for dispatch. */ - outCount += 1; + int32_t motionEventAction; + if (downChanged) { + motionEventAction = mDown ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; + } else { + motionEventAction = AMOTION_EVENT_ACTION_MOVE; + } + + int32_t pointerId = 0; + PointerCoords pointerCoords; + pointerCoords.x = fields & Accumulator::FIELD_REL_X + ? mAccumulator.relX * mXScale : 0; + pointerCoords.y = fields & Accumulator::FIELD_REL_Y + ? mAccumulator.relY * mYScale : 0; + pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise. + pointerCoords.size = 0; + pointerCoords.touchMajor = 0; + pointerCoords.touchMinor = 0; + pointerCoords.toolMajor = 0; + pointerCoords.toolMinor = 0; + pointerCoords.orientation = 0; + + float temp; + switch (orientation) { + case InputReaderPolicyInterface::ROTATION_90: + temp = pointerCoords.x; + pointerCoords.x = pointerCoords.y; + pointerCoords.y = - temp; + break; + + case InputReaderPolicyInterface::ROTATION_180: + pointerCoords.x = - pointerCoords.x; + pointerCoords.y = - pointerCoords.y; + break; + + case InputReaderPolicyInterface::ROTATION_270: + temp = pointerCoords.x; + pointerCoords.x = - pointerCoords.y; + pointerCoords.y = temp; + break; } - out->pointerCount = outCount; + int32_t metaState = mContext->getGlobalMetaState(); + getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, policyFlags, + motionEventAction, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + 1, & pointerId, & pointerCoords, mXPrecision, mYPrecision, mDownTime); +} + - onTouchScreenChanged(when, device, havePointerIds); +// --- TouchInputMapper --- + +TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : + InputMapper(device), mAssociatedDisplayId(associatedDisplayId), + mSurfaceOrientation(-1), mSurfaceWidth(-1), mSurfaceHeight(-1) { + initialize(); } -void InputReader::onSingleTouchScreenStateChanged(nsecs_t when, - InputDevice* device) { - /* Refresh display properties so we can map touch screen coords into display coords */ +TouchInputMapper::~TouchInputMapper() { +} - if (! refreshDisplayProperties()) { - return; +uint32_t TouchInputMapper::getSources() { + return mAssociatedDisplayId >= 0 ? AINPUT_SOURCE_TOUCHSCREEN : AINPUT_SOURCE_TOUCHPAD; +} + +void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + // FIXME: Should ensure the surface information is up to date so that orientation changes + // are noticed immediately. Unfortunately we will need to add some extra locks here + // to prevent race conditions. + // configureSurface(); + + info->addMotionRange(AINPUT_MOTION_RANGE_X, mOrientedRanges.x); + info->addMotionRange(AINPUT_MOTION_RANGE_Y, mOrientedRanges.y); + info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, mOrientedRanges.pressure); + info->addMotionRange(AINPUT_MOTION_RANGE_SIZE, mOrientedRanges.size); + info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, mOrientedRanges.touchMajor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, mOrientedRanges.touchMinor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR, mOrientedRanges.toolMajor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR, mOrientedRanges.toolMinor); + info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION, mOrientedRanges.orientation); +} + +void TouchInputMapper::initialize() { + mLastTouch.clear(); + mDownTime = 0; + mCurrentVirtualKey.down = false; + + for (uint32_t i = 0; i < MAX_POINTERS; i++) { + mAveragingTouchFilter.historyStart[i] = 0; + mAveragingTouchFilter.historyEnd[i] = 0; } - /* Update device state */ + mJumpyTouchFilter.jumpyPointsDropped = 0; +} - InputDevice::SingleTouchScreenState* in = & device->singleTouchScreen; - InputDevice::TouchData* out = & device->touchScreen.currentTouch; +void TouchInputMapper::configure() { + InputMapper::configure(); - uint32_t fields = in->accumulator.fields; + // Configure basic parameters. + mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents(); + mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents(); + mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents(); - if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_BTN_TOUCH) { - in->current.down = in->accumulator.btnTouch; + // Configure absolute axis information. + configureAxes(); + + // Configure pressure factors. + if (mAxes.pressure.valid) { + mPressureOrigin = mAxes.pressure.minValue; + mPressureScale = 1.0f / mAxes.pressure.getRange(); + } else { + mPressureOrigin = 0; + mPressureScale = 1.0f; } - if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X) { - in->current.x = in->accumulator.absX; + mOrientedRanges.pressure.min = 0.0f; + mOrientedRanges.pressure.max = 1.0f; + mOrientedRanges.pressure.flat = 0.0f; + mOrientedRanges.pressure.fuzz = mPressureScale; + + // Configure size factors. + if (mAxes.size.valid) { + mSizeOrigin = mAxes.size.minValue; + mSizeScale = 1.0f / mAxes.size.getRange(); + } else { + mSizeOrigin = 0; + mSizeScale = 1.0f; } - if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y) { - in->current.y = in->accumulator.absY; + mOrientedRanges.size.min = 0.0f; + mOrientedRanges.size.max = 1.0f; + mOrientedRanges.size.flat = 0.0f; + mOrientedRanges.size.fuzz = mSizeScale; + + // Configure orientation factors. + if (mAxes.orientation.valid && mAxes.orientation.maxValue > 0) { + mOrientationScale = float(M_PI_2) / mAxes.orientation.maxValue; + } else { + mOrientationScale = 0.0f; } - if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE) { - in->current.pressure = in->accumulator.absPressure; + mOrientedRanges.orientation.min = - M_PI_2; + mOrientedRanges.orientation.max = M_PI_2; + mOrientedRanges.orientation.flat = 0; + mOrientedRanges.orientation.fuzz = mOrientationScale; + + // Configure surface dimensions and orientation. + configureSurface(); +} + +void TouchInputMapper::configureAxes() { + mAxes.x.valid = false; + mAxes.y.valid = false; + mAxes.pressure.valid = false; + mAxes.size.valid = false; + mAxes.touchMajor.valid = false; + mAxes.touchMinor.valid = false; + mAxes.toolMajor.valid = false; + mAxes.toolMinor.valid = false; + mAxes.orientation.valid = false; +} + +bool TouchInputMapper::configureSurface() { + // Update orientation and dimensions if needed. + int32_t orientation; + int32_t width, height; + if (mAssociatedDisplayId >= 0) { + if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) { + return false; + } + } else { + orientation = InputReaderPolicyInterface::ROTATION_0; + width = mAxes.x.getRange(); + height = mAxes.y.getRange(); } - if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH) { - in->current.size = in->accumulator.absToolWidth; + bool orientationChanged = mSurfaceOrientation != orientation; + if (orientationChanged) { + mSurfaceOrientation = orientation; } - out->clear(); + bool sizeChanged = mSurfaceWidth != width || mSurfaceHeight != height; + if (sizeChanged) { + mSurfaceWidth = width; + mSurfaceHeight = height; + + // Compute size-dependent translation and scaling factors and place virtual keys. + if (mAxes.x.valid && mAxes.y.valid) { + mXOrigin = mAxes.x.minValue; + mYOrigin = mAxes.y.minValue; + + LOGI("Device configured: id=0x%x, name=%s (display size was changed)", + getDeviceId(), getDeviceName().string()); + + mXScale = float(width) / mAxes.x.getRange(); + mYScale = float(height) / mAxes.y.getRange(); + mXPrecision = 1.0f / mXScale; + mYPrecision = 1.0f / mYScale; + + configureVirtualKeys(); + } else { + mXOrigin = 0; + mYOrigin = 0; + mXScale = 1.0f; + mYScale = 1.0f; + mXPrecision = 1.0f; + mYPrecision = 1.0f; + } + + // Configure touch and tool area ranges. + float diagonal = sqrt(float(width * width + height * height)); + float diagonalFuzz = sqrt(mXScale * mXScale + mYScale * mYScale); + + mOrientedRanges.touchMajor.min = 0.0f; + mOrientedRanges.touchMajor.max = diagonal; + mOrientedRanges.touchMajor.flat = 0.0f; + mOrientedRanges.touchMajor.fuzz = diagonalFuzz; + mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; + + mOrientedRanges.toolMinor = mOrientedRanges.toolMajor = mOrientedRanges.touchMajor; + } + + if (orientationChanged || sizeChanged) { + // Compute oriented surface dimensions, precision, and scales. + float orientedXScale, orientedYScale; + switch (mSurfaceOrientation) { + case InputReaderPolicyInterface::ROTATION_90: + case InputReaderPolicyInterface::ROTATION_270: + mOrientedSurfaceWidth = mSurfaceHeight; + mOrientedSurfaceHeight = mSurfaceWidth; + mOrientedXPrecision = mYPrecision; + mOrientedYPrecision = mXPrecision; + orientedXScale = mYScale; + orientedYScale = mXScale; + break; + default: + mOrientedSurfaceWidth = mSurfaceWidth; + mOrientedSurfaceHeight = mSurfaceHeight; + mOrientedXPrecision = mXPrecision; + mOrientedYPrecision = mYPrecision; + orientedXScale = mXScale; + orientedYScale = mYScale; + break; + } + + // Configure position ranges. + mOrientedRanges.x.min = 0; + mOrientedRanges.x.max = mOrientedSurfaceWidth; + mOrientedRanges.x.flat = 0; + mOrientedRanges.x.fuzz = orientedXScale; - if (in->current.down) { - out->pointerCount = 1; - out->pointers[0].id = 0; - out->pointers[0].x = in->current.x; - out->pointers[0].y = in->current.y; - out->pointers[0].pressure = in->current.pressure; - out->pointers[0].size = in->current.size; - out->pointers[0].touchMajor = in->current.pressure; - out->pointers[0].touchMinor = in->current.pressure; - out->pointers[0].toolMajor = in->current.size; - out->pointers[0].toolMinor = in->current.size; - out->pointers[0].orientation = 0; - out->idToIndex[0] = 0; - out->idBits.markBit(0); + mOrientedRanges.y.min = 0; + mOrientedRanges.y.max = mOrientedSurfaceHeight; + mOrientedRanges.y.flat = 0; + mOrientedRanges.y.fuzz = orientedYScale; } - onTouchScreenChanged(when, device, true); + return true; } -void InputReader::onTouchScreenChanged(nsecs_t when, - InputDevice* device, bool havePointerIds) { - /* Apply policy */ +void TouchInputMapper::configureVirtualKeys() { + assert(mAxes.x.valid && mAxes.y.valid); + + Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions; + getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions); + + { // acquire virtual key lock + AutoMutex _l(mVirtualKeyLock); + + mVirtualKeys.clear(); + + if (virtualKeyDefinitions.size() == 0) { + return; + } + + mVirtualKeys.setCapacity(virtualKeyDefinitions.size()); + + int32_t touchScreenLeft = mAxes.x.minValue; + int32_t touchScreenTop = mAxes.y.minValue; + int32_t touchScreenWidth = mAxes.x.getRange(); + int32_t touchScreenHeight = mAxes.y.getRange(); - int32_t policyActions = mPolicy->interceptTouch(when); + for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) { + const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition = + virtualKeyDefinitions[i]; + + mVirtualKeys.add(); + VirtualKey& virtualKey = mVirtualKeys.editTop(); + + virtualKey.scanCode = virtualKeyDefinition.scanCode; + int32_t keyCode; + uint32_t flags; + if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode, + & keyCode, & flags)) { + LOGW(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); + mVirtualKeys.pop(); // drop the key + continue; + } + + virtualKey.keyCode = keyCode; + virtualKey.flags = flags; + + // convert the key definition's display coordinates into touch coordinates for a hit box + int32_t halfWidth = virtualKeyDefinition.width / 2; + int32_t halfHeight = virtualKeyDefinition.height / 2; + + virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) + * touchScreenWidth / mSurfaceWidth + touchScreenLeft; + virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth) + * touchScreenWidth / mSurfaceWidth + touchScreenLeft; + virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) + * touchScreenHeight / mSurfaceHeight + touchScreenTop; + virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) + * touchScreenHeight / mSurfaceHeight + touchScreenTop; + + LOGI(" VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d", + virtualKey.scanCode, virtualKey.keyCode, + virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); + } + } // release virtual key lock +} + +void TouchInputMapper::reset() { + // Synthesize touch up event if touch is currently down. + // This will also take care of finishing virtual key processing if needed. + if (mLastTouch.pointerCount != 0) { + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + mCurrentTouch.clear(); + syncTouch(when, true); + } + + InputMapper::reset(); + + // Reinitialize. + initialize(); +} + +void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { + /* Refresh associated display information and update our size configuration if needed. */ + + if (! configureSurface()) { + return; + } + + /* Apply policy */ uint32_t policyFlags = 0; - if (! applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) { - device->touchScreen.lastTouch.clear(); + int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags); + + if (! applyStandardPolicyActions(when, policyActions)) { + mLastTouch.clear(); return; // event dropped } /* Preprocess pointer data */ - if (device->touchScreen.parameters.useBadTouchFilter) { - if (device->touchScreen.applyBadTouchFilter()) { + if (mParameters.useBadTouchFilter) { + if (applyBadTouchFilter()) { havePointerIds = false; } } - if (device->touchScreen.parameters.useJumpyTouchFilter) { - if (device->touchScreen.applyJumpyTouchFilter()) { + if (mParameters.useJumpyTouchFilter) { + if (applyJumpyTouchFilter()) { havePointerIds = false; } } if (! havePointerIds) { - device->touchScreen.calculatePointerIds(); + calculatePointerIds(); } - InputDevice::TouchData temp; - InputDevice::TouchData* savedTouch; - if (device->touchScreen.parameters.useAveragingTouchFilter) { - temp.copyFrom(device->touchScreen.currentTouch); + TouchData temp; + TouchData* savedTouch; + if (mParameters.useAveragingTouchFilter) { + temp.copyFrom(mCurrentTouch); savedTouch = & temp; - device->touchScreen.applyAveragingTouchFilter(); + applyAveragingTouchFilter(); } else { - savedTouch = & device->touchScreen.currentTouch; + savedTouch = & mCurrentTouch; } - /* Process virtual keys or touches */ + /* Process touches and virtual keys */ - if (! consumeVirtualKeyTouches(when, device, policyFlags)) { - dispatchTouches(when, device, policyFlags); + TouchResult touchResult = consumeOffScreenTouches(when, policyFlags); + if (touchResult == DISPATCH_TOUCH) { + dispatchTouches(when, policyFlags); } - // Copy current touch to last touch in preparation for the next cycle. - device->touchScreen.lastTouch.copyFrom(*savedTouch); + /* Copy current touch to last touch in preparation for the next cycle. */ + + if (touchResult == DROP_STROKE) { + mLastTouch.clear(); + } else { + mLastTouch.copyFrom(*savedTouch); + } } -bool InputReader::consumeVirtualKeyTouches(nsecs_t when, - InputDevice* device, uint32_t policyFlags) { - switch (device->touchScreen.currentVirtualKey.status) { - case InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_CANCELED: - if (device->touchScreen.currentTouch.pointerCount == 0) { - // Pointer went up after virtual key canceled. - device->touchScreen.currentVirtualKey.status = - InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_UP; - } - return true; // consumed +TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches( + nsecs_t when, uint32_t policyFlags) { + int32_t keyEventAction, keyEventFlags; + int32_t keyCode, scanCode, downTime; + TouchResult touchResult; + + { // acquire virtual key lock + AutoMutex _l(mVirtualKeyLock); - case InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN: - if (device->touchScreen.currentTouch.pointerCount == 0) { - // Pointer went up while virtual key was down. - device->touchScreen.currentVirtualKey.status = - InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_UP; + if (mCurrentVirtualKey.down) { + if (mCurrentTouch.pointerCount == 0) { + // Pointer went up while virtual key was down. + mCurrentVirtualKey.down = false; #if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", - device->touchScreen.currentVirtualKey.keyCode, - device->touchScreen.currentVirtualKey.scanCode); + LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); #endif - dispatchVirtualKey(when, device, policyFlags, AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); - return true; // consumed - } + keyEventAction = AKEY_EVENT_ACTION_UP; + keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; + touchResult = SKIP_TOUCH; + goto DispatchVirtualKey; + } - if (device->touchScreen.currentTouch.pointerCount == 1) { - const InputDevice::VirtualKey* virtualKey = device->touchScreen.findVirtualKeyHit(); - if (virtualKey - && virtualKey->keyCode == device->touchScreen.currentVirtualKey.keyCode) { - // Pointer is still within the space of the virtual key. - return true; // consumed + if (mCurrentTouch.pointerCount == 1) { + int32_t x = mCurrentTouch.pointers[0].x; + int32_t y = mCurrentTouch.pointers[0].y; + const VirtualKey* virtualKey = findVirtualKeyHitLvk(x, y); + if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { + // Pointer is still within the space of the virtual key. + return SKIP_TOUCH; + } } - } - // Pointer left virtual key area or another pointer also went down. - // Send key cancellation. - device->touchScreen.currentVirtualKey.status = - InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_CANCELED; + // Pointer left virtual key area or another pointer also went down. + // Send key cancellation and drop the stroke so subsequent motions will be + // considered fresh downs. This is useful when the user swipes away from the + // virtual key area into the main display surface. + mCurrentVirtualKey.down = false; #if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", - device->touchScreen.currentVirtualKey.keyCode, - device->touchScreen.currentVirtualKey.scanCode); + LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); #endif - dispatchVirtualKey(when, device, policyFlags, AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY - | AKEY_EVENT_FLAG_CANCELED); - return true; // consumed - - default: - if (device->touchScreen.currentTouch.pointerCount == 1 - && device->touchScreen.lastTouch.pointerCount == 0) { - // Pointer just went down. Check for virtual key hit. - const InputDevice::VirtualKey* virtualKey = device->touchScreen.findVirtualKeyHit(); - if (virtualKey) { - device->touchScreen.currentVirtualKey.status = - InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN; - device->touchScreen.currentVirtualKey.downTime = when; - device->touchScreen.currentVirtualKey.keyCode = virtualKey->keyCode; - device->touchScreen.currentVirtualKey.scanCode = virtualKey->scanCode; + keyEventAction = AKEY_EVENT_ACTION_UP; + keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY + | AKEY_EVENT_FLAG_CANCELED; + touchResult = DROP_STROKE; + goto DispatchVirtualKey; + } else { + if (mCurrentTouch.pointerCount >= 1 && mLastTouch.pointerCount == 0) { + // Pointer just went down. Handle off-screen touches, if needed. + int32_t x = mCurrentTouch.pointers[0].x; + int32_t y = mCurrentTouch.pointers[0].y; + if (! isPointInsideSurface(x, y)) { + // If exactly one pointer went down, check for virtual key hit. + // Otherwise we will drop the entire stroke. + if (mCurrentTouch.pointerCount == 1) { + const VirtualKey* virtualKey = findVirtualKeyHitLvk(x, y); + if (virtualKey) { + mCurrentVirtualKey.down = true; + mCurrentVirtualKey.downTime = when; + mCurrentVirtualKey.keyCode = virtualKey->keyCode; + mCurrentVirtualKey.scanCode = virtualKey->scanCode; #if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", - device->touchScreen.currentVirtualKey.keyCode, - device->touchScreen.currentVirtualKey.scanCode); + LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); #endif - dispatchVirtualKey(when, device, policyFlags, AKEY_EVENT_ACTION_DOWN, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); - return true; // consumed + keyEventAction = AKEY_EVENT_ACTION_DOWN; + keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM + | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; + touchResult = SKIP_TOUCH; + goto DispatchVirtualKey; + } + } + return DROP_STROKE; + } } + return DISPATCH_TOUCH; } - return false; // not consumed - } -} -void InputReader::dispatchVirtualKey(nsecs_t when, - InputDevice* device, uint32_t policyFlags, - int32_t keyEventAction, int32_t keyEventFlags) { - updateExportedVirtualKeyState(); + DispatchVirtualKey: + // Collect remaining state needed to dispatch virtual key. + keyCode = mCurrentVirtualKey.keyCode; + scanCode = mCurrentVirtualKey.scanCode; + downTime = mCurrentVirtualKey.downTime; + } // release virtual key lock - int32_t keyCode = device->touchScreen.currentVirtualKey.keyCode; - int32_t scanCode = device->touchScreen.currentVirtualKey.scanCode; - nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime; - int32_t metaState = globalMetaState(); + // Dispatch virtual key. + int32_t metaState = mContext->getGlobalMetaState(); if (keyEventAction == AKEY_EVENT_ACTION_DOWN) { - mPolicy->virtualKeyDownFeedback(); + getPolicy()->virtualKeyDownFeedback(); } - int32_t policyActions = mPolicy->interceptKey(when, device->id, + int32_t policyActions = getPolicy()->interceptKey(when, getDeviceId(), keyEventAction == AKEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags); - if (applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) { - mDispatcher->notifyKey(when, device->id, AINPUT_SOURCE_KEYBOARD, policyFlags, + if (applyStandardPolicyActions(when, policyActions)) { + getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); } + return touchResult; } -void InputReader::dispatchTouches(nsecs_t when, - InputDevice* device, uint32_t policyFlags) { - uint32_t currentPointerCount = device->touchScreen.currentTouch.pointerCount; - uint32_t lastPointerCount = device->touchScreen.lastTouch.pointerCount; +void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { + uint32_t currentPointerCount = mCurrentTouch.pointerCount; + uint32_t lastPointerCount = mLastTouch.pointerCount; if (currentPointerCount == 0 && lastPointerCount == 0) { return; // nothing to do! } - BitSet32 currentIdBits = device->touchScreen.currentTouch.idBits; - BitSet32 lastIdBits = device->touchScreen.lastTouch.idBits; + BitSet32 currentIdBits = mCurrentTouch.idBits; + BitSet32 lastIdBits = mLastTouch.idBits; if (currentIdBits == lastIdBits) { // No pointer id changes so this is a move event. // The dispatcher takes care of batching moves so we don't have to deal with that here. int32_t motionEventAction = AMOTION_EVENT_ACTION_MOVE; - dispatchTouch(when, device, policyFlags, & device->touchScreen.currentTouch, + dispatchTouch(when, policyFlags, & mCurrentTouch, currentIdBits, -1, motionEventAction); } else { // There may be pointers going up and pointers going down at the same time when pointer @@ -791,7 +1536,7 @@ void InputReader::dispatchTouches(nsecs_t when, motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP; } - dispatchTouch(when, device, policyFlags, & device->touchScreen.lastTouch, + dispatchTouch(when, policyFlags, & mLastTouch, oldActiveIdBits, upId, motionEventAction); } @@ -804,40 +1549,24 @@ void InputReader::dispatchTouches(nsecs_t when, int32_t motionEventAction; if (oldActiveIdBits.isEmpty()) { motionEventAction = AMOTION_EVENT_ACTION_DOWN; - device->touchScreen.downTime = when; + mDownTime = when; } else { motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN; } - dispatchTouch(when, device, policyFlags, & device->touchScreen.currentTouch, + dispatchTouch(when, policyFlags, & mCurrentTouch, activeIdBits, downId, motionEventAction); } } } -void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t policyFlags, - InputDevice::TouchData* touch, BitSet32 idBits, uint32_t changedId, +void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, + TouchData* touch, BitSet32 idBits, uint32_t changedId, int32_t motionEventAction) { - int32_t orientedWidth, orientedHeight; - switch (mDisplayOrientation) { - case InputReaderPolicyInterface::ROTATION_90: - case InputReaderPolicyInterface::ROTATION_270: - orientedWidth = mDisplayHeight; - orientedHeight = mDisplayWidth; - break; - default: - orientedWidth = mDisplayWidth; - orientedHeight = mDisplayHeight; - break; - } - uint32_t pointerCount = 0; int32_t pointerIds[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; - const InputDevice::TouchScreenState::Precalculated& precalculated = - device->touchScreen.precalculated; - // Walk through the the active pointers and map touch screen coordinates (TouchData) into // display coordinates (PointerCoords) and adjust for display orientation. while (! idBits.isEmpty()) { @@ -845,55 +1574,57 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli idBits.clearBit(id); uint32_t index = touch->idToIndex[id]; - float x = float(touch->pointers[index].x - - precalculated.xOrigin) * precalculated.xScale; - float y = float(touch->pointers[index].y - - precalculated.yOrigin) * precalculated.yScale; - float pressure = float(touch->pointers[index].pressure - - precalculated.pressureOrigin) * precalculated.pressureScale; - float size = float(touch->pointers[index].size - - precalculated.sizeOrigin) * precalculated.sizeScale; + float x = float(touch->pointers[index].x - mXOrigin) * mXScale; + float y = float(touch->pointers[index].y - mYOrigin) * mYScale; + float pressure = float(touch->pointers[index].pressure - mPressureOrigin) * mPressureScale; + float size = float(touch->pointers[index].size - mSizeOrigin) * mSizeScale; - float orientation = float(touch->pointers[index].orientation) - * precalculated.orientationScale; + float orientation = float(touch->pointers[index].orientation) * mOrientationScale; - bool vertical = abs(orientation) <= M_PI / 8; + float touchMajor, touchMinor, toolMajor, toolMinor; + if (abs(orientation) <= M_PI_4) { + // Nominally vertical orientation: scale major axis by Y, and scale minor axis by X. + touchMajor = float(touch->pointers[index].touchMajor) * mYScale; + touchMinor = float(touch->pointers[index].touchMinor) * mXScale; + toolMajor = float(touch->pointers[index].toolMajor) * mYScale; + toolMinor = float(touch->pointers[index].toolMinor) * mXScale; + } else { + // Nominally horizontal orientation: scale major axis by X, and scale minor axis by Y. + touchMajor = float(touch->pointers[index].touchMajor) * mXScale; + touchMinor = float(touch->pointers[index].touchMinor) * mYScale; + toolMajor = float(touch->pointers[index].toolMajor) * mXScale; + toolMinor = float(touch->pointers[index].toolMinor) * mYScale; + } - switch (mDisplayOrientation) { + switch (mSurfaceOrientation) { case InputReaderPolicyInterface::ROTATION_90: { float xTemp = x; x = y; - y = mDisplayWidth - xTemp; - vertical = ! vertical; + y = mOrientedSurfaceWidth - xTemp; + orientation -= M_PI_2; + if (orientation < - M_PI_2) { + orientation += M_PI; + } break; } case InputReaderPolicyInterface::ROTATION_180: { - x = mDisplayWidth - x; - y = mDisplayHeight - y; + x = mOrientedSurfaceWidth - x; + y = mOrientedSurfaceHeight - y; + orientation = - orientation; break; } case InputReaderPolicyInterface::ROTATION_270: { float xTemp = x; - x = mDisplayHeight - y; + x = mOrientedSurfaceHeight - y; y = xTemp; - vertical = ! vertical; + orientation += M_PI_2; + if (orientation > M_PI_2) { + orientation -= M_PI; + } break; } } - float touchMajor, touchMinor, toolMajor, toolMinor; - if (vertical) { - touchMajor = float(touch->pointers[index].touchMajor) * precalculated.yScale; - touchMinor = float(touch->pointers[index].touchMinor) * precalculated.xScale; - toolMajor = float(touch->pointers[index].toolMajor) * precalculated.yScale; - toolMinor = float(touch->pointers[index].toolMinor) * precalculated.xScale; - } else { - touchMajor = float(touch->pointers[index].touchMajor) * precalculated.xScale; - touchMinor = float(touch->pointers[index].touchMinor) * precalculated.yScale; - toolMajor = float(touch->pointers[index].toolMajor) * precalculated.xScale; - toolMinor = float(touch->pointers[index].toolMinor) * precalculated.yScale; - } - pointerIds[pointerCount] = int32_t(id); pointerCoords[pointerCount].x = x; @@ -919,561 +1650,984 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) { if (pointerCoords[0].x <= 0) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT; - } else if (pointerCoords[0].x >= orientedWidth) { + } else if (pointerCoords[0].x >= mOrientedSurfaceWidth) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT; } if (pointerCoords[0].y <= 0) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP; - } else if (pointerCoords[0].y >= orientedHeight) { + } else if (pointerCoords[0].y >= mOrientedSurfaceHeight) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM; } } - nsecs_t downTime = device->touchScreen.downTime; - mDispatcher->notifyMotion(when, device->id, AINPUT_SOURCE_TOUCHSCREEN, policyFlags, - motionEventAction, globalMetaState(), motionEventEdgeFlags, + getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TOUCHSCREEN, policyFlags, + motionEventAction, getContext()->getGlobalMetaState(), motionEventEdgeFlags, pointerCount, pointerIds, pointerCoords, - 0, 0, downTime); + mOrientedXPrecision, mOrientedYPrecision, mDownTime); } -void InputReader::onTrackballStateChanged(nsecs_t when, - InputDevice* device) { - static const uint32_t DELTA_FIELDS = - InputDevice::TrackballState::Accumulator::FIELD_REL_X - | InputDevice::TrackballState::Accumulator::FIELD_REL_Y; +bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { + if (mAxes.x.valid && mAxes.y.valid) { + return x >= mAxes.x.minValue && x <= mAxes.x.maxValue + && y >= mAxes.y.minValue && y <= mAxes.y.maxValue; + } + return true; +} - /* Refresh display properties so we can trackball moves according to display orientation */ +const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLvk(int32_t x, int32_t y) { + for (size_t i = 0; i < mVirtualKeys.size(); i++) { + const VirtualKey& virtualKey = mVirtualKeys[i]; - if (! refreshDisplayProperties()) { - return; - } +#if DEBUG_VIRTUAL_KEYS + LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " + "left=%d, top=%d, right=%d, bottom=%d", + x, y, + virtualKey.keyCode, virtualKey.scanCode, + virtualKey.hitLeft, virtualKey.hitTop, + virtualKey.hitRight, virtualKey.hitBottom); +#endif - /* Update device state */ + if (virtualKey.isHit(x, y)) { + return & virtualKey; + } + } - uint32_t fields = device->trackball.accumulator.fields; - bool downChanged = fields & InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE; - bool deltaChanged = fields & DELTA_FIELDS; + return NULL; +} - bool down; - if (downChanged) { - if (device->trackball.accumulator.btnMouse) { - device->trackball.current.down = true; - device->trackball.current.downTime = when; - down = true; - } else { - device->trackball.current.down = false; - down = false; +void TouchInputMapper::calculatePointerIds() { + uint32_t currentPointerCount = mCurrentTouch.pointerCount; + uint32_t lastPointerCount = mLastTouch.pointerCount; + + if (currentPointerCount == 0) { + // No pointers to assign. + mCurrentTouch.idBits.clear(); + } else if (lastPointerCount == 0) { + // All pointers are new. + mCurrentTouch.idBits.clear(); + for (uint32_t i = 0; i < currentPointerCount; i++) { + mCurrentTouch.pointers[i].id = i; + mCurrentTouch.idToIndex[i] = i; + mCurrentTouch.idBits.markBit(i); } + } else if (currentPointerCount == 1 && lastPointerCount == 1) { + // Only one pointer and no change in count so it must have the same id as before. + uint32_t id = mLastTouch.pointers[0].id; + mCurrentTouch.pointers[0].id = id; + mCurrentTouch.idToIndex[id] = 0; + mCurrentTouch.idBits.value = BitSet32::valueForBit(id); } else { - down = device->trackball.current.down; - } + // General case. + // We build a heap of squared euclidean distances between current and last pointers + // associated with the current and last pointer indices. Then, we find the best + // match (by distance) for each current pointer. + PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; + + uint32_t heapSize = 0; + for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; + currentPointerIndex++) { + for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; + lastPointerIndex++) { + int64_t deltaX = mCurrentTouch.pointers[currentPointerIndex].x + - mLastTouch.pointers[lastPointerIndex].x; + int64_t deltaY = mCurrentTouch.pointers[currentPointerIndex].y + - mLastTouch.pointers[lastPointerIndex].y; + + uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); + + // Insert new element into the heap (sift up). + heap[heapSize].currentPointerIndex = currentPointerIndex; + heap[heapSize].lastPointerIndex = lastPointerIndex; + heap[heapSize].distance = distance; + heapSize += 1; + } + } - /* Apply policy */ + // Heapify + for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) { + startIndex -= 1; + for (uint32_t parentIndex = startIndex; ;) { + uint32_t childIndex = parentIndex * 2 + 1; + if (childIndex >= heapSize) { + break; + } - int32_t policyActions = mPolicy->interceptTrackball(when, downChanged, down, deltaChanged); + if (childIndex + 1 < heapSize + && heap[childIndex + 1].distance < heap[childIndex].distance) { + childIndex += 1; + } - uint32_t policyFlags = 0; - if (! applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) { - return; // event dropped - } + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } - /* Enqueue motion event for dispatch */ + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + } - int32_t motionEventAction; - if (downChanged) { - motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; - } else { - motionEventAction = AMOTION_EVENT_ACTION_MOVE; - } +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", + i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, + heap[i].distance); + } +#endif - int32_t pointerId = 0; - PointerCoords pointerCoords; - pointerCoords.x = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_X - ? device->trackball.accumulator.relX * device->trackball.precalculated.xScale : 0; - pointerCoords.y = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_Y - ? device->trackball.accumulator.relY * device->trackball.precalculated.yScale : 0; - pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise. - pointerCoords.size = 0; - pointerCoords.touchMajor = 0; - pointerCoords.touchMinor = 0; - pointerCoords.toolMajor = 0; - pointerCoords.toolMinor = 0; - pointerCoords.orientation = 0; + // Pull matches out by increasing order of distance. + // To avoid reassigning pointers that have already been matched, the loop keeps track + // of which last and current pointers have been matched using the matchedXXXBits variables. + // It also tracks the used pointer id bits. + BitSet32 matchedLastBits(0); + BitSet32 matchedCurrentBits(0); + BitSet32 usedIdBits(0); + bool first = true; + for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) { + for (;;) { + if (first) { + // The first time through the loop, we just consume the root element of + // the heap (the one with smallest distance). + first = false; + } else { + // Previous iterations consumed the root element of the heap. + // Pop root element off of the heap (sift down). + heapSize -= 1; + assert(heapSize > 0); + + // Sift down. + heap[0] = heap[heapSize]; + for (uint32_t parentIndex = 0; ;) { + uint32_t childIndex = parentIndex * 2 + 1; + if (childIndex >= heapSize) { + break; + } + + if (childIndex + 1 < heapSize + && heap[childIndex + 1].distance < heap[childIndex].distance) { + childIndex += 1; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", + i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, + heap[i].distance); + } +#endif + } - float temp; - switch (mDisplayOrientation) { - case InputReaderPolicyInterface::ROTATION_90: - temp = pointerCoords.x; - pointerCoords.x = pointerCoords.y; - pointerCoords.y = - temp; - break; + uint32_t currentPointerIndex = heap[0].currentPointerIndex; + if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched - case InputReaderPolicyInterface::ROTATION_180: - pointerCoords.x = - pointerCoords.x; - pointerCoords.y = - pointerCoords.y; - break; + uint32_t lastPointerIndex = heap[0].lastPointerIndex; + if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched - case InputReaderPolicyInterface::ROTATION_270: - temp = pointerCoords.x; - pointerCoords.x = - pointerCoords.y; - pointerCoords.y = temp; - break; - } + matchedCurrentBits.markBit(currentPointerIndex); + matchedLastBits.markBit(lastPointerIndex); - mDispatcher->notifyMotion(when, device->id, AINPUT_SOURCE_TRACKBALL, policyFlags, - motionEventAction, globalMetaState(), AMOTION_EVENT_EDGE_FLAG_NONE, - 1, & pointerId, & pointerCoords, - device->trackball.precalculated.xPrecision, - device->trackball.precalculated.yPrecision, - device->trackball.current.downTime); -} + uint32_t id = mLastTouch.pointers[lastPointerIndex].id; + mCurrentTouch.pointers[currentPointerIndex].id = id; + mCurrentTouch.idToIndex[id] = currentPointerIndex; + usedIdBits.markBit(id); -void InputReader::onConfigurationChanged(nsecs_t when) { - // Reset global meta state because it depends on the list of all configured devices. - resetGlobalMetaState(); +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld", + lastPointerIndex, currentPointerIndex, id, heap[0].distance); +#endif + break; + } + } - // Reset virtual keys, just in case. - updateExportedVirtualKeyState(); + // Assign fresh ids to new pointers. + if (currentPointerCount > lastPointerCount) { + for (uint32_t i = currentPointerCount - lastPointerCount; ;) { + uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit(); + uint32_t id = usedIdBits.firstUnmarkedBit(); - // Update input configuration. - updateExportedInputConfiguration(); + mCurrentTouch.pointers[currentPointerIndex].id = id; + mCurrentTouch.idToIndex[id] = currentPointerIndex; + usedIdBits.markBit(id); - // Enqueue configuration changed. - mDispatcher->notifyConfigurationChanged(when); -} +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - assigned: cur=%d, id=%d", + currentPointerIndex, id); +#endif -bool InputReader::applyStandardInputDispatchPolicyActions(nsecs_t when, - int32_t policyActions, uint32_t* policyFlags) { - if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) { - mDispatcher->notifyAppSwitchComing(when); + if (--i == 0) break; // done + matchedCurrentBits.markBit(currentPointerIndex); + } + } + + // Fix id bits. + mCurrentTouch.idBits = usedIdBits; } +} - if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) { - *policyFlags |= POLICY_FLAG_WOKE_HERE; +/* Special hack for devices that have bad screen data: if one of the + * points has moved more than a screen height from the last position, + * then drop it. */ +bool TouchInputMapper::applyBadTouchFilter() { + // This hack requires valid axis parameters. + if (! mAxes.y.valid) { + return false; } - if (policyActions & InputReaderPolicyInterface::ACTION_BRIGHT_HERE) { - *policyFlags |= POLICY_FLAG_BRIGHT_HERE; + uint32_t pointerCount = mCurrentTouch.pointerCount; + + // Nothing to do if there are no points. + if (pointerCount == 0) { + return false; } - return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH; -} + // Don't do anything if a finger is going down or up. We run + // here before assigning pointer IDs, so there isn't a good + // way to do per-finger matching. + if (pointerCount != mLastTouch.pointerCount) { + return false; + } -void InputReader::resetDisplayProperties() { - mDisplayWidth = mDisplayHeight = -1; - mDisplayOrientation = -1; -} + // We consider a single movement across more than a 7/16 of + // the long size of the screen to be bad. This was a magic value + // determined by looking at the maximum distance it is feasible + // to actually move in one sample. + int32_t maxDeltaY = mAxes.y.getRange() * 7 / 16; + + // XXX The original code in InputDevice.java included commented out + // code for testing the X axis. Note that when we drop a point + // we don't actually restore the old X either. Strange. + // The old code also tries to track when bad points were previously + // detected but it turns out that due to the placement of a "break" + // at the end of the loop, we never set mDroppedBadPoint to true + // so it is effectively dead code. + // Need to figure out if the old code is busted or just overcomplicated + // but working as intended. + + // Look through all new points and see if any are farther than + // acceptable from all previous points. + for (uint32_t i = pointerCount; i-- > 0; ) { + int32_t y = mCurrentTouch.pointers[i].y; + int32_t closestY = INT_MAX; + int32_t closestDeltaY = 0; + +#if DEBUG_HACKS + LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y); +#endif -bool InputReader::refreshDisplayProperties() { - int32_t newWidth, newHeight, newOrientation; - if (mPolicy->getDisplayInfo(0, & newWidth, & newHeight, & newOrientation)) { - if (newWidth != mDisplayWidth || newHeight != mDisplayHeight) { - LOGD("Display size changed from %dx%d to %dx%d, updating device configuration", - mDisplayWidth, mDisplayHeight, newWidth, newHeight); + for (uint32_t j = pointerCount; j-- > 0; ) { + int32_t lastY = mLastTouch.pointers[j].y; + int32_t deltaY = abs(y - lastY); - mDisplayWidth = newWidth; - mDisplayHeight = newHeight; +#if DEBUG_HACKS + LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d", + j, lastY, deltaY); +#endif - for (size_t i = 0; i < mDevices.size(); i++) { - configureDeviceForCurrentDisplaySize(mDevices.valueAt(i)); + if (deltaY < maxDeltaY) { + goto SkipSufficientlyClosePoint; + } + if (deltaY < closestDeltaY) { + closestDeltaY = deltaY; + closestY = lastY; } } - if (newOrientation != mDisplayOrientation) { - LOGD("Display orientation changed to %d", mDisplayOrientation); + // Must not have found a close enough match. +#if DEBUG_HACKS + LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d", + i, y, closestY, closestDeltaY, maxDeltaY); +#endif - mDisplayOrientation = newOrientation; - } - return true; - } else { - resetDisplayProperties(); - return false; + mCurrentTouch.pointers[i].y = closestY; + return true; // XXX original code only corrects one point + + SkipSufficientlyClosePoint: ; } -} -InputDevice* InputReader::getDevice(int32_t deviceId) { - ssize_t index = mDevices.indexOfKey(deviceId); - return index >= 0 ? mDevices.valueAt((size_t) index) : NULL; + // No change. + return false; } -InputDevice* InputReader::getNonIgnoredDevice(int32_t deviceId) { - InputDevice* device = getDevice(deviceId); - return device && ! device->ignored ? device : NULL; -} +/* Special hack for devices that have bad screen data: drop points where + * the coordinate value for one axis has jumped to the other pointer's location. + */ +bool TouchInputMapper::applyJumpyTouchFilter() { + // This hack requires valid axis parameters. + if (! mAxes.y.valid) { + return false; + } -void InputReader::addDevice(nsecs_t when, int32_t deviceId) { - uint32_t classes = mEventHub->getDeviceClasses(deviceId); - String8 name = mEventHub->getDeviceName(deviceId); - InputDevice* device = new InputDevice(deviceId, classes, name); + uint32_t pointerCount = mCurrentTouch.pointerCount; + if (mLastTouch.pointerCount != pointerCount) { +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Different pointer count %d -> %d", + mLastTouch.pointerCount, pointerCount); + for (uint32_t i = 0; i < pointerCount; i++) { + LOGD(" Pointer %d (%d, %d)", i, + mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y); + } +#endif - if (classes != 0) { - LOGI("Device added: id=0x%x, name=%s, classes=%02x", device->id, - device->name.string(), device->classes); + if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) { + if (mLastTouch.pointerCount == 1 && pointerCount == 2) { + // Just drop the first few events going from 1 to 2 pointers. + // They're bad often enough that they're not worth considering. + mCurrentTouch.pointerCount = 1; + mJumpyTouchFilter.jumpyPointsDropped += 1; - configureDevice(device); - } else { - LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", device->id, - device->name.string()); +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Pointer 2 dropped"); +#endif + return true; + } else if (mLastTouch.pointerCount == 2 && pointerCount == 1) { + // The event when we go from 2 -> 1 tends to be messed up too + mCurrentTouch.pointerCount = 2; + mCurrentTouch.pointers[0] = mLastTouch.pointers[0]; + mCurrentTouch.pointers[1] = mLastTouch.pointers[1]; + mJumpyTouchFilter.jumpyPointsDropped += 1; + +#if DEBUG_HACKS + for (int32_t i = 0; i < 2; i++) { + LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i, + mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y); + } +#endif + return true; + } + } + // Reset jumpy points dropped on other transitions or if limit exceeded. + mJumpyTouchFilter.jumpyPointsDropped = 0; - device->ignored = true; +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Transition - drop limit reset"); +#endif + return false; } - device->reset(); + // We have the same number of pointers as last time. + // A 'jumpy' point is one where the coordinate value for one axis + // has jumped to the other pointer's location. No need to do anything + // else if we only have one pointer. + if (pointerCount < 2) { + return false; + } - mDevices.add(deviceId, device); + if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) { + int jumpyEpsilon = mAxes.y.getRange() / JUMPY_EPSILON_DIVISOR; - if (! device->ignored) { - onConfigurationChanged(when); - } -} + // We only replace the single worst jumpy point as characterized by pointer distance + // in a single axis. + int32_t badPointerIndex = -1; + int32_t badPointerReplacementIndex = -1; + int32_t badPointerDistance = INT_MIN; // distance to be corrected -void InputReader::removeDevice(nsecs_t when, InputDevice* device) { - mDevices.removeItem(device->id); + for (uint32_t i = pointerCount; i-- > 0; ) { + int32_t x = mCurrentTouch.pointers[i].x; + int32_t y = mCurrentTouch.pointers[i].y; - if (! device->ignored) { - LOGI("Device removed: id=0x%x, name=%s, classes=%02x", device->id, - device->name.string(), device->classes); +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y); +#endif - onConfigurationChanged(when); - } else { - LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)", device->id, - device->name.string()); - } + // Check if a touch point is too close to another's coordinates + bool dropX = false, dropY = false; + for (uint32_t j = 0; j < pointerCount; j++) { + if (i == j) { + continue; + } - delete device; -} + if (abs(x - mCurrentTouch.pointers[j].x) <= jumpyEpsilon) { + dropX = true; + break; + } -void InputReader::configureDevice(InputDevice* device) { - if (device->isMultiTouchScreen()) { - configureAbsoluteAxisInfo(device, ABS_MT_POSITION_X, "X", - & device->touchScreen.parameters.xAxis); - configureAbsoluteAxisInfo(device, ABS_MT_POSITION_Y, "Y", - & device->touchScreen.parameters.yAxis); - configureAbsoluteAxisInfo(device, ABS_MT_TOUCH_MAJOR, "Pressure", - & device->touchScreen.parameters.pressureAxis); - configureAbsoluteAxisInfo(device, ABS_MT_WIDTH_MAJOR, "Size", - & device->touchScreen.parameters.sizeAxis); - configureAbsoluteAxisInfo(device, ABS_MT_ORIENTATION, "Orientation", - & device->touchScreen.parameters.orientationAxis); - } else if (device->isSingleTouchScreen()) { - configureAbsoluteAxisInfo(device, ABS_X, "X", - & device->touchScreen.parameters.xAxis); - configureAbsoluteAxisInfo(device, ABS_Y, "Y", - & device->touchScreen.parameters.yAxis); - configureAbsoluteAxisInfo(device, ABS_PRESSURE, "Pressure", - & device->touchScreen.parameters.pressureAxis); - configureAbsoluteAxisInfo(device, ABS_TOOL_WIDTH, "Size", - & device->touchScreen.parameters.sizeAxis); - device->touchScreen.parameters.orientationAxis.valid = false; - } - - if (device->isTouchScreen()) { - device->touchScreen.parameters.useBadTouchFilter = - mPolicy->filterTouchEvents(); - device->touchScreen.parameters.useAveragingTouchFilter = - mPolicy->filterTouchEvents(); - device->touchScreen.parameters.useJumpyTouchFilter = - mPolicy->filterJumpyTouchEvents(); - - if (device->touchScreen.parameters.pressureAxis.valid) { - device->touchScreen.precalculated.pressureOrigin = - device->touchScreen.parameters.pressureAxis.minValue; - device->touchScreen.precalculated.pressureScale = - 1.0f / device->touchScreen.parameters.pressureAxis.range; - } else { - device->touchScreen.precalculated.pressureOrigin = 0; - device->touchScreen.precalculated.pressureScale = 1.0f; - } + if (abs(y - mCurrentTouch.pointers[j].y) <= jumpyEpsilon) { + dropY = true; + break; + } + } + if (! dropX && ! dropY) { + continue; // not jumpy + } - if (device->touchScreen.parameters.sizeAxis.valid) { - device->touchScreen.precalculated.sizeOrigin = - device->touchScreen.parameters.sizeAxis.minValue; - device->touchScreen.precalculated.sizeScale = - 1.0f / device->touchScreen.parameters.sizeAxis.range; - } else { - device->touchScreen.precalculated.sizeOrigin = 0; - device->touchScreen.precalculated.sizeScale = 1.0f; - } + // Find a replacement candidate by comparing with older points on the + // complementary (non-jumpy) axis. + int32_t distance = INT_MIN; // distance to be corrected + int32_t replacementIndex = -1; + + if (dropX) { + // X looks too close. Find an older replacement point with a close Y. + int32_t smallestDeltaY = INT_MAX; + for (uint32_t j = 0; j < pointerCount; j++) { + int32_t deltaY = abs(y - mLastTouch.pointers[j].y); + if (deltaY < smallestDeltaY) { + smallestDeltaY = deltaY; + replacementIndex = j; + } + } + distance = abs(x - mLastTouch.pointers[replacementIndex].x); + } else { + // Y looks too close. Find an older replacement point with a close X. + int32_t smallestDeltaX = INT_MAX; + for (uint32_t j = 0; j < pointerCount; j++) { + int32_t deltaX = abs(x - mLastTouch.pointers[j].x); + if (deltaX < smallestDeltaX) { + smallestDeltaX = deltaX; + replacementIndex = j; + } + } + distance = abs(y - mLastTouch.pointers[replacementIndex].y); + } - if (device->touchScreen.parameters.orientationAxis.valid - && device->touchScreen.parameters.orientationAxis.maxValue > 0) { - device->touchScreen.precalculated.orientationScale = - M_PI_4 / device->touchScreen.parameters.orientationAxis.maxValue; - } else { - device->touchScreen.precalculated.orientationScale = 0.0f; + // If replacing this pointer would correct a worse error than the previous ones + // considered, then use this replacement instead. + if (distance > badPointerDistance) { + badPointerIndex = i; + badPointerReplacementIndex = replacementIndex; + badPointerDistance = distance; + } } - } - if (device->isTrackball()) { - device->trackball.precalculated.xPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - device->trackball.precalculated.yPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - device->trackball.precalculated.xScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - device->trackball.precalculated.yScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + // Correct the jumpy pointer if one was found. + if (badPointerIndex >= 0) { +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)", + badPointerIndex, + mLastTouch.pointers[badPointerReplacementIndex].x, + mLastTouch.pointers[badPointerReplacementIndex].y); +#endif + + mCurrentTouch.pointers[badPointerIndex].x = + mLastTouch.pointers[badPointerReplacementIndex].x; + mCurrentTouch.pointers[badPointerIndex].y = + mLastTouch.pointers[badPointerReplacementIndex].y; + mJumpyTouchFilter.jumpyPointsDropped += 1; + return true; + } } - configureDeviceForCurrentDisplaySize(device); + mJumpyTouchFilter.jumpyPointsDropped = 0; + return false; } -void InputReader::configureDeviceForCurrentDisplaySize(InputDevice* device) { - if (device->isTouchScreen()) { - if (device->touchScreen.parameters.xAxis.valid - && device->touchScreen.parameters.yAxis.valid) { - device->touchScreen.precalculated.xOrigin = - device->touchScreen.parameters.xAxis.minValue; - device->touchScreen.precalculated.yOrigin = - device->touchScreen.parameters.yAxis.minValue; +/* Special hack for devices that have bad screen data: aggregate and + * compute averages of the coordinate data, to reduce the amount of + * jitter seen by applications. */ +void TouchInputMapper::applyAveragingTouchFilter() { + for (uint32_t currentIndex = 0; currentIndex < mCurrentTouch.pointerCount; currentIndex++) { + uint32_t id = mCurrentTouch.pointers[currentIndex].id; + int32_t x = mCurrentTouch.pointers[currentIndex].x; + int32_t y = mCurrentTouch.pointers[currentIndex].y; + int32_t pressure = mCurrentTouch.pointers[currentIndex].pressure; + + if (mLastTouch.idBits.hasBit(id)) { + // Pointer was down before and is still down now. + // Compute average over history trace. + uint32_t start = mAveragingTouchFilter.historyStart[id]; + uint32_t end = mAveragingTouchFilter.historyEnd[id]; + + int64_t deltaX = x - mAveragingTouchFilter.historyData[end].pointers[id].x; + int64_t deltaY = y - mAveragingTouchFilter.historyData[end].pointers[id].y; + uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); + +#if DEBUG_HACKS + LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld", + id, distance); +#endif - if (mDisplayWidth < 0) { - LOGD("Skipping part of touch screen configuration since display size is unknown."); + if (distance < AVERAGING_DISTANCE_LIMIT) { + // Increment end index in preparation for recording new historical data. + end += 1; + if (end > AVERAGING_HISTORY_SIZE) { + end = 0; + } - device->touchScreen.precalculated.xScale = 1.0f; - device->touchScreen.precalculated.yScale = 1.0f; - } else { - LOGI("Device configured: id=0x%x, name=%s (display size was changed)", device->id, - device->name.string()); + // If the end index has looped back to the start index then we have filled + // the historical trace up to the desired size so we drop the historical + // data at the start of the trace. + if (end == start) { + start += 1; + if (start > AVERAGING_HISTORY_SIZE) { + start = 0; + } + } - device->touchScreen.precalculated.xScale = - float(mDisplayWidth) / device->touchScreen.parameters.xAxis.range; - device->touchScreen.precalculated.yScale = - float(mDisplayHeight) / device->touchScreen.parameters.yAxis.range; + // Add the raw data to the historical trace. + mAveragingTouchFilter.historyStart[id] = start; + mAveragingTouchFilter.historyEnd[id] = end; + mAveragingTouchFilter.historyData[end].pointers[id].x = x; + mAveragingTouchFilter.historyData[end].pointers[id].y = y; + mAveragingTouchFilter.historyData[end].pointers[id].pressure = pressure; + + // Average over all historical positions in the trace by total pressure. + int32_t averagedX = 0; + int32_t averagedY = 0; + int32_t totalPressure = 0; + for (;;) { + int32_t historicalX = mAveragingTouchFilter.historyData[start].pointers[id].x; + int32_t historicalY = mAveragingTouchFilter.historyData[start].pointers[id].y; + int32_t historicalPressure = mAveragingTouchFilter.historyData[start] + .pointers[id].pressure; + + averagedX += historicalX * historicalPressure; + averagedY += historicalY * historicalPressure; + totalPressure += historicalPressure; + + if (start == end) { + break; + } + + start += 1; + if (start > AVERAGING_HISTORY_SIZE) { + start = 0; + } + } + + averagedX /= totalPressure; + averagedY /= totalPressure; + +#if DEBUG_HACKS + LOGD("AveragingTouchFilter: Pointer id %d - " + "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure, + averagedX, averagedY); +#endif - configureVirtualKeys(device); + mCurrentTouch.pointers[currentIndex].x = averagedX; + mCurrentTouch.pointers[currentIndex].y = averagedY; + } else { +#if DEBUG_HACKS + LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id); +#endif } } else { - device->touchScreen.precalculated.xOrigin = 0; - device->touchScreen.precalculated.xScale = 1.0f; - device->touchScreen.precalculated.yOrigin = 0; - device->touchScreen.precalculated.yScale = 1.0f; +#if DEBUG_HACKS + LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id); +#endif } + + // Reset pointer history. + mAveragingTouchFilter.historyStart[id] = 0; + mAveragingTouchFilter.historyEnd[id] = 0; + mAveragingTouchFilter.historyData[0].pointers[id].x = x; + mAveragingTouchFilter.historyData[0].pointers[id].y = y; + mAveragingTouchFilter.historyData[0].pointers[id].pressure = pressure; } } -void InputReader::configureVirtualKeys(InputDevice* device) { - assert(device->touchScreen.parameters.xAxis.valid - && device->touchScreen.parameters.yAxis.valid); - - device->touchScreen.virtualKeys.clear(); - - Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions; - mPolicy->getVirtualKeyDefinitions(device->name, virtualKeyDefinitions); - if (virtualKeyDefinitions.size() == 0) { - return; - } +int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + { // acquire virtual key lock + AutoMutex _l(mVirtualKeyLock); - device->touchScreen.virtualKeys.setCapacity(virtualKeyDefinitions.size()); + if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) { + return AKEY_STATE_VIRTUAL; + } - int32_t touchScreenLeft = device->touchScreen.parameters.xAxis.minValue; - int32_t touchScreenTop = device->touchScreen.parameters.yAxis.minValue; - int32_t touchScreenWidth = device->touchScreen.parameters.xAxis.range; - int32_t touchScreenHeight = device->touchScreen.parameters.yAxis.range; + for (size_t i = 0; i < mVirtualKeys.size(); i++) { + const VirtualKey& virtualKey = mVirtualKeys[i]; + if (virtualKey.keyCode == keyCode) { + return AKEY_STATE_UP; + } + } + } // release virtual key lock - for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) { - const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition = - virtualKeyDefinitions[i]; + return AKEY_STATE_UNKNOWN; +} - device->touchScreen.virtualKeys.add(); - InputDevice::VirtualKey& virtualKey = - device->touchScreen.virtualKeys.editTop(); +int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + { // acquire virtual key lock + AutoMutex _l(mVirtualKeyLock); - virtualKey.scanCode = virtualKeyDefinition.scanCode; - int32_t keyCode; - uint32_t flags; - if (mEventHub->scancodeToKeycode(device->id, virtualKey.scanCode, - & keyCode, & flags)) { - LOGW(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); - device->touchScreen.virtualKeys.pop(); // drop the key - continue; + if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) { + return AKEY_STATE_VIRTUAL; } - virtualKey.keyCode = keyCode; - virtualKey.flags = flags; + for (size_t i = 0; i < mVirtualKeys.size(); i++) { + const VirtualKey& virtualKey = mVirtualKeys[i]; + if (virtualKey.scanCode == scanCode) { + return AKEY_STATE_UP; + } + } + } // release virtual key lock - // convert the key definition's display coordinates into touch coordinates for a hit box - int32_t halfWidth = virtualKeyDefinition.width / 2; - int32_t halfHeight = virtualKeyDefinition.height / 2; + return AKEY_STATE_UNKNOWN; +} - virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) - * touchScreenWidth / mDisplayWidth + touchScreenLeft; - virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth) - * touchScreenWidth / mDisplayWidth + touchScreenLeft; - virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) - * touchScreenHeight / mDisplayHeight + touchScreenTop; - virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) - * touchScreenHeight / mDisplayHeight + touchScreenTop; +bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + { // acquire virtual key lock + AutoMutex _l(mVirtualKeyLock); - LOGI(" VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d", - virtualKey.scanCode, virtualKey.keyCode, - virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); - } -} + for (size_t i = 0; i < mVirtualKeys.size(); i++) { + const VirtualKey& virtualKey = mVirtualKeys[i]; -void InputReader::configureAbsoluteAxisInfo(InputDevice* device, - int axis, const char* name, InputDevice::AbsoluteAxisInfo* out) { - if (! mEventHub->getAbsoluteInfo(device->id, axis, - & out->minValue, & out->maxValue, & out->flat, &out->fuzz)) { - out->range = out->maxValue - out->minValue; - if (out->range != 0) { - LOGI(" %s: min=%d max=%d flat=%d fuzz=%d", - name, out->minValue, out->maxValue, out->flat, out->fuzz); - out->valid = true; - return; + for (size_t i = 0; i < numCodes; i++) { + if (virtualKey.keyCode == keyCodes[i]) { + outFlags[i] = 1; + } + } } - } + } // release virtual key lock - out->valid = false; - out->minValue = 0; - out->maxValue = 0; - out->flat = 0; - out->fuzz = 0; - out->range = 0; - LOGI(" %s: unknown axis values, marking as invalid", name); + return true; } -void InputReader::configureExcludedDevices() { - Vector<String8> excludedDeviceNames; - mPolicy->getExcludedDeviceNames(excludedDeviceNames); - for (size_t i = 0; i < excludedDeviceNames.size(); i++) { - mEventHub->addExcludedDevice(excludedDeviceNames[i]); - } +// --- SingleTouchInputMapper --- + +SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : + TouchInputMapper(device, associatedDisplayId) { + initialize(); } -void InputReader::resetGlobalMetaState() { - mGlobalMetaState = -1; +SingleTouchInputMapper::~SingleTouchInputMapper() { } -int32_t InputReader::globalMetaState() { - if (mGlobalMetaState == -1) { - mGlobalMetaState = 0; - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - if (device->isKeyboard()) { - mGlobalMetaState |= device->keyboard.current.metaState; +void SingleTouchInputMapper::initialize() { + mAccumulator.clear(); + + mDown = false; + mX = 0; + mY = 0; + mPressure = 0; + mSize = 0; +} + +void SingleTouchInputMapper::reset() { + TouchInputMapper::reset(); + + // Reinitialize. + initialize(); + } + +void SingleTouchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: + switch (rawEvent->scanCode) { + case BTN_TOUCH: + mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH; + mAccumulator.btnTouch = rawEvent->value != 0; + + sync(rawEvent->when); + mAccumulator.clear(); + break; + } + break; + + case EV_ABS: + switch (rawEvent->scanCode) { + case ABS_X: + mAccumulator.fields |= Accumulator::FIELD_ABS_X; + mAccumulator.absX = rawEvent->value; + break; + case ABS_Y: + mAccumulator.fields |= Accumulator::FIELD_ABS_Y; + mAccumulator.absY = rawEvent->value; + break; + case ABS_PRESSURE: + mAccumulator.fields |= Accumulator::FIELD_ABS_PRESSURE; + mAccumulator.absPressure = rawEvent->value; + break; + case ABS_TOOL_WIDTH: + mAccumulator.fields |= Accumulator::FIELD_ABS_TOOL_WIDTH; + mAccumulator.absToolWidth = rawEvent->value; + break; + } + break; + + case EV_SYN: + switch (rawEvent->scanCode) { + case SYN_REPORT: + if (mAccumulator.isDirty()) { + sync(rawEvent->when); + mAccumulator.clear(); } + break; } + break; } - return mGlobalMetaState; } -void InputReader::updateExportedVirtualKeyState() { - int32_t keyCode = -1, scanCode = -1; +void SingleTouchInputMapper::sync(nsecs_t when) { + /* Update device state */ - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - if (device->isTouchScreen()) { - if (device->touchScreen.currentVirtualKey.status - == InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN) { - keyCode = device->touchScreen.currentVirtualKey.keyCode; - scanCode = device->touchScreen.currentVirtualKey.scanCode; - } - } + uint32_t fields = mAccumulator.fields; + + if (fields & Accumulator::FIELD_BTN_TOUCH) { + mDown = mAccumulator.btnTouch; + } + + if (fields & Accumulator::FIELD_ABS_X) { + mX = mAccumulator.absX; } - { // acquire exported state lock - AutoMutex _l(mExportedStateLock); + if (fields & Accumulator::FIELD_ABS_Y) { + mY = mAccumulator.absY; + } - mExportedVirtualKeyCode = keyCode; - mExportedVirtualScanCode = scanCode; - } // release exported state lock + if (fields & Accumulator::FIELD_ABS_PRESSURE) { + mPressure = mAccumulator.absPressure; + } + + if (fields & Accumulator::FIELD_ABS_TOOL_WIDTH) { + mSize = mAccumulator.absToolWidth; + } + + mCurrentTouch.clear(); + + if (mDown) { + mCurrentTouch.pointerCount = 1; + mCurrentTouch.pointers[0].id = 0; + mCurrentTouch.pointers[0].x = mX; + mCurrentTouch.pointers[0].y = mY; + mCurrentTouch.pointers[0].pressure = mPressure; + mCurrentTouch.pointers[0].size = mSize; + mCurrentTouch.pointers[0].touchMajor = mPressure; + mCurrentTouch.pointers[0].touchMinor = mPressure; + mCurrentTouch.pointers[0].toolMajor = mSize; + mCurrentTouch.pointers[0].toolMinor = mSize; + mCurrentTouch.pointers[0].orientation = 0; + mCurrentTouch.idToIndex[0] = 0; + mCurrentTouch.idBits.markBit(0); + } + + syncTouch(when, true); } -bool InputReader::getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const { - { // acquire exported state lock - AutoMutex _l(mExportedStateLock); +void SingleTouchInputMapper::configureAxes() { + TouchInputMapper::configureAxes(); - *outKeyCode = mExportedVirtualKeyCode; - *outScanCode = mExportedVirtualScanCode; - return mExportedVirtualKeyCode != -1; - } // release exported state lock + // The axes are aliased to take into account the manner in which they are presented + // as part of the TouchData during the sync. + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mAxes.x); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mAxes.y); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mAxes.pressure); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mAxes.size); + + mAxes.touchMajor = mAxes.pressure; + mAxes.touchMinor = mAxes.pressure; + mAxes.toolMajor = mAxes.size; + mAxes.toolMinor = mAxes.size; } -void InputReader::updateExportedInputConfiguration() { - int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH; - int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS; - int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV; - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - int32_t deviceClasses = device->classes; +// --- MultiTouchInputMapper --- + +MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : + TouchInputMapper(device, associatedDisplayId) { + initialize(); +} + +MultiTouchInputMapper::~MultiTouchInputMapper() { +} + +void MultiTouchInputMapper::initialize() { + mAccumulator.clear(); +} + +void MultiTouchInputMapper::reset() { + TouchInputMapper::reset(); - if (deviceClasses & INPUT_DEVICE_CLASS_TOUCHSCREEN) { - touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER; + // Reinitialize. + initialize(); +} + +void MultiTouchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_ABS: { + uint32_t pointerIndex = mAccumulator.pointerCount; + Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex]; + + switch (rawEvent->scanCode) { + case ABS_MT_POSITION_X: + pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_X; + pointer->absMTPositionX = rawEvent->value; + break; + case ABS_MT_POSITION_Y: + pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y; + pointer->absMTPositionY = rawEvent->value; + break; + case ABS_MT_TOUCH_MAJOR: + pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR; + pointer->absMTTouchMajor = rawEvent->value; + break; + case ABS_MT_TOUCH_MINOR: + pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR; + pointer->absMTTouchMinor = rawEvent->value; + break; + case ABS_MT_WIDTH_MAJOR: + pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; + pointer->absMTWidthMajor = rawEvent->value; + break; + case ABS_MT_WIDTH_MINOR: + pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR; + pointer->absMTWidthMinor = rawEvent->value; + break; + case ABS_MT_ORIENTATION: + pointer->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION; + pointer->absMTOrientation = rawEvent->value; + break; + case ABS_MT_TRACKING_ID: + pointer->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID; + pointer->absMTTrackingId = rawEvent->value; + break; } - if (deviceClasses & INPUT_DEVICE_CLASS_ALPHAKEY) { - keyboardConfig = InputConfiguration::KEYBOARD_QWERTY; + break; + } + + case EV_SYN: + switch (rawEvent->scanCode) { + case SYN_MT_REPORT: { + // MultiTouch Sync: The driver has returned all data for *one* of the pointers. + uint32_t pointerIndex = mAccumulator.pointerCount; + + if (mAccumulator.pointers[pointerIndex].fields) { + if (pointerIndex == MAX_POINTERS) { + LOGW("MultiTouch device driver returned more than maximum of %d pointers.", + MAX_POINTERS); + } else { + pointerIndex += 1; + mAccumulator.pointerCount = pointerIndex; + } + } + + mAccumulator.pointers[pointerIndex].clear(); + break; } - if (deviceClasses & INPUT_DEVICE_CLASS_TRACKBALL) { - navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL; - } else if (deviceClasses & INPUT_DEVICE_CLASS_DPAD) { - navigationConfig = InputConfiguration::NAVIGATION_DPAD; + + case SYN_REPORT: + if (mAccumulator.isDirty()) { + sync(rawEvent->when); + mAccumulator.clear(); + } + break; } + break; } +} - { // acquire exported state lock - AutoMutex _l(mExportedStateLock); +void MultiTouchInputMapper::sync(nsecs_t when) { + static const uint32_t REQUIRED_FIELDS = + Accumulator::FIELD_ABS_MT_POSITION_X + | Accumulator::FIELD_ABS_MT_POSITION_Y + | Accumulator::FIELD_ABS_MT_TOUCH_MAJOR + | Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; - mExportedInputConfiguration.touchScreen = touchScreenConfig; - mExportedInputConfiguration.keyboard = keyboardConfig; - mExportedInputConfiguration.navigation = navigationConfig; - } // release exported state lock -} + /* Update device state */ + + uint32_t inCount = mAccumulator.pointerCount; + uint32_t outCount = 0; + bool havePointerIds = true; -void InputReader::getCurrentInputConfiguration(InputConfiguration* outConfiguration) const { - { // acquire exported state lock - AutoMutex _l(mExportedStateLock); + mCurrentTouch.clear(); - *outConfiguration = mExportedInputConfiguration; - } // release exported state lock -} + for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) { + uint32_t fields = mAccumulator.pointers[inIndex].fields; -int32_t InputReader::getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t scanCode) const { - { // acquire exported state lock - AutoMutex _l(mExportedStateLock); + if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) { +#if DEBUG_POINTERS + LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d", + inIndex, fields); + continue; +#endif + } - if (mExportedVirtualScanCode == scanCode) { - return AKEY_STATE_VIRTUAL; + if (mAccumulator.pointers[inIndex].absMTTouchMajor <= 0) { + // Pointer is not down. Drop it. + continue; } - } // release exported state lock - return mEventHub->getScanCodeState(deviceId, deviceClasses, scanCode); -} + mCurrentTouch.pointers[outCount].x = mAccumulator.pointers[inIndex].absMTPositionX; + mCurrentTouch.pointers[outCount].y = mAccumulator.pointers[inIndex].absMTPositionY; -int32_t InputReader::getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t keyCode) const { - { // acquire exported state lock - AutoMutex _l(mExportedStateLock); + mCurrentTouch.pointers[outCount].touchMajor = + mAccumulator.pointers[inIndex].absMTTouchMajor; + mCurrentTouch.pointers[outCount].touchMinor = + (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0 + ? mAccumulator.pointers[inIndex].absMTTouchMinor + : mAccumulator.pointers[inIndex].absMTTouchMajor; - if (mExportedVirtualKeyCode == keyCode) { - return AKEY_STATE_VIRTUAL; + mCurrentTouch.pointers[outCount].toolMajor = + mAccumulator.pointers[inIndex].absMTWidthMajor; + mCurrentTouch.pointers[outCount].toolMinor = + (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0 + ? mAccumulator.pointers[inIndex].absMTWidthMinor + : mAccumulator.pointers[inIndex].absMTWidthMajor; + + mCurrentTouch.pointers[outCount].orientation = + (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) != 0 + ? mAccumulator.pointers[inIndex].absMTOrientation : 0; + + // Derive an approximation of pressure and size. + // FIXME assignment of pressure may be incorrect, probably better to let + // pressure = touch / width. Later on we pass width to MotionEvent as a size, which + // isn't quite right either. Should be using touch for that. + mCurrentTouch.pointers[outCount].pressure = mAccumulator.pointers[inIndex].absMTTouchMajor; + mCurrentTouch.pointers[outCount].size = mAccumulator.pointers[inIndex].absMTWidthMajor; + + if (havePointerIds) { + if (fields & Accumulator:: + FIELD_ABS_MT_TRACKING_ID) { + uint32_t id = uint32_t(mAccumulator.pointers[inIndex].absMTTrackingId); + + if (id > MAX_POINTER_ID) { +#if DEBUG_POINTERS + LOGD("Pointers: Ignoring driver provided pointer id %d because " + "it is larger than max supported id %d for optimizations", + id, MAX_POINTER_ID); +#endif + havePointerIds = false; + } + else { + mCurrentTouch.pointers[outCount].id = id; + mCurrentTouch.idToIndex[id] = outCount; + mCurrentTouch.idBits.markBit(id); + } + } else { + havePointerIds = false; + } } - } // release exported state lock - return mEventHub->getKeyCodeState(deviceId, deviceClasses, keyCode); -} + outCount += 1; + } -int32_t InputReader::getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses, - int32_t sw) const { - return mEventHub->getSwitchState(deviceId, deviceClasses, sw); -} + mCurrentTouch.pointerCount = outCount; -bool InputReader::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { - return mEventHub->hasKeys(numCodes, keyCodes, outFlags); + syncTouch(when, havePointerIds); } +void MultiTouchInputMapper::configureAxes() { + TouchInputMapper::configureAxes(); -// --- InputReaderThread --- + // The axes are aliased to take into account the manner in which they are presented + // as part of the TouchData during the sync. + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mAxes.x); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mAxes.y); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mAxes.touchMajor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mAxes.touchMinor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mAxes.toolMajor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mAxes.toolMinor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mAxes.orientation); -InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) : - Thread(/*canCallJava*/ true), mReader(reader) { -} + if (! mAxes.touchMinor.valid) { + mAxes.touchMinor = mAxes.touchMajor; + } -InputReaderThread::~InputReaderThread() { -} + if (! mAxes.toolMinor.valid) { + mAxes.toolMinor = mAxes.toolMajor; + } -bool InputReaderThread::threadLoop() { - mReader->loopOnce(); - return true; + mAxes.pressure = mAxes.touchMajor; + mAxes.size = mAxes.toolMajor; } + } // namespace android |