replicant-frameworks_native/libs/ui/InputReader.cpp
Jeff Brown a665ca805c Input dispatcher ANR handling enhancements.
This change is essentially a rewrite of the main input dispatcher loop
with the target identification folded in.  Since the input dispatcher now
has all of the window state, it can make better decisions about
when to ANR.

Added a .5 second deadline for processing app switch keys.  This behavior
predates Gingerbread but had not previously been ported.

Fixed some timing inaccuracies in the ANR accounting that could cause
applications to ANR sooner than they should have.

Added a mechanism for tracking key and motion events that have been
dispatched to a window so that appropriate cancelation events can be
synthesized when recovering from ANR.  This change helps to keep
applications in sync so they don't end up with stuck buttons upon
recovery from ANRs.

Added more comments to describe the tricky parts of PollLoop.

Change-Id: I13dffca27acb436fc383980db536abc4d8b9e6f1
2010-09-12 16:52:03 -07:00

3412 lines
119 KiB
C++

//
// Copyright 2010 The Android Open Source Project
//
// The input reader.
//
#define LOG_TAG "InputReader"
//#define LOG_NDEBUG 0
// Log debug messages for each raw event received from the EventHub.
#define DEBUG_RAW_EVENTS 0
// Log debug messages about touch screen filtering hacks.
#define DEBUG_HACKS 0
// Log debug messages about virtual key processing.
#define DEBUG_VIRTUAL_KEYS 0
// Log debug messages about pointers.
#define DEBUG_POINTERS 0
// Log debug messages about pointer assignment calculations.
#define DEBUG_POINTER_ASSIGNMENT 0
#include <cutils/log.h>
#include <ui/InputReader.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <math.h>
#define INDENT " "
namespace android {
// --- Static Functions ---
template<typename T>
inline static T abs(const T& value) {
return value < 0 ? - value : value;
}
template<typename T>
inline static T min(const T& a, const T& b) {
return a < b ? a : b;
}
template<typename T>
inline static void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
inline static float avg(float x, float y) {
return (x + y) / 2;
}
inline static float pythag(float x, float y) {
return sqrtf(x * x + y * y);
}
int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
int32_t mask;
switch (keyCode) {
case AKEYCODE_ALT_LEFT:
mask = AMETA_ALT_LEFT_ON;
break;
case AKEYCODE_ALT_RIGHT:
mask = AMETA_ALT_RIGHT_ON;
break;
case AKEYCODE_SHIFT_LEFT:
mask = AMETA_SHIFT_LEFT_ON;
break;
case AKEYCODE_SHIFT_RIGHT:
mask = AMETA_SHIFT_RIGHT_ON;
break;
case AKEYCODE_SYM:
mask = AMETA_SYM_ON;
break;
default:
return oldMetaState;
}
int32_t newMetaState = down ? oldMetaState | mask : oldMetaState & ~ mask
& ~ (AMETA_ALT_ON | AMETA_SHIFT_ON);
if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
newMetaState |= AMETA_ALT_ON;
}
if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
newMetaState |= AMETA_SHIFT_ON;
}
return newMetaState;
}
static const int32_t keyCodeRotationMap[][4] = {
// key codes enumerated counter-clockwise with the original (unrotated) key first
// no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
{ AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT },
{ AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN },
{ AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT },
{ AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP },
};
static const int keyCodeRotationMapSize =
sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
if (orientation != InputReaderPolicyInterface::ROTATION_0) {
for (int i = 0; i < keyCodeRotationMapSize; i++) {
if (keyCode == keyCodeRotationMap[i][0]) {
return keyCodeRotationMap[i][orientation];
}
}
}
return keyCode;
}
static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0;
}
// --- InputDeviceCalibration ---
InputDeviceCalibration::InputDeviceCalibration() {
}
void InputDeviceCalibration::clear() {
mProperties.clear();
}
void InputDeviceCalibration::addProperty(const String8& key, const String8& value) {
mProperties.add(key, value);
}
bool InputDeviceCalibration::tryGetProperty(const String8& key, String8& outValue) const {
ssize_t index = mProperties.indexOfKey(key);
if (index < 0) {
return false;
}
outValue = mProperties.valueAt(index);
return true;
}
bool InputDeviceCalibration::tryGetProperty(const String8& key, int32_t& outValue) const {
String8 stringValue;
if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
return false;
}
char* end;
int value = strtol(stringValue.string(), & end, 10);
if (*end != '\0') {
LOGW("Input device calibration key '%s' has invalid value '%s'. Expected an integer.",
key.string(), stringValue.string());
return false;
}
outValue = value;
return true;
}
bool InputDeviceCalibration::tryGetProperty(const String8& key, float& outValue) const {
String8 stringValue;
if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
return false;
}
char* end;
float value = strtof(stringValue.string(), & end);
if (*end != '\0') {
LOGW("Input device calibration key '%s' has invalid value '%s'. Expected a float.",
key.string(), stringValue.string());
return false;
}
outValue = value;
return true;
}
// --- InputReader ---
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputDispatcherInterface>& dispatcher) :
mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
mGlobalMetaState(0) {
configureExcludedDevices();
updateGlobalMetaState();
updateInputConfiguration();
}
InputReader::~InputReader() {
for (size_t i = 0; i < mDevices.size(); i++) {
delete mDevices.valueAt(i);
}
}
void InputReader::loopOnce() {
RawEvent rawEvent;
mEventHub->getEvent(& rawEvent);
#if DEBUG_RAW_EVENTS
LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d",
rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode,
rawEvent.value);
#endif
process(& rawEvent);
}
void InputReader::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
addDevice(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
removeDevice(rawEvent->when, rawEvent->deviceId);
break;
default:
consumeEvent(rawEvent);
break;
}
}
void InputReader::addDevice(nsecs_t when, int32_t deviceId) {
String8 name = mEventHub->getDeviceName(deviceId);
uint32_t classes = mEventHub->getDeviceClasses(deviceId);
// Write a log message about the added device as a heading for subsequent log messages.
LOGI("Device added: id=0x%x, name=%s", deviceId, name.string());
InputDevice* device = createDevice(deviceId, name, classes);
device->configure();
if (device->isIgnored()) {
LOGI(INDENT "Sources: none (device is ignored)");
} else {
LOGI(INDENT "Sources: 0x%08x", device->getSources());
}
bool added = false;
{ // acquire device registry writer lock
RWLock::AutoWLock _wl(mDeviceRegistryLock);
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;
}
handleConfigurationChanged(when);
}
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;
}
// Write a log message about the removed device as a heading for subsequent log messages.
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());
}
device->reset();
delete device;
handleConfigurationChanged(when);
}
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));
}
// 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::consumeEvent(const RawEvent* rawEvent) {
int32_t deviceId = rawEvent->deviceId;
{ // acquire device registry reader lock
RWLock::AutoRLock _rl(mDeviceRegistryLock);
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);
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex < 0) {
return NAME_NOT_FOUND;
}
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());
}
}
} // 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);
}
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::dump(String8& dump) {
dumpDeviceInfo(dump);
}
static void dumpMotionRange(String8& dump,
const char* name, const InputDeviceInfo::MotionRange* range) {
if (range) {
dump.appendFormat(" %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
name, range->min, range->max, range->flat, range->fuzz);
}
}
#define DUMP_MOTION_RANGE(range) \
dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
void InputReader::dumpDeviceInfo(String8& dump) {
Vector<int32_t> deviceIds;
getInputDeviceIds(deviceIds);
InputDeviceInfo deviceInfo;
for (size_t i = 0; i < deviceIds.size(); i++) {
int32_t deviceId = deviceIds[i];
status_t result = getInputDeviceInfo(deviceId, & deviceInfo);
if (result == NAME_NOT_FOUND) {
continue;
} else if (result != OK) {
dump.appendFormat(" ** Unexpected error %d getting information about input devices.\n",
result);
continue;
}
dump.appendFormat(" Device %d: '%s'\n",
deviceInfo.getId(), deviceInfo.getName().string());
dump.appendFormat(" sources = 0x%08x\n",
deviceInfo.getSources());
dump.appendFormat(" keyboardType = %d\n",
deviceInfo.getKeyboardType());
dump.append(" motion ranges:\n");
DUMP_MOTION_RANGE(X);
DUMP_MOTION_RANGE(Y);
DUMP_MOTION_RANGE(PRESSURE);
DUMP_MOTION_RANGE(SIZE);
DUMP_MOTION_RANGE(TOUCH_MAJOR);
DUMP_MOTION_RANGE(TOUCH_MINOR);
DUMP_MOTION_RANGE(TOOL_MAJOR);
DUMP_MOTION_RANGE(TOOL_MINOR);
DUMP_MOTION_RANGE(ORIENTATION);
}
}
#undef DUMP_MOTION_RANGE
// --- InputReaderThread ---
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();
}
void InputDevice::addMapper(InputMapper* mapper) {
mMappers.add(mapper);
}
void InputDevice::configure() {
if (! isIgnored()) {
mContext->getPolicy()->getInputDeviceCalibration(mName, mCalibration);
}
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();
}
}
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 InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
outDeviceInfo->initialize(mId, mName);
size_t numMappers = mMappers.size();
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->populateDeviceInfo(outDeviceInfo);
}
}
int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState);
}
int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
return getState(sourceMask, scanCode, & InputMapper::getScanCodeState);
}
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;
}
}
}
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) {
return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
}
// --- SwitchInputMapper ---
SwitchInputMapper::SwitchInputMapper(InputDevice* device) :
InputMapper(device) {
}
SwitchInputMapper::~SwitchInputMapper() {
}
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;
}
}
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);
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) {
initializeLocked();
}
KeyboardInputMapper::~KeyboardInputMapper() {
}
void KeyboardInputMapper::initializeLocked() {
mLocked.metaState = AMETA_NONE;
mLocked.downTime = 0;
}
uint32_t KeyboardInputMapper::getSources() {
return mSources;
}
void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
info->setKeyboardType(mKeyboardType);
}
void KeyboardInputMapper::reset() {
for (;;) {
int32_t keyCode, scanCode;
{ // acquire lock
AutoMutex _l(mLock);
// Synthesize key up event on reset if keys are currently down.
if (mLocked.keyDowns.isEmpty()) {
initializeLocked();
break; // done
}
const KeyDown& keyDown = mLocked.keyDowns.top();
keyCode = keyDown.keyCode;
scanCode = keyDown.scanCode;
} // release lock
nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
processKey(when, false, keyCode, scanCode, 0);
}
InputMapper::reset();
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;
}
}
}
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) {
int32_t newMetaState;
nsecs_t downTime;
bool metaStateChanged = false;
{ // acquire lock
AutoMutex _l(mLock);
if (down) {
// Rotate key codes according to orientation if needed.
// Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
if (mAssociatedDisplayId >= 0) {
int32_t orientation;
if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
return;
}
keyCode = rotateKeyCode(keyCode, orientation);
}
// Add key down.
ssize_t keyDownIndex = findKeyDownLocked(scanCode);
if (keyDownIndex >= 0) {
// key repeat, be sure to use same keycode as before in case of rotation
keyCode = mLocked.keyDowns.top().keyCode;
} else {
// key down
mLocked.keyDowns.push();
KeyDown& keyDown = mLocked.keyDowns.editTop();
keyDown.keyCode = keyCode;
keyDown.scanCode = scanCode;
}
mLocked.downTime = when;
} else {
// Remove key down.
ssize_t keyDownIndex = findKeyDownLocked(scanCode);
if (keyDownIndex >= 0) {
// key up, be sure to use same keycode as before in case of rotation
keyCode = mLocked.keyDowns.top().keyCode;
mLocked.keyDowns.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;
}
}
int32_t oldMetaState = mLocked.metaState;
newMetaState = updateMetaState(keyCode, down, oldMetaState);
if (oldMetaState != newMetaState) {
mLocked.metaState = newMetaState;
metaStateChanged = true;
}
downTime = mLocked.downTime;
} // release lock
if (metaStateChanged) {
getContext()->updateGlobalMetaState();
}
applyPolicyAndDispatch(when, policyFlags, down, keyCode, scanCode, newMetaState, downTime);
}
void KeyboardInputMapper::applyPolicyAndDispatch(nsecs_t when, uint32_t policyFlags, bool down,
int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {
int32_t policyActions = getPolicy()->interceptKey(when,
getDeviceId(), down, keyCode, scanCode, policyFlags);
if (! applyStandardPolicyActions(when, policyActions)) {
return; // event dropped
}
int32_t keyEventAction = down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP;
int32_t keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM;
if (policyFlags & POLICY_FLAG_WOKE_HERE) {
keyEventFlags = keyEventFlags | AKEY_EVENT_FLAG_WOKE_HERE;
}
getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
}
ssize_t KeyboardInputMapper::findKeyDownLocked(int32_t scanCode) {
size_t n = mLocked.keyDowns.size();
for (size_t i = 0; i < n; i++) {
if (mLocked.keyDowns[i].scanCode == scanCode) {
return i;
}
}
return -1;
}
int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
return getEventHub()->getKeyCodeState(getDeviceId(), keyCode);
}
int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
}
bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) {
return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags);
}
int32_t KeyboardInputMapper::getMetaState() {
{ // acquire lock
AutoMutex _l(mLock);
return mLocked.metaState;
} // release lock
}
// --- 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;
initializeLocked();
}
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::initializeLocked() {
mAccumulator.clear();
mLocked.down = false;
mLocked.downTime = 0;
}
void TrackballInputMapper::reset() {
for (;;) {
{ // acquire lock
AutoMutex _l(mLock);
if (! mLocked.down) {
initializeLocked();
break; // done
}
} // release lock
// Synthesize trackball button up event on reset.
nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
mAccumulator.fields = Accumulator::FIELD_BTN_MOUSE;
mAccumulator.btnMouse = false;
sync(when);
}
InputMapper::reset();
}
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;
// Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and
// we need to ensure that we report the up/down promptly.
sync(rawEvent->when);
break;
}
break;
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;
case EV_SYN:
switch (rawEvent->scanCode) {
case SYN_REPORT:
sync(rawEvent->when);
break;
}
break;
}
}
void TrackballInputMapper::sync(nsecs_t when) {
uint32_t fields = mAccumulator.fields;
if (fields == 0) {
return; // no new state changes, so nothing to do
}
int motionEventAction;
PointerCoords pointerCoords;
nsecs_t downTime;
{ // acquire lock
AutoMutex _l(mLock);
bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE;
if (downChanged) {
if (mAccumulator.btnMouse) {
mLocked.down = true;
mLocked.downTime = when;
} else {
mLocked.down = false;
}
}
downTime = mLocked.downTime;
float x = fields & Accumulator::FIELD_REL_X ? mAccumulator.relX * mXScale : 0.0f;
float y = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY * mYScale : 0.0f;
if (downChanged) {
motionEventAction = mLocked.down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
} else {
motionEventAction = AMOTION_EVENT_ACTION_MOVE;
}
pointerCoords.x = x;
pointerCoords.y = y;
pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f;
pointerCoords.size = 0;
pointerCoords.touchMajor = 0;
pointerCoords.touchMinor = 0;
pointerCoords.toolMajor = 0;
pointerCoords.toolMinor = 0;
pointerCoords.orientation = 0;
if (mAssociatedDisplayId >= 0 && (x != 0.0f || y != 0.0f)) {
// Rotate motion based on display orientation if needed.
// Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
int32_t orientation;
if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
return;
}
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;
}
}
} // release lock
applyPolicyAndDispatch(when, motionEventAction, & pointerCoords, downTime);
mAccumulator.clear();
}
void TrackballInputMapper::applyPolicyAndDispatch(nsecs_t when, int32_t motionEventAction,
PointerCoords* pointerCoords, nsecs_t downTime) {
uint32_t policyFlags = 0;
int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags);
if (! applyStandardPolicyActions(when, policyActions)) {
return; // event dropped
}
int32_t metaState = mContext->getGlobalMetaState();
int32_t pointerId = 0;
getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, policyFlags,
motionEventAction, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
1, & pointerId, pointerCoords, mXPrecision, mYPrecision, downTime);
}
int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
} else {
return AKEY_STATE_UNKNOWN;
}
}
// --- TouchInputMapper ---
TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
InputMapper(device), mAssociatedDisplayId(associatedDisplayId) {
mLocked.surfaceOrientation = -1;
mLocked.surfaceWidth = -1;
mLocked.surfaceHeight = -1;
initializeLocked();
}
TouchInputMapper::~TouchInputMapper() {
}
uint32_t TouchInputMapper::getSources() {
return mAssociatedDisplayId >= 0 ? AINPUT_SOURCE_TOUCHSCREEN : AINPUT_SOURCE_TOUCHPAD;
}
void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
{ // acquire lock
AutoMutex _l(mLock);
// Ensure surface information is up to date so that orientation changes are
// noticed immediately.
configureSurfaceLocked();
info->addMotionRange(AINPUT_MOTION_RANGE_X, mLocked.orientedRanges.x);
info->addMotionRange(AINPUT_MOTION_RANGE_Y, mLocked.orientedRanges.y);
if (mLocked.orientedRanges.havePressure) {
info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE,
mLocked.orientedRanges.pressure);
}
if (mLocked.orientedRanges.haveSize) {
info->addMotionRange(AINPUT_MOTION_RANGE_SIZE,
mLocked.orientedRanges.size);
}
if (mLocked.orientedRanges.haveTouchArea) {
info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR,
mLocked.orientedRanges.touchMajor);
info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR,
mLocked.orientedRanges.touchMinor);
}
if (mLocked.orientedRanges.haveToolArea) {
info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR,
mLocked.orientedRanges.toolMajor);
info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR,
mLocked.orientedRanges.toolMinor);
}
if (mLocked.orientedRanges.haveOrientation) {
info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION,
mLocked.orientedRanges.orientation);
}
} // release lock
}
void TouchInputMapper::initializeLocked() {
mCurrentTouch.clear();
mLastTouch.clear();
mDownTime = 0;
for (uint32_t i = 0; i < MAX_POINTERS; i++) {
mAveragingTouchFilter.historyStart[i] = 0;
mAveragingTouchFilter.historyEnd[i] = 0;
}
mJumpyTouchFilter.jumpyPointsDropped = 0;
mLocked.currentVirtualKey.down = false;
mLocked.orientedRanges.havePressure = false;
mLocked.orientedRanges.haveSize = false;
mLocked.orientedRanges.haveTouchArea = false;
mLocked.orientedRanges.haveToolArea = false;
mLocked.orientedRanges.haveOrientation = false;
}
void TouchInputMapper::configure() {
InputMapper::configure();
// Configure basic parameters.
configureParameters();
logParameters();
// Configure absolute axis information.
configureRawAxes();
logRawAxes();
// Prepare input device calibration.
parseCalibration();
resolveCalibration();
logCalibration();
{ // acquire lock
AutoMutex _l(mLock);
// Configure surface dimensions and orientation.
configureSurfaceLocked();
} // release lock
}
void TouchInputMapper::configureParameters() {
mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents();
mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents();
mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
}
void TouchInputMapper::logParameters() {
if (mParameters.useBadTouchFilter) {
LOGI(INDENT "Bad touch filter enabled.");
}
if (mParameters.useAveragingTouchFilter) {
LOGI(INDENT "Averaging touch filter enabled.");
}
if (mParameters.useJumpyTouchFilter) {
LOGI(INDENT "Jumpy touch filter enabled.");
}
}
void TouchInputMapper::configureRawAxes() {
mRawAxes.x.clear();
mRawAxes.y.clear();
mRawAxes.pressure.clear();
mRawAxes.touchMajor.clear();
mRawAxes.touchMinor.clear();
mRawAxes.toolMajor.clear();
mRawAxes.toolMinor.clear();
mRawAxes.orientation.clear();
}
static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) {
if (axis.valid) {
LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d",
name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
} else {
LOGI(INDENT "Raw %s axis: unknown range", name);
}
}
void TouchInputMapper::logRawAxes() {
logAxisInfo(mRawAxes.x, "x");
logAxisInfo(mRawAxes.y, "y");
logAxisInfo(mRawAxes.pressure, "pressure");
logAxisInfo(mRawAxes.touchMajor, "touchMajor");
logAxisInfo(mRawAxes.touchMinor, "touchMinor");
logAxisInfo(mRawAxes.toolMajor, "toolMajor");
logAxisInfo(mRawAxes.toolMinor, "toolMinor");
logAxisInfo(mRawAxes.orientation, "orientation");
}
bool TouchInputMapper::configureSurfaceLocked() {
// Update orientation and dimensions if needed.
int32_t orientation;
int32_t width, height;
if (mAssociatedDisplayId >= 0) {
// Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) {
return false;
}
} else {
orientation = InputReaderPolicyInterface::ROTATION_0;
width = mRawAxes.x.getRange();
height = mRawAxes.y.getRange();
}
bool orientationChanged = mLocked.surfaceOrientation != orientation;
if (orientationChanged) {
mLocked.surfaceOrientation = orientation;
}
bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
if (sizeChanged) {
LOGI("Device reconfigured (display size changed): id=0x%x, name=%s",
getDeviceId(), getDeviceName().string());
LOGI(INDENT "Width: %dpx", width);
LOGI(INDENT "Height: %dpx", height);
mLocked.surfaceWidth = width;
mLocked.surfaceHeight = height;
// Configure X and Y factors.
if (mRawAxes.x.valid && mRawAxes.y.valid) {
mLocked.xOrigin = mRawAxes.x.minValue;
mLocked.yOrigin = mRawAxes.y.minValue;
mLocked.xScale = float(width) / mRawAxes.x.getRange();
mLocked.yScale = float(height) / mRawAxes.y.getRange();
mLocked.xPrecision = 1.0f / mLocked.xScale;
mLocked.yPrecision = 1.0f / mLocked.yScale;
configureVirtualKeysLocked();
} else {
LOGW(INDENT "Touch device did not report support for X or Y axis!");
mLocked.xOrigin = 0;
mLocked.yOrigin = 0;
mLocked.xScale = 1.0f;
mLocked.yScale = 1.0f;
mLocked.xPrecision = 1.0f;
mLocked.yPrecision = 1.0f;
}
// Scale factor for terms that are not oriented in a particular axis.
// If the pixels are square then xScale == yScale otherwise we fake it
// by choosing an average.
mLocked.geometricScale = avg(mLocked.xScale, mLocked.yScale);
// Size of diagonal axis.
float diagonalSize = pythag(width, height);
// TouchMajor and TouchMinor factors.
if (mCalibration.touchAreaCalibration != Calibration::TOUCH_AREA_CALIBRATION_NONE) {
mLocked.orientedRanges.haveTouchArea = true;
mLocked.orientedRanges.touchMajor.min = 0;
mLocked.orientedRanges.touchMajor.max = diagonalSize;
mLocked.orientedRanges.touchMajor.flat = 0;
mLocked.orientedRanges.touchMajor.fuzz = 0;
mLocked.orientedRanges.touchMinor = mLocked.orientedRanges.touchMajor;
}
// ToolMajor and ToolMinor factors.
if (mCalibration.toolAreaCalibration != Calibration::TOOL_AREA_CALIBRATION_NONE) {
mLocked.toolAreaLinearScale = 0;
mLocked.toolAreaLinearBias = 0;
if (mCalibration.toolAreaCalibration == Calibration::TOOL_AREA_CALIBRATION_LINEAR) {
if (mCalibration.haveToolAreaLinearScale) {
mLocked.toolAreaLinearScale = mCalibration.toolAreaLinearScale;
} else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
mLocked.toolAreaLinearScale = float(min(width, height))
/ mRawAxes.toolMajor.maxValue;
}
if (mCalibration.haveToolAreaLinearBias) {
mLocked.toolAreaLinearBias = mCalibration.toolAreaLinearBias;
}
}
mLocked.orientedRanges.haveToolArea = true;
mLocked.orientedRanges.toolMajor.min = 0;
mLocked.orientedRanges.toolMajor.max = diagonalSize;
mLocked.orientedRanges.toolMajor.flat = 0;
mLocked.orientedRanges.toolMajor.fuzz = 0;
mLocked.orientedRanges.toolMinor = mLocked.orientedRanges.toolMajor;
}
// Pressure factors.
if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE) {
RawAbsoluteAxisInfo rawPressureAxis;
switch (mCalibration.pressureSource) {
case Calibration::PRESSURE_SOURCE_PRESSURE:
rawPressureAxis = mRawAxes.pressure;
break;
case Calibration::PRESSURE_SOURCE_TOUCH:
rawPressureAxis = mRawAxes.touchMajor;
break;
default:
rawPressureAxis.clear();
}
mLocked.pressureScale = 0;
if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL
|| mCalibration.pressureCalibration
== Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
if (mCalibration.havePressureScale) {
mLocked.pressureScale = mCalibration.pressureScale;
} else if (rawPressureAxis.valid && rawPressureAxis.maxValue != 0) {
mLocked.pressureScale = 1.0f / rawPressureAxis.maxValue;
}
}
mLocked.orientedRanges.havePressure = true;
mLocked.orientedRanges.pressure.min = 0;
mLocked.orientedRanges.pressure.max = 1.0;
mLocked.orientedRanges.pressure.flat = 0;
mLocked.orientedRanges.pressure.fuzz = 0;
}
// Size factors.
if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
mLocked.sizeScale = 0;
if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_NORMALIZED) {
if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
mLocked.sizeScale = 1.0f / mRawAxes.toolMajor.maxValue;
}
}
mLocked.orientedRanges.haveSize = true;
mLocked.orientedRanges.size.min = 0;
mLocked.orientedRanges.size.max = 1.0;
mLocked.orientedRanges.size.flat = 0;
mLocked.orientedRanges.size.fuzz = 0;
}
// Orientation
if (mCalibration.orientationCalibration != Calibration::ORIENTATION_CALIBRATION_NONE) {
mLocked.orientationScale = 0;
if (mCalibration.orientationCalibration
== Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
if (mRawAxes.orientation.valid && mRawAxes.orientation.maxValue != 0) {
mLocked.orientationScale = float(M_PI_2) / mRawAxes.orientation.maxValue;
}
}
mLocked.orientedRanges.orientation.min = - M_PI_2;
mLocked.orientedRanges.orientation.max = M_PI_2;
mLocked.orientedRanges.orientation.flat = 0;
mLocked.orientedRanges.orientation.fuzz = 0;
}
}
if (orientationChanged || sizeChanged) {
// Compute oriented surface dimensions, precision, and scales.
float orientedXScale, orientedYScale;
switch (mLocked.surfaceOrientation) {
case InputReaderPolicyInterface::ROTATION_90:
case InputReaderPolicyInterface::ROTATION_270:
mLocked.orientedSurfaceWidth = mLocked.surfaceHeight;
mLocked.orientedSurfaceHeight = mLocked.surfaceWidth;
mLocked.orientedXPrecision = mLocked.yPrecision;
mLocked.orientedYPrecision = mLocked.xPrecision;
orientedXScale = mLocked.yScale;
orientedYScale = mLocked.xScale;
break;
default:
mLocked.orientedSurfaceWidth = mLocked.surfaceWidth;
mLocked.orientedSurfaceHeight = mLocked.surfaceHeight;
mLocked.orientedXPrecision = mLocked.xPrecision;
mLocked.orientedYPrecision = mLocked.yPrecision;
orientedXScale = mLocked.xScale;
orientedYScale = mLocked.yScale;
break;
}
// Configure position ranges.
mLocked.orientedRanges.x.min = 0;
mLocked.orientedRanges.x.max = mLocked.orientedSurfaceWidth;
mLocked.orientedRanges.x.flat = 0;
mLocked.orientedRanges.x.fuzz = orientedXScale;
mLocked.orientedRanges.y.min = 0;
mLocked.orientedRanges.y.max = mLocked.orientedSurfaceHeight;
mLocked.orientedRanges.y.flat = 0;
mLocked.orientedRanges.y.fuzz = orientedYScale;
}
if (sizeChanged) {
logMotionRangesLocked();
}
return true;
}
static void logMotionRangeInfo(InputDeviceInfo::MotionRange* range, const char* name) {
if (range) {
LOGI(INDENT "Output %s range: min=%f, max=%f, flat=%f, fuzz=%f",
name, range->min, range->max, range->flat, range->fuzz);
} else {
LOGI(INDENT "Output %s range: unsupported", name);
}
}
void TouchInputMapper::logMotionRangesLocked() {
logMotionRangeInfo(& mLocked.orientedRanges.x, "x");
logMotionRangeInfo(& mLocked.orientedRanges.y, "y");
logMotionRangeInfo(mLocked.orientedRanges.havePressure
? & mLocked.orientedRanges.pressure : NULL, "pressure");
logMotionRangeInfo(mLocked.orientedRanges.haveSize
? & mLocked.orientedRanges.size : NULL, "size");
logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
? & mLocked.orientedRanges.touchMajor : NULL, "touchMajor");
logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
? & mLocked.orientedRanges.touchMinor : NULL, "touchMinor");
logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
? & mLocked.orientedRanges.toolMajor : NULL, "toolMajor");
logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
? & mLocked.orientedRanges.toolMinor : NULL, "toolMinor");
logMotionRangeInfo(mLocked.orientedRanges.haveOrientation
? & mLocked.orientedRanges.orientation : NULL, "orientation");
}
void TouchInputMapper::configureVirtualKeysLocked() {
assert(mRawAxes.x.valid && mRawAxes.y.valid);
// Note: getVirtualKeyDefinitions is non-reentrant so we can continue holding the lock.
Vector<VirtualKeyDefinition> virtualKeyDefinitions;
getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions);
mLocked.virtualKeys.clear();
if (virtualKeyDefinitions.size() == 0) {
return;
}
mLocked.virtualKeys.setCapacity(virtualKeyDefinitions.size());
int32_t touchScreenLeft = mRawAxes.x.minValue;
int32_t touchScreenTop = mRawAxes.y.minValue;
int32_t touchScreenWidth = mRawAxes.x.getRange();
int32_t touchScreenHeight = mRawAxes.y.getRange();
for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
const VirtualKeyDefinition& virtualKeyDefinition =
virtualKeyDefinitions[i];
mLocked.virtualKeys.add();
VirtualKey& virtualKey = mLocked.virtualKeys.editTop();
virtualKey.scanCode = virtualKeyDefinition.scanCode;
int32_t keyCode;
uint32_t flags;
if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode,
& keyCode, & flags)) {
LOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring",
virtualKey.scanCode);
mLocked.virtualKeys.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 / mLocked.surfaceWidth + touchScreenLeft;
virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
* touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft;
virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
* touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
* touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
LOGI(INDENT "VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d",
virtualKey.scanCode, virtualKey.keyCode,
virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom);
}
}
void TouchInputMapper::parseCalibration() {
const InputDeviceCalibration& in = getDevice()->getCalibration();
Calibration& out = mCalibration;
// Touch Area
out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_DEFAULT;
String8 touchAreaCalibrationString;
if (in.tryGetProperty(String8("touch.touchArea.calibration"), touchAreaCalibrationString)) {
if (touchAreaCalibrationString == "none") {
out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_NONE;
} else if (touchAreaCalibrationString == "geometric") {
out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC;
} else if (touchAreaCalibrationString == "pressure") {
out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_PRESSURE;
} else if (touchAreaCalibrationString != "default") {
LOGW("Invalid value for touch.touchArea.calibration: '%s'",
touchAreaCalibrationString.string());
}
}
// Tool Area
out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_DEFAULT;
String8 toolAreaCalibrationString;
if (in.tryGetProperty(String8("tool.toolArea.calibration"), toolAreaCalibrationString)) {
if (toolAreaCalibrationString == "none") {
out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_NONE;
} else if (toolAreaCalibrationString == "geometric") {
out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC;
} else if (toolAreaCalibrationString == "linear") {
out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_LINEAR;
} else if (toolAreaCalibrationString != "default") {
LOGW("Invalid value for tool.toolArea.calibration: '%s'",
toolAreaCalibrationString.string());
}
}
out.haveToolAreaLinearScale = in.tryGetProperty(String8("touch.toolArea.linearScale"),
out.toolAreaLinearScale);
out.haveToolAreaLinearBias = in.tryGetProperty(String8("touch.toolArea.linearBias"),
out.toolAreaLinearBias);
out.haveToolAreaIsSummed = in.tryGetProperty(String8("touch.toolArea.isSummed"),
out.toolAreaIsSummed);
// Pressure
out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
String8 pressureCalibrationString;
if (in.tryGetProperty(String8("tool.pressure.calibration"), pressureCalibrationString)) {
if (pressureCalibrationString == "none") {
out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
} else if (pressureCalibrationString == "physical") {
out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
} else if (pressureCalibrationString == "amplitude") {
out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
} else if (pressureCalibrationString != "default") {
LOGW("Invalid value for tool.pressure.calibration: '%s'",
pressureCalibrationString.string());
}
}
out.pressureSource = Calibration::PRESSURE_SOURCE_DEFAULT;
String8 pressureSourceString;
if (in.tryGetProperty(String8("touch.pressure.source"), pressureSourceString)) {
if (pressureSourceString == "pressure") {
out.pressureSource = Calibration::PRESSURE_SOURCE_PRESSURE;
} else if (pressureSourceString == "touch") {
out.pressureSource = Calibration::PRESSURE_SOURCE_TOUCH;
} else if (pressureSourceString != "default") {
LOGW("Invalid value for touch.pressure.source: '%s'",
pressureSourceString.string());
}
}
out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"),
out.pressureScale);
// Size
out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
String8 sizeCalibrationString;
if (in.tryGetProperty(String8("tool.size.calibration"), sizeCalibrationString)) {
if (sizeCalibrationString == "none") {
out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
} else if (sizeCalibrationString == "normalized") {
out.sizeCalibration = Calibration::SIZE_CALIBRATION_NORMALIZED;
} else if (sizeCalibrationString != "default") {
LOGW("Invalid value for tool.size.calibration: '%s'",
sizeCalibrationString.string());
}
}
// Orientation
out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
String8 orientationCalibrationString;
if (in.tryGetProperty(String8("tool.orientation.calibration"), orientationCalibrationString)) {
if (orientationCalibrationString == "none") {
out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
} else if (orientationCalibrationString == "interpolated") {
out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
} else if (orientationCalibrationString != "default") {
LOGW("Invalid value for tool.orientation.calibration: '%s'",
orientationCalibrationString.string());
}
}
}
void TouchInputMapper::resolveCalibration() {
// Pressure
switch (mCalibration.pressureSource) {
case Calibration::PRESSURE_SOURCE_DEFAULT:
if (mRawAxes.pressure.valid) {
mCalibration.pressureSource = Calibration::PRESSURE_SOURCE_PRESSURE;
} else if (mRawAxes.touchMajor.valid) {
mCalibration.pressureSource = Calibration::PRESSURE_SOURCE_TOUCH;
}
break;
case Calibration::PRESSURE_SOURCE_PRESSURE:
if (! mRawAxes.pressure.valid) {
LOGW("Calibration property touch.pressure.source is 'pressure' but "
"the pressure axis is not available.");
}
break;
case Calibration::PRESSURE_SOURCE_TOUCH:
if (! mRawAxes.touchMajor.valid) {
LOGW("Calibration property touch.pressure.source is 'touch' but "
"the touchMajor axis is not available.");
}
break;
default:
break;
}
switch (mCalibration.pressureCalibration) {
case Calibration::PRESSURE_CALIBRATION_DEFAULT:
if (mCalibration.pressureSource != Calibration::PRESSURE_SOURCE_DEFAULT) {
mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
} else {
mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
}
break;
default:
break;
}
// Tool Area
switch (mCalibration.toolAreaCalibration) {
case Calibration::TOOL_AREA_CALIBRATION_DEFAULT:
if (mRawAxes.toolMajor.valid) {
mCalibration.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_LINEAR;
} else {
mCalibration.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_NONE;
}
break;
default:
break;
}
// Touch Area
switch (mCalibration.touchAreaCalibration) {
case Calibration::TOUCH_AREA_CALIBRATION_DEFAULT:
if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE
&& mCalibration.toolAreaCalibration != Calibration::TOOL_AREA_CALIBRATION_NONE) {
mCalibration.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_PRESSURE;
} else {
mCalibration.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_NONE;
}
break;
default:
break;
}
// Size
switch (mCalibration.sizeCalibration) {
case Calibration::SIZE_CALIBRATION_DEFAULT:
if (mRawAxes.toolMajor.valid) {
mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NORMALIZED;
} else {
mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
}
break;
default:
break;
}
// Orientation
switch (mCalibration.orientationCalibration) {
case Calibration::ORIENTATION_CALIBRATION_DEFAULT:
if (mRawAxes.orientation.valid) {
mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
} else {
mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
}
break;
default:
break;
}
}
void TouchInputMapper::logCalibration() {
LOGI(INDENT "Calibration:");
// Touch Area
switch (mCalibration.touchAreaCalibration) {
case Calibration::TOUCH_AREA_CALIBRATION_NONE:
LOGI(INDENT INDENT "touch.touchArea.calibration: none");
break;
case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC:
LOGI(INDENT INDENT "touch.touchArea.calibration: geometric");
break;
case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE:
LOGI(INDENT INDENT "touch.touchArea.calibration: pressure");
break;
default:
assert(false);
}
// Tool Area
switch (mCalibration.toolAreaCalibration) {
case Calibration::TOOL_AREA_CALIBRATION_NONE:
LOGI(INDENT INDENT "touch.toolArea.calibration: none");
break;
case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC:
LOGI(INDENT INDENT "touch.toolArea.calibration: geometric");
break;
case Calibration::TOOL_AREA_CALIBRATION_LINEAR:
LOGI(INDENT INDENT "touch.toolArea.calibration: linear");
break;
default:
assert(false);
}
if (mCalibration.haveToolAreaLinearScale) {
LOGI(INDENT INDENT "touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale);
}
if (mCalibration.haveToolAreaLinearBias) {
LOGI(INDENT INDENT "touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias);
}
if (mCalibration.haveToolAreaIsSummed) {
LOGI(INDENT INDENT "touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed);
}
// Pressure
switch (mCalibration.pressureCalibration) {
case Calibration::PRESSURE_CALIBRATION_NONE:
LOGI(INDENT INDENT "touch.pressure.calibration: none");
break;
case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
LOGI(INDENT INDENT "touch.pressure.calibration: physical");
break;
case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
LOGI(INDENT INDENT "touch.pressure.calibration: amplitude");
break;
default:
assert(false);
}
switch (mCalibration.pressureSource) {
case Calibration::PRESSURE_SOURCE_PRESSURE:
LOGI(INDENT INDENT "touch.pressure.source: pressure");
break;
case Calibration::PRESSURE_SOURCE_TOUCH:
LOGI(INDENT INDENT "touch.pressure.source: touch");
break;
case Calibration::PRESSURE_SOURCE_DEFAULT:
break;
default:
assert(false);
}
if (mCalibration.havePressureScale) {
LOGI(INDENT INDENT "touch.pressure.scale: %f", mCalibration.pressureScale);
}
// Size
switch (mCalibration.sizeCalibration) {
case Calibration::SIZE_CALIBRATION_NONE:
LOGI(INDENT INDENT "touch.size.calibration: none");
break;
case Calibration::SIZE_CALIBRATION_NORMALIZED:
LOGI(INDENT INDENT "touch.size.calibration: normalized");
break;
default:
assert(false);
}
// Orientation
switch (mCalibration.orientationCalibration) {
case Calibration::ORIENTATION_CALIBRATION_NONE:
LOGI(INDENT INDENT "touch.orientation.calibration: none");
break;
case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
LOGI(INDENT INDENT "touch.orientation.calibration: interpolated");
break;
default:
assert(false);
}
}
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);
}
{ // acquire lock
AutoMutex _l(mLock);
initializeLocked();
} // release lock
InputMapper::reset();
}
void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
// Apply generic policy actions.
uint32_t policyFlags = 0;
int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags);
if (! applyStandardPolicyActions(when, policyActions)) {
mLastTouch.clear();
return; // event dropped
}
// Preprocess pointer data.
if (mParameters.useBadTouchFilter) {
if (applyBadTouchFilter()) {
havePointerIds = false;
}
}
if (mParameters.useJumpyTouchFilter) {
if (applyJumpyTouchFilter()) {
havePointerIds = false;
}
}
if (! havePointerIds) {
calculatePointerIds();
}
TouchData temp;
TouchData* savedTouch;
if (mParameters.useAveragingTouchFilter) {
temp.copyFrom(mCurrentTouch);
savedTouch = & temp;
applyAveragingTouchFilter();
} else {
savedTouch = & mCurrentTouch;
}
// Process touches and virtual keys.
TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);
if (touchResult == DISPATCH_TOUCH) {
dispatchTouches(when, policyFlags);
}
// Copy current touch to last touch in preparation for the next cycle.
if (touchResult == DROP_STROKE) {
mLastTouch.clear();
} else {
mLastTouch.copyFrom(*savedTouch);
}
}
TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches(
nsecs_t when, uint32_t policyFlags) {
int32_t keyEventAction, keyEventFlags;
int32_t keyCode, scanCode, downTime;
TouchResult touchResult;
{ // acquire lock
AutoMutex _l(mLock);
// Update surface size and orientation, including virtual key positions.
if (! configureSurfaceLocked()) {
return DROP_STROKE;
}
// Check for virtual key press.
if (mLocked.currentVirtualKey.down) {
if (mCurrentTouch.pointerCount == 0) {
// Pointer went up while virtual key was down.
mLocked.currentVirtualKey.down = false;
#if DEBUG_VIRTUAL_KEYS
LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
#endif
keyEventAction = AKEY_EVENT_ACTION_UP;
keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
touchResult = SKIP_TOUCH;
goto DispatchVirtualKey;
}
if (mCurrentTouch.pointerCount == 1) {
int32_t x = mCurrentTouch.pointers[0].x;
int32_t y = mCurrentTouch.pointers[0].y;
const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y);
if (virtualKey && virtualKey->keyCode == mLocked.currentVirtualKey.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 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.
mLocked.currentVirtualKey.down = false;
#if DEBUG_VIRTUAL_KEYS
LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
#endif
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 (! isPointInsideSurfaceLocked(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 = findVirtualKeyHitLocked(x, y);
if (virtualKey) {
mLocked.currentVirtualKey.down = true;
mLocked.currentVirtualKey.downTime = when;
mLocked.currentVirtualKey.keyCode = virtualKey->keyCode;
mLocked.currentVirtualKey.scanCode = virtualKey->scanCode;
#if DEBUG_VIRTUAL_KEYS
LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
#endif
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;
}
DispatchVirtualKey:
// Collect remaining state needed to dispatch virtual key.
keyCode = mLocked.currentVirtualKey.keyCode;
scanCode = mLocked.currentVirtualKey.scanCode;
downTime = mLocked.currentVirtualKey.downTime;
} // release lock
// Dispatch virtual key.
applyPolicyAndDispatchVirtualKey(when, policyFlags, keyEventAction, keyEventFlags,
keyCode, scanCode, downTime);
return touchResult;
}
void TouchInputMapper::applyPolicyAndDispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
int32_t keyEventAction, int32_t keyEventFlags,
int32_t keyCode, int32_t scanCode, nsecs_t downTime) {
int32_t metaState = mContext->getGlobalMetaState();
if (keyEventAction == AKEY_EVENT_ACTION_DOWN) {
getPolicy()->virtualKeyDownFeedback();
}
int32_t policyActions = getPolicy()->interceptKey(when, getDeviceId(),
keyEventAction == AKEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
if (applyStandardPolicyActions(when, policyActions)) {
getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
}
}
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 = 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, policyFlags, & mCurrentTouch,
currentIdBits, -1, currentPointerCount, motionEventAction);
} else {
// There may be pointers going up and pointers going down at the same time when pointer
// ids are reported by the device driver.
BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value);
BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value);
BitSet32 activeIdBits(lastIdBits.value);
uint32_t pointerCount = lastPointerCount;
while (! upIdBits.isEmpty()) {
uint32_t upId = upIdBits.firstMarkedBit();
upIdBits.clearBit(upId);
BitSet32 oldActiveIdBits = activeIdBits;
activeIdBits.clearBit(upId);
int32_t motionEventAction;
if (activeIdBits.isEmpty()) {
motionEventAction = AMOTION_EVENT_ACTION_UP;
} else {
motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP;
}
dispatchTouch(when, policyFlags, & mLastTouch,
oldActiveIdBits, upId, pointerCount, motionEventAction);
pointerCount -= 1;
}
while (! downIdBits.isEmpty()) {
uint32_t downId = downIdBits.firstMarkedBit();
downIdBits.clearBit(downId);
BitSet32 oldActiveIdBits = activeIdBits;
activeIdBits.markBit(downId);
int32_t motionEventAction;
if (oldActiveIdBits.isEmpty()) {
motionEventAction = AMOTION_EVENT_ACTION_DOWN;
mDownTime = when;
} else {
motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN;
}
pointerCount += 1;
dispatchTouch(when, policyFlags, & mCurrentTouch,
activeIdBits, downId, pointerCount, motionEventAction);
}
}
}
void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags,
TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount,
int32_t motionEventAction) {
int32_t pointerIds[MAX_POINTERS];
PointerCoords pointerCoords[MAX_POINTERS];
int32_t motionEventEdgeFlags = 0;
float xPrecision, yPrecision;
{ // acquire lock
AutoMutex _l(mLock);
// Walk through the the active pointers and map touch screen coordinates (TouchData) into
// display coordinates (PointerCoords) and adjust for display orientation.
for (uint32_t outIndex = 0; ! idBits.isEmpty(); outIndex++) {
uint32_t id = idBits.firstMarkedBit();
idBits.clearBit(id);
uint32_t inIndex = touch->idToIndex[id];
const PointerData& in = touch->pointers[inIndex];
// X and Y
float x = float(in.x - mLocked.xOrigin) * mLocked.xScale;
float y = float(in.y - mLocked.yOrigin) * mLocked.yScale;
// ToolMajor and ToolMinor
float toolMajor, toolMinor;
switch (mCalibration.toolAreaCalibration) {
case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC:
toolMajor = in.toolMajor * mLocked.geometricScale;
if (mRawAxes.toolMinor.valid) {
toolMinor = in.toolMinor * mLocked.geometricScale;
} else {
toolMinor = toolMajor;
}
break;
case Calibration::TOOL_AREA_CALIBRATION_LINEAR:
toolMajor = in.toolMajor != 0
? in.toolMajor * mLocked.toolAreaLinearScale + mLocked.toolAreaLinearBias
: 0;
if (mRawAxes.toolMinor.valid) {
toolMinor = in.toolMinor != 0
? in.toolMinor * mLocked.toolAreaLinearScale
+ mLocked.toolAreaLinearBias
: 0;
} else {
toolMinor = toolMajor;
}
break;
default:
toolMajor = 0;
toolMinor = 0;
break;
}
if (mCalibration.haveToolAreaIsSummed && mCalibration.toolAreaIsSummed) {
toolMajor /= pointerCount;
toolMinor /= pointerCount;
}
// Pressure
float rawPressure;
switch (mCalibration.pressureSource) {
case Calibration::PRESSURE_SOURCE_PRESSURE:
rawPressure = in.pressure;
break;
case Calibration::PRESSURE_SOURCE_TOUCH:
rawPressure = in.touchMajor;
break;
default:
rawPressure = 0;
}
float pressure;
switch (mCalibration.pressureCalibration) {
case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
pressure = rawPressure * mLocked.pressureScale;
break;
default:
pressure = 1;
break;
}
// TouchMajor and TouchMinor
float touchMajor, touchMinor;
switch (mCalibration.touchAreaCalibration) {
case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC:
touchMajor = in.touchMajor * mLocked.geometricScale;
if (mRawAxes.touchMinor.valid) {
touchMinor = in.touchMinor * mLocked.geometricScale;
} else {
touchMinor = touchMajor;
}
break;
case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE:
touchMajor = toolMajor * pressure;
touchMinor = toolMinor * pressure;
break;
default:
touchMajor = 0;
touchMinor = 0;
break;
}
if (touchMajor > toolMajor) {
touchMajor = toolMajor;
}
if (touchMinor > toolMinor) {
touchMinor = toolMinor;
}
// Size
float size;
switch (mCalibration.sizeCalibration) {
case Calibration::SIZE_CALIBRATION_NORMALIZED: {
float rawSize = mRawAxes.toolMinor.valid
? avg(in.toolMajor, in.toolMinor)
: in.toolMajor;
size = rawSize * mLocked.sizeScale;
break;
}
default:
size = 0;
break;
}
// Orientation
float orientation;
switch (mCalibration.orientationCalibration) {
case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
orientation = in.orientation * mLocked.orientationScale;
break;
default:
orientation = 0;
}
// Adjust coords for orientation.
switch (mLocked.surfaceOrientation) {
case InputReaderPolicyInterface::ROTATION_90: {
float xTemp = x;
x = y;
y = mLocked.surfaceWidth - xTemp;
orientation -= M_PI_2;
if (orientation < - M_PI_2) {
orientation += M_PI;
}
break;
}
case InputReaderPolicyInterface::ROTATION_180: {
x = mLocked.surfaceWidth - x;
y = mLocked.surfaceHeight - y;
orientation = - orientation;
break;
}
case InputReaderPolicyInterface::ROTATION_270: {
float xTemp = x;
x = mLocked.surfaceHeight - y;
y = xTemp;
orientation += M_PI_2;
if (orientation > M_PI_2) {
orientation -= M_PI;
}
break;
}
}
// Write output coords.
PointerCoords& out = pointerCoords[outIndex];
out.x = x;
out.y = y;
out.pressure = pressure;
out.size = size;
out.touchMajor = touchMajor;
out.touchMinor = touchMinor;
out.toolMajor = toolMajor;
out.toolMinor = toolMinor;
out.orientation = orientation;
pointerIds[outIndex] = int32_t(id);
if (id == changedId) {
motionEventAction |= outIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
}
// Check edge flags by looking only at the first pointer since the flags are
// global to the event.
if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) {
if (pointerCoords[0].x <= 0) {
motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT;
} else if (pointerCoords[0].x >= mLocked.orientedSurfaceWidth) {
motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT;
}
if (pointerCoords[0].y <= 0) {
motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP;
} else if (pointerCoords[0].y >= mLocked.orientedSurfaceHeight) {
motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM;
}
}
xPrecision = mLocked.orientedXPrecision;
yPrecision = mLocked.orientedYPrecision;
} // release lock
getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TOUCHSCREEN, policyFlags,
motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
pointerCount, pointerIds, pointerCoords,
xPrecision, yPrecision, mDownTime);
}
bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) {
if (mRawAxes.x.valid && mRawAxes.y.valid) {
return x >= mRawAxes.x.minValue && x <= mRawAxes.x.maxValue
&& y >= mRawAxes.y.minValue && y <= mRawAxes.y.maxValue;
}
return true;
}
const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLocked(
int32_t x, int32_t y) {
size_t numVirtualKeys = mLocked.virtualKeys.size();
for (size_t i = 0; i < numVirtualKeys; i++) {
const VirtualKey& virtualKey = mLocked.virtualKeys[i];
#if DEBUG_VIRTUAL_KEYS
LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
"left=%d, top=%d, right=%d, bottom=%d",
x, y,
virtualKey.keyCode, virtualKey.scanCode,
virtualKey.hitLeft, virtualKey.hitTop,
virtualKey.hitRight, virtualKey.hitBottom);
#endif
if (virtualKey.isHit(x, y)) {
return & virtualKey;
}
}
return NULL;
}
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 {
// 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;
}
}
// Heapify
for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
startIndex -= 1;
for (uint32_t parentIndex = startIndex; ;) {
uint32_t childIndex = parentIndex * 2 + 1;
if (childIndex >= heapSize) {
break;
}
if (childIndex + 1 < heapSize
&& heap[childIndex + 1].distance < heap[childIndex].distance) {
childIndex += 1;
}
if (heap[parentIndex].distance <= heap[childIndex].distance) {
break;
}
swap(heap[parentIndex], heap[childIndex]);
parentIndex = childIndex;
}
}
#if DEBUG_POINTER_ASSIGNMENT
LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize);
for (size_t i = 0; i < heapSize; i++) {
LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
heap[i].distance);
}
#endif
// Pull matches out by increasing order of distance.
// To avoid reassigning pointers that have already been matched, the loop keeps track
// of which last and current pointers have been matched using the matchedXXXBits variables.
// It also tracks the used pointer id bits.
BitSet32 matchedLastBits(0);
BitSet32 matchedCurrentBits(0);
BitSet32 usedIdBits(0);
bool first = true;
for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) {
for (;;) {
if (first) {
// The first time through the loop, we just consume the root element of
// the heap (the one with smallest distance).
first = false;
} else {
// Previous iterations consumed the root element of the heap.
// Pop root element off of the heap (sift down).
heapSize -= 1;
assert(heapSize > 0);
// Sift down.
heap[0] = heap[heapSize];
for (uint32_t parentIndex = 0; ;) {
uint32_t childIndex = parentIndex * 2 + 1;
if (childIndex >= heapSize) {
break;
}
if (childIndex + 1 < heapSize
&& heap[childIndex + 1].distance < heap[childIndex].distance) {
childIndex += 1;
}
if (heap[parentIndex].distance <= heap[childIndex].distance) {
break;
}
swap(heap[parentIndex], heap[childIndex]);
parentIndex = childIndex;
}
#if DEBUG_POINTER_ASSIGNMENT
LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize);
for (size_t i = 0; i < heapSize; i++) {
LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
heap[i].distance);
}
#endif
}
uint32_t currentPointerIndex = heap[0].currentPointerIndex;
if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
uint32_t lastPointerIndex = heap[0].lastPointerIndex;
if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
matchedCurrentBits.markBit(currentPointerIndex);
matchedLastBits.markBit(lastPointerIndex);
uint32_t id = mLastTouch.pointers[lastPointerIndex].id;
mCurrentTouch.pointers[currentPointerIndex].id = id;
mCurrentTouch.idToIndex[id] = currentPointerIndex;
usedIdBits.markBit(id);
#if DEBUG_POINTER_ASSIGNMENT
LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
lastPointerIndex, currentPointerIndex, id, heap[0].distance);
#endif
break;
}
}
// Assign fresh ids to new pointers.
if (currentPointerCount > lastPointerCount) {
for (uint32_t i = currentPointerCount - lastPointerCount; ;) {
uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit();
uint32_t id = usedIdBits.firstUnmarkedBit();
mCurrentTouch.pointers[currentPointerIndex].id = id;
mCurrentTouch.idToIndex[id] = currentPointerIndex;
usedIdBits.markBit(id);
#if DEBUG_POINTER_ASSIGNMENT
LOGD("calculatePointerIds - assigned: cur=%d, id=%d",
currentPointerIndex, id);
#endif
if (--i == 0) break; // done
matchedCurrentBits.markBit(currentPointerIndex);
}
}
// Fix id bits.
mCurrentTouch.idBits = usedIdBits;
}
}
/* Special hack for devices that have bad screen data: if one of the
* points has moved more than a screen height from the last position,
* then drop it. */
bool TouchInputMapper::applyBadTouchFilter() {
// This hack requires valid axis parameters.
if (! mRawAxes.y.valid) {
return false;
}
uint32_t pointerCount = mCurrentTouch.pointerCount;
// Nothing to do if there are no points.
if (pointerCount == 0) {
return false;
}
// Don't do anything if a finger is going down or up. We run
// here before assigning pointer IDs, so there isn't a good
// way to do per-finger matching.
if (pointerCount != mLastTouch.pointerCount) {
return false;
}
// We consider a single movement across more than a 7/16 of
// the long size of the screen to be bad. This was a magic value
// determined by looking at the maximum distance it is feasible
// to actually move in one sample.
int32_t maxDeltaY = mRawAxes.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
for (uint32_t j = pointerCount; j-- > 0; ) {
int32_t lastY = mLastTouch.pointers[j].y;
int32_t deltaY = abs(y - lastY);
#if DEBUG_HACKS
LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d",
j, lastY, deltaY);
#endif
if (deltaY < maxDeltaY) {
goto SkipSufficientlyClosePoint;
}
if (deltaY < closestDeltaY) {
closestDeltaY = deltaY;
closestY = lastY;
}
}
// Must not have found a close enough match.
#if DEBUG_HACKS
LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d",
i, y, closestY, closestDeltaY, maxDeltaY);
#endif
mCurrentTouch.pointers[i].y = closestY;
return true; // XXX original code only corrects one point
SkipSufficientlyClosePoint: ;
}
// No change.
return false;
}
/* Special hack for devices that have bad screen data: drop points where
* the coordinate value for one axis has jumped to the other pointer's location.
*/
bool TouchInputMapper::applyJumpyTouchFilter() {
// This hack requires valid axis parameters.
if (! mRawAxes.y.valid) {
return false;
}
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 (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;
#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;
#if DEBUG_HACKS
LOGD("JumpyTouchFilter: Transition - drop limit reset");
#endif
return false;
}
// We have the same number of pointers as last time.
// A 'jumpy' point is one where the coordinate value for one axis
// has jumped to the other pointer's location. No need to do anything
// else if we only have one pointer.
if (pointerCount < 2) {
return false;
}
if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) {
int jumpyEpsilon = mRawAxes.y.getRange() / JUMPY_EPSILON_DIVISOR;
// We only replace the single worst jumpy point as characterized by pointer distance
// in a single axis.
int32_t badPointerIndex = -1;
int32_t badPointerReplacementIndex = -1;
int32_t badPointerDistance = INT_MIN; // distance to be corrected
for (uint32_t i = pointerCount; i-- > 0; ) {
int32_t x = mCurrentTouch.pointers[i].x;
int32_t y = mCurrentTouch.pointers[i].y;
#if DEBUG_HACKS
LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y);
#endif
// Check if a touch point is too close to another's coordinates
bool dropX = false, dropY = false;
for (uint32_t j = 0; j < pointerCount; j++) {
if (i == j) {
continue;
}
if (abs(x - mCurrentTouch.pointers[j].x) <= jumpyEpsilon) {
dropX = true;
break;
}
if (abs(y - mCurrentTouch.pointers[j].y) <= jumpyEpsilon) {
dropY = true;
break;
}
}
if (! dropX && ! dropY) {
continue; // not jumpy
}
// Find a replacement candidate by comparing with older points on the
// complementary (non-jumpy) axis.
int32_t distance = INT_MIN; // distance to be corrected
int32_t replacementIndex = -1;
if (dropX) {
// X looks too close. Find an older replacement point with a close Y.
int32_t smallestDeltaY = INT_MAX;
for (uint32_t j = 0; j < pointerCount; j++) {
int32_t deltaY = abs(y - 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 replacing this pointer would correct a worse error than the previous ones
// considered, then use this replacement instead.
if (distance > badPointerDistance) {
badPointerIndex = i;
badPointerReplacementIndex = replacementIndex;
badPointerDistance = distance;
}
}
// Correct the jumpy pointer if one was found.
if (badPointerIndex >= 0) {
#if DEBUG_HACKS
LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)",
badPointerIndex,
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;
}
}
mJumpyTouchFilter.jumpyPointsDropped = 0;
return false;
}
/* Special hack for devices that have bad screen data: aggregate and
* compute averages of the coordinate data, to reduce the amount of
* jitter seen by applications. */
void 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;
switch (mCalibration.pressureSource) {
case Calibration::PRESSURE_SOURCE_PRESSURE:
pressure = mCurrentTouch.pointers[currentIndex].pressure;
break;
case Calibration::PRESSURE_SOURCE_TOUCH:
pressure = mCurrentTouch.pointers[currentIndex].touchMajor;
break;
default:
pressure = 1;
break;
}
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 (distance < AVERAGING_DISTANCE_LIMIT) {
// Increment end index in preparation for recording new historical data.
end += 1;
if (end > AVERAGING_HISTORY_SIZE) {
end = 0;
}
// If the end index has looped back to the start index then we have filled
// the historical trace up to the desired size so we drop the historical
// data at the start of the trace.
if (end == start) {
start += 1;
if (start > AVERAGING_HISTORY_SIZE) {
start = 0;
}
}
// Add the raw data to the historical trace.
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;
}
}
if (totalPressure != 0) {
averagedX /= totalPressure;
averagedY /= totalPressure;
#if DEBUG_HACKS
LOGD("AveragingTouchFilter: Pointer id %d - "
"totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure,
averagedX, averagedY);
#endif
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 {
#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;
}
}
int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
{ // acquire lock
AutoMutex _l(mLock);
if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.keyCode == keyCode) {
return AKEY_STATE_VIRTUAL;
}
size_t numVirtualKeys = mLocked.virtualKeys.size();
for (size_t i = 0; i < numVirtualKeys; i++) {
const VirtualKey& virtualKey = mLocked.virtualKeys[i];
if (virtualKey.keyCode == keyCode) {
return AKEY_STATE_UP;
}
}
} // release lock
return AKEY_STATE_UNKNOWN;
}
int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
{ // acquire lock
AutoMutex _l(mLock);
if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.scanCode == scanCode) {
return AKEY_STATE_VIRTUAL;
}
size_t numVirtualKeys = mLocked.virtualKeys.size();
for (size_t i = 0; i < numVirtualKeys; i++) {
const VirtualKey& virtualKey = mLocked.virtualKeys[i];
if (virtualKey.scanCode == scanCode) {
return AKEY_STATE_UP;
}
}
} // release lock
return AKEY_STATE_UNKNOWN;
}
bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) {
{ // acquire lock
AutoMutex _l(mLock);
size_t numVirtualKeys = mLocked.virtualKeys.size();
for (size_t i = 0; i < numVirtualKeys; i++) {
const VirtualKey& virtualKey = mLocked.virtualKeys[i];
for (size_t i = 0; i < numCodes; i++) {
if (virtualKey.keyCode == keyCodes[i]) {
outFlags[i] = 1;
}
}
}
} // release lock
return true;
}
// --- SingleTouchInputMapper ---
SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
TouchInputMapper(device, associatedDisplayId) {
initialize();
}
SingleTouchInputMapper::~SingleTouchInputMapper() {
}
void SingleTouchInputMapper::initialize() {
mAccumulator.clear();
mDown = false;
mX = 0;
mY = 0;
mPressure = 0; // default to 0 for devices that don't report pressure
mToolWidth = 0; // default to 0 for devices that don't report tool width
}
void SingleTouchInputMapper::reset() {
TouchInputMapper::reset();
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;
// Don't sync immediately. Wait until the next SYN_REPORT since we might
// not have received valid position information yet. This logic assumes that
// BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet.
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:
sync(rawEvent->when);
break;
}
break;
}
}
void SingleTouchInputMapper::sync(nsecs_t when) {
uint32_t fields = mAccumulator.fields;
if (fields == 0) {
return; // no new state changes, so nothing to do
}
if (fields & Accumulator::FIELD_BTN_TOUCH) {
mDown = mAccumulator.btnTouch;
}
if (fields & Accumulator::FIELD_ABS_X) {
mX = mAccumulator.absX;
}
if (fields & Accumulator::FIELD_ABS_Y) {
mY = mAccumulator.absY;
}
if (fields & Accumulator::FIELD_ABS_PRESSURE) {
mPressure = mAccumulator.absPressure;
}
if (fields & Accumulator::FIELD_ABS_TOOL_WIDTH) {
mToolWidth = 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].touchMajor = 0;
mCurrentTouch.pointers[0].touchMinor = 0;
mCurrentTouch.pointers[0].toolMajor = mToolWidth;
mCurrentTouch.pointers[0].toolMinor = mToolWidth;
mCurrentTouch.pointers[0].orientation = 0;
mCurrentTouch.idToIndex[0] = 0;
mCurrentTouch.idBits.markBit(0);
}
syncTouch(when, true);
mAccumulator.clear();
}
void SingleTouchInputMapper::configureRawAxes() {
TouchInputMapper::configureRawAxes();
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mRawAxes.x);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mRawAxes.y);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mRawAxes.pressure);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mRawAxes.toolMajor);
}
// --- MultiTouchInputMapper ---
MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
TouchInputMapper(device, associatedDisplayId) {
initialize();
}
MultiTouchInputMapper::~MultiTouchInputMapper() {
}
void MultiTouchInputMapper::initialize() {
mAccumulator.clear();
}
void MultiTouchInputMapper::reset() {
TouchInputMapper::reset();
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;
case ABS_MT_PRESSURE:
pointer->fields |= Accumulator::FIELD_ABS_MT_PRESSURE;
pointer->absMTPressure = rawEvent->value;
break;
}
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;
}
case SYN_REPORT:
sync(rawEvent->when);
break;
}
break;
}
}
void MultiTouchInputMapper::sync(nsecs_t when) {
static const uint32_t REQUIRED_FIELDS =
Accumulator::FIELD_ABS_MT_POSITION_X | Accumulator::FIELD_ABS_MT_POSITION_Y;
uint32_t inCount = mAccumulator.pointerCount;
uint32_t outCount = 0;
bool havePointerIds = true;
mCurrentTouch.clear();
for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
const Accumulator::Pointer& inPointer = mAccumulator.pointers[inIndex];
uint32_t fields = inPointer.fields;
if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
// Some drivers send empty MT sync packets without X / Y to indicate a pointer up.
// Drop this finger.
continue;
}
PointerData& outPointer = mCurrentTouch.pointers[outCount];
outPointer.x = inPointer.absMTPositionX;
outPointer.y = inPointer.absMTPositionY;
if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) {
if (inPointer.absMTPressure <= 0) {
// Some devices send sync packets with X / Y but with a 0 presure to indicate
// a pointer up. Drop this finger.
continue;
}
outPointer.pressure = inPointer.absMTPressure;
} else {
// Default pressure to 0 if absent.
outPointer.pressure = 0;
}
if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) {
if (inPointer.absMTTouchMajor <= 0) {
// Some devices send sync packets with X / Y but with a 0 touch major to indicate
// a pointer going up. Drop this finger.
continue;
}
outPointer.touchMajor = inPointer.absMTTouchMajor;
} else {
// Default touch area to 0 if absent.
outPointer.touchMajor = 0;
}
if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) {
outPointer.touchMinor = inPointer.absMTTouchMinor;
} else {
// Assume touch area is circular.
outPointer.touchMinor = outPointer.touchMajor;
}
if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) {
outPointer.toolMajor = inPointer.absMTWidthMajor;
} else {
// Default tool area to 0 if absent.
outPointer.toolMajor = 0;
}
if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) {
outPointer.toolMinor = inPointer.absMTWidthMinor;
} else {
// Assume tool area is circular.
outPointer.toolMinor = outPointer.toolMajor;
}
if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) {
outPointer.orientation = inPointer.absMTOrientation;
} else {
// Default orientation to vertical if absent.
outPointer.orientation = 0;
}
// Assign pointer id using tracking id if available.
if (havePointerIds) {
if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
uint32_t id = uint32_t(inPointer.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 {
outPointer.id = id;
mCurrentTouch.idToIndex[id] = outCount;
mCurrentTouch.idBits.markBit(id);
}
} else {
havePointerIds = false;
}
}
outCount += 1;
}
mCurrentTouch.pointerCount = outCount;
syncTouch(when, havePointerIds);
mAccumulator.clear();
}
void MultiTouchInputMapper::configureRawAxes() {
TouchInputMapper::configureRawAxes();
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mRawAxes.x);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mRawAxes.y);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mRawAxes.touchMajor);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mRawAxes.touchMinor);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mRawAxes.toolMajor);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mRawAxes.toolMinor);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mRawAxes.orientation);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mRawAxes.pressure);
}
} // namespace android