diff options
Diffstat (limited to 'libs/ui/InputReader.cpp')
-rw-r--r-- | libs/ui/InputReader.cpp | 2942 |
1 files changed, 2048 insertions, 894 deletions
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 |