//
// 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 " "
#define INDENT2 " "
#define INDENT3 " "
#define INDENT4 " "
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);
}
static inline const char* toString(bool value) {
return value ? "true" : "false";
}
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->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
removeDevice(rawEvent->deviceId);
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
handleConfigurationChanged(rawEvent->when);
break;
default:
consumeEvent(rawEvent);
break;
}
}
void InputReader::addDevice(int32_t deviceId) {
String8 name = mEventHub->getDeviceName(deviceId);
uint32_t classes = mEventHub->getDeviceClasses(deviceId);
InputDevice* device = createDevice(deviceId, name, classes);
device->configure();
if (device->isIgnored()) {
LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", deviceId, name.string());
} else {
LOGI("Device added: id=0x%x, name=%s, sources=%08x", deviceId, name.string(),
device->getSources());
}
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;
}
}
void InputReader::removeDevice(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;
}
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;
}
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 (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) {
mEventHub->dump(dump);
dump.append("\n");
dump.append("Input Reader State:\n");
{ // acquire device registry reader lock
RWLock::AutoRLock _rl(mDeviceRegistryLock);
for (size_t i = 0; i < mDevices.size(); i++) {
mDevices.valueAt(i)->dump(dump);
}
} // release device registy reader lock
}
// --- 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();
}
static void dumpMotionRange(String8& dump, const InputDeviceInfo& deviceInfo,
int32_t rangeType, const char* name) {
const InputDeviceInfo::MotionRange* range = deviceInfo.getMotionRange(rangeType);
if (range) {
dump.appendFormat(INDENT3 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n",
name, range->min, range->max, range->flat, range->fuzz);
}
}
void InputDevice::dump(String8& dump) {
InputDeviceInfo deviceInfo;
getDeviceInfo(& deviceInfo);
dump.appendFormat(INDENT "Device 0x%x: %s\n", deviceInfo.getId(),
deviceInfo.getName().string());
dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
if (!deviceInfo.getMotionRanges().isEmpty()) {
dump.append(INDENT2 "Motion Ranges:\n");
dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_X, "X");
dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_Y, "Y");
dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_PRESSURE, "Pressure");
dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_SIZE, "Size");
dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MAJOR, "TouchMajor");
dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MINOR, "TouchMinor");
dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MAJOR, "ToolMajor");
dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MINOR, "ToolMinor");
dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_ORIENTATION, "Orientation");
}
size_t numMappers = mMappers.size();
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->dump(dump);
}
}
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::dump(String8& dump) {
}
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;
}
// --- 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) {
getDispatcher()->notifySwitch(when, switchCode, switchValue, 0);
}
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::dump(String8& dump) {
{ // acquire lock
AutoMutex _l(mLock);
dump.append(INDENT2 "Keyboard Input Mapper:\n");
dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType);
dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mLocked.keyDowns.size());
dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mLocked.metaState);
dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
} // release lock
}
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.itemAt(keyDownIndex).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.itemAt(keyDownIndex).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();
}
getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, 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::dump(String8& dump) {
{ // acquire lock
AutoMutex _l(mLock);
dump.append(INDENT2 "Trackball Input Mapper:\n");
dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down));
dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
} // release lock
}
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
int32_t metaState = mContext->getGlobalMetaState();
int32_t pointerId = 0;
getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, 0,
motionEventAction, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
mAccumulator.clear();
}
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.haveTouchSize) {
info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR,
mLocked.orientedRanges.touchMajor);
info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR,
mLocked.orientedRanges.touchMinor);
}
if (mLocked.orientedRanges.haveToolSize) {
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::dump(String8& dump) {
{ // acquire lock
AutoMutex _l(mLock);
dump.append(INDENT2 "Touch Input Mapper:\n");
dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
dumpParameters(dump);
dumpVirtualKeysLocked(dump);
dumpRawAxes(dump);
dumpCalibration(dump);
dumpSurfaceLocked(dump);
dump.appendFormat(INDENT3 "Translation and Scaling Factors:");
dump.appendFormat(INDENT4 "XOrigin: %d\n", mLocked.xOrigin);
dump.appendFormat(INDENT4 "YOrigin: %d\n", mLocked.yOrigin);
dump.appendFormat(INDENT4 "XScale: %0.3f\n", mLocked.xScale);
dump.appendFormat(INDENT4 "YScale: %0.3f\n", mLocked.yScale);
dump.appendFormat(INDENT4 "XPrecision: %0.3f\n", mLocked.xPrecision);
dump.appendFormat(INDENT4 "YPrecision: %0.3f\n", mLocked.yPrecision);
dump.appendFormat(INDENT4 "GeometricScale: %0.3f\n", mLocked.geometricScale);
dump.appendFormat(INDENT4 "ToolSizeLinearScale: %0.3f\n", mLocked.toolSizeLinearScale);
dump.appendFormat(INDENT4 "ToolSizeLinearBias: %0.3f\n", mLocked.toolSizeLinearBias);
dump.appendFormat(INDENT4 "ToolSizeAreaScale: %0.3f\n", mLocked.toolSizeAreaScale);
dump.appendFormat(INDENT4 "ToolSizeAreaBias: %0.3f\n", mLocked.toolSizeAreaBias);
dump.appendFormat(INDENT4 "PressureScale: %0.3f\n", mLocked.pressureScale);
dump.appendFormat(INDENT4 "SizeScale: %0.3f\n", mLocked.sizeScale);
dump.appendFormat(INDENT4 "OrientationSCale: %0.3f\n", mLocked.orientationScale);
} // 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.haveTouchSize = false;
mLocked.orientedRanges.haveToolSize = false;
mLocked.orientedRanges.haveOrientation = false;
}
void TouchInputMapper::configure() {
InputMapper::configure();
// Configure basic parameters.
configureParameters();
// Configure absolute axis information.
configureRawAxes();
// Prepare input device calibration.
parseCalibration();
resolveCalibration();
{ // 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::dumpParameters(String8& dump) {
dump.appendFormat(INDENT3 "UseBadTouchFilter: %s\n",
toString(mParameters.useBadTouchFilter));
dump.appendFormat(INDENT3 "UseAveragingTouchFilter: %s\n",
toString(mParameters.useAveragingTouchFilter));
dump.appendFormat(INDENT3 "UseJumpyTouchFilter: %s\n",
toString(mParameters.useJumpyTouchFilter));
}
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 dumpAxisInfo(String8& dump, RawAbsoluteAxisInfo axis, const char* name) {
if (axis.valid) {
dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n",
name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
} else {
dump.appendFormat(INDENT4 "%s: unknown range\n", name);
}
}
void TouchInputMapper::dumpRawAxes(String8& dump) {
dump.append(INDENT3 "Raw Axes:\n");
dumpAxisInfo(dump, mRawAxes.x, "X");
dumpAxisInfo(dump, mRawAxes.y, "Y");
dumpAxisInfo(dump, mRawAxes.pressure, "Pressure");
dumpAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor");
dumpAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor");
dumpAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor");
dumpAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor");
dumpAxisInfo(dump, 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: id=0x%x, name=%s, display size is now %dx%d",
getDeviceId(), getDeviceName().string(), width, 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.touchSizeCalibration != Calibration::TOUCH_SIZE_CALIBRATION_NONE) {
mLocked.orientedRanges.haveTouchSize = 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.
mLocked.toolSizeLinearScale = 0;
mLocked.toolSizeLinearBias = 0;
mLocked.toolSizeAreaScale = 0;
mLocked.toolSizeAreaBias = 0;
if (mCalibration.toolSizeCalibration != Calibration::TOOL_SIZE_CALIBRATION_NONE) {
if (mCalibration.toolSizeCalibration == Calibration::TOOL_SIZE_CALIBRATION_LINEAR) {
if (mCalibration.haveToolSizeLinearScale) {
mLocked.toolSizeLinearScale = mCalibration.toolSizeLinearScale;
} else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
mLocked.toolSizeLinearScale = float(min(width, height))
/ mRawAxes.toolMajor.maxValue;
}
if (mCalibration.haveToolSizeLinearBias) {
mLocked.toolSizeLinearBias = mCalibration.toolSizeLinearBias;
}
} else if (mCalibration.toolSizeCalibration ==
Calibration::TOOL_SIZE_CALIBRATION_AREA) {
if (mCalibration.haveToolSizeLinearScale) {
mLocked.toolSizeLinearScale = mCalibration.toolSizeLinearScale;
} else {
mLocked.toolSizeLinearScale = min(width, height);
}
if (mCalibration.haveToolSizeLinearBias) {
mLocked.toolSizeLinearBias = mCalibration.toolSizeLinearBias;
}
if (mCalibration.haveToolSizeAreaScale) {
mLocked.toolSizeAreaScale = mCalibration.toolSizeAreaScale;
} else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
mLocked.toolSizeAreaScale = 1.0f / mRawAxes.toolMajor.maxValue;
}
if (mCalibration.haveToolSizeAreaBias) {
mLocked.toolSizeAreaBias = mCalibration.toolSizeAreaBias;
}
}
mLocked.orientedRanges.haveToolSize = 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.
mLocked.pressureScale = 0;
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();
}
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.
mLocked.sizeScale = 0;
if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
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
mLocked.orientationScale = 0;
if (mCalibration.orientationCalibration != Calibration::ORIENTATION_CALIBRATION_NONE) {
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;
}
return true;
}
void TouchInputMapper::dumpSurfaceLocked(String8& dump) {
dump.appendFormat(INDENT3 "SurfaceWidth: %dpx\n", mLocked.surfaceWidth);
dump.appendFormat(INDENT3 "SurfaceHeight: %dpx\n", mLocked.surfaceHeight);
dump.appendFormat(INDENT3 "SurfaceOrientation: %d\n", mLocked.surfaceOrientation);
}
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;
}
}
void TouchInputMapper::dumpVirtualKeysLocked(String8& dump) {
if (!mLocked.virtualKeys.isEmpty()) {
dump.append(INDENT3 "Virtual Keys:\n");
for (size_t i = 0; i < mLocked.virtualKeys.size(); i++) {
const VirtualKey& virtualKey = mLocked.virtualKeys.itemAt(i);
dump.appendFormat(INDENT4 "%d: scanCode=%d, keyCode=%d, "
"hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
i, virtualKey.scanCode, virtualKey.keyCode,
virtualKey.hitLeft, virtualKey.hitRight,
virtualKey.hitTop, virtualKey.hitBottom);
}
}
}
void TouchInputMapper::parseCalibration() {
const InputDeviceCalibration& in = getDevice()->getCalibration();
Calibration& out = mCalibration;
// Touch Size
out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_DEFAULT;
String8 touchSizeCalibrationString;
if (in.tryGetProperty(String8("touch.touchSize.calibration"), touchSizeCalibrationString)) {
if (touchSizeCalibrationString == "none") {
out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_NONE;
} else if (touchSizeCalibrationString == "geometric") {
out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC;
} else if (touchSizeCalibrationString == "pressure") {
out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE;
} else if (touchSizeCalibrationString != "default") {
LOGW("Invalid value for touch.touchSize.calibration: '%s'",
touchSizeCalibrationString.string());
}
}
// Tool Size
out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_DEFAULT;
String8 toolSizeCalibrationString;
if (in.tryGetProperty(String8("touch.toolSize.calibration"), toolSizeCalibrationString)) {
if (toolSizeCalibrationString == "none") {
out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_NONE;
} else if (toolSizeCalibrationString == "geometric") {
out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC;
} else if (toolSizeCalibrationString == "linear") {
out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_LINEAR;
} else if (toolSizeCalibrationString == "area") {
out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_AREA;
} else if (toolSizeCalibrationString != "default") {
LOGW("Invalid value for touch.toolSize.calibration: '%s'",
toolSizeCalibrationString.string());
}
}
out.haveToolSizeLinearScale = in.tryGetProperty(String8("touch.toolSize.linearScale"),
out.toolSizeLinearScale);
out.haveToolSizeLinearBias = in.tryGetProperty(String8("touch.toolSize.linearBias"),
out.toolSizeLinearBias);
out.haveToolSizeAreaScale = in.tryGetProperty(String8("touch.toolSize.areaScale"),
out.toolSizeAreaScale);
out.haveToolSizeAreaBias = in.tryGetProperty(String8("touch.toolSize.areaBias"),
out.toolSizeAreaBias);
out.haveToolSizeIsSummed = in.tryGetProperty(String8("touch.toolSize.isSummed"),
out.toolSizeIsSummed);
// Pressure
out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
String8 pressureCalibrationString;
if (in.tryGetProperty(String8("touch.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 touch.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("touch.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 touch.size.calibration: '%s'",
sizeCalibrationString.string());
}
}
// Orientation
out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
String8 orientationCalibrationString;
if (in.tryGetProperty(String8("touch.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 touch.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 Size
switch (mCalibration.toolSizeCalibration) {
case Calibration::TOOL_SIZE_CALIBRATION_DEFAULT:
if (mRawAxes.toolMajor.valid) {
mCalibration.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_LINEAR;
} else {
mCalibration.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_NONE;
}
break;
default:
break;
}
// Touch Size
switch (mCalibration.touchSizeCalibration) {
case Calibration::TOUCH_SIZE_CALIBRATION_DEFAULT:
if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE
&& mCalibration.toolSizeCalibration != Calibration::TOOL_SIZE_CALIBRATION_NONE) {
mCalibration.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE;
} else {
mCalibration.touchSizeCalibration = Calibration::TOUCH_SIZE_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::dumpCalibration(String8& dump) {
dump.append(INDENT3 "Calibration:\n");
// Touch Size
switch (mCalibration.touchSizeCalibration) {
case Calibration::TOUCH_SIZE_CALIBRATION_NONE:
dump.append(INDENT4 "touch.touchSize.calibration: none\n");
break;
case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC:
dump.append(INDENT4 "touch.touchSize.calibration: geometric\n");
break;
case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE:
dump.append(INDENT4 "touch.touchSize.calibration: pressure\n");
break;
default:
assert(false);
}
// Tool Size
switch (mCalibration.toolSizeCalibration) {
case Calibration::TOOL_SIZE_CALIBRATION_NONE:
dump.append(INDENT4 "touch.toolSize.calibration: none\n");
break;
case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC:
dump.append(INDENT4 "touch.toolSize.calibration: geometric\n");
break;
case Calibration::TOOL_SIZE_CALIBRATION_LINEAR:
dump.append(INDENT4 "touch.toolSize.calibration: linear\n");
break;
case Calibration::TOOL_SIZE_CALIBRATION_AREA:
dump.append(INDENT4 "touch.toolSize.calibration: area\n");
break;
default:
assert(false);
}
if (mCalibration.haveToolSizeLinearScale) {
dump.appendFormat(INDENT4 "touch.toolSize.linearScale: %0.3f\n",
mCalibration.toolSizeLinearScale);
}
if (mCalibration.haveToolSizeLinearBias) {
dump.appendFormat(INDENT4 "touch.toolSize.linearBias: %0.3f\n",
mCalibration.toolSizeLinearBias);
}
if (mCalibration.haveToolSizeAreaScale) {
dump.appendFormat(INDENT4 "touch.toolSize.areaScale: %0.3f\n",
mCalibration.toolSizeAreaScale);
}
if (mCalibration.haveToolSizeAreaBias) {
dump.appendFormat(INDENT4 "touch.toolSize.areaBias: %0.3f\n",
mCalibration.toolSizeAreaBias);
}
if (mCalibration.haveToolSizeIsSummed) {
dump.appendFormat(INDENT4 "touch.toolSize.isSummed: %d\n",
mCalibration.toolSizeIsSummed);
}
// Pressure
switch (mCalibration.pressureCalibration) {
case Calibration::PRESSURE_CALIBRATION_NONE:
dump.append(INDENT4 "touch.pressure.calibration: none\n");
break;
case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
dump.append(INDENT4 "touch.pressure.calibration: physical\n");
break;
case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
dump.append(INDENT4 "touch.pressure.calibration: amplitude\n");
break;
default:
assert(false);
}
switch (mCalibration.pressureSource) {
case Calibration::PRESSURE_SOURCE_PRESSURE:
dump.append(INDENT4 "touch.pressure.source: pressure\n");
break;
case Calibration::PRESSURE_SOURCE_TOUCH:
dump.append(INDENT4 "touch.pressure.source: touch\n");
break;
case Calibration::PRESSURE_SOURCE_DEFAULT:
break;
default:
assert(false);
}
if (mCalibration.havePressureScale) {
dump.appendFormat(INDENT4 "touch.pressure.scale: %0.3f\n",
mCalibration.pressureScale);
}
// Size
switch (mCalibration.sizeCalibration) {
case Calibration::SIZE_CALIBRATION_NONE:
dump.append(INDENT4 "touch.size.calibration: none\n");
break;
case Calibration::SIZE_CALIBRATION_NORMALIZED:
dump.append(INDENT4 "touch.size.calibration: normalized\n");
break;
default:
assert(false);
}
// Orientation
switch (mCalibration.orientationCalibration) {
case Calibration::ORIENTATION_CALIBRATION_NONE:
dump.append(INDENT4 "touch.orientation.calibration: none\n");
break;
case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
dump.append(INDENT4 "touch.orientation.calibration: interpolated\n");
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) {
uint32_t policyFlags = 0;
// 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",
mLocked.currentVirtualKey.keyCode, mLocked.currentVirtualKey.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",
mLocked.currentVirtualKey.keyCode, mLocked.currentVirtualKey.scanCode);
#endif
keyEventAction = AKEY_EVENT_ACTION_UP;
keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
| AKEY_EVENT_FLAG_CANCELED;
// Check whether the pointer moved inside the display area where we should
// start a new stroke.
int32_t x = mCurrentTouch.pointers[0].x;
int32_t y = mCurrentTouch.pointers[0].y;
if (isPointInsideSurfaceLocked(x, y)) {
mLastTouch.clear();
touchResult = DISPATCH_TOUCH;
} else {
touchResult = DROP_STROKE;
}
} 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",
mLocked.currentVirtualKey.keyCode,
mLocked.currentVirtualKey.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.
int32_t metaState = mContext->getGlobalMetaState();
policyFlags |= POLICY_FLAG_VIRTUAL;
getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
return touchResult;
}
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 and pointers moving
// all at the same time.
BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value);
BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value);
BitSet32 activeIdBits(lastIdBits.value);
uint32_t pointerCount = lastPointerCount;
// Produce an intermediate representation of the touch data that consists of the
// old location of pointers that have just gone up and the new location of pointers that
// have just moved but omits the location of pointers that have just gone down.
TouchData interimTouch;
interimTouch.copyFrom(mLastTouch);
BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
bool moveNeeded = false;
while (!moveIdBits.isEmpty()) {
uint32_t moveId = moveIdBits.firstMarkedBit();
moveIdBits.clearBit(moveId);
int32_t oldIndex = mLastTouch.idToIndex[moveId];
int32_t newIndex = mCurrentTouch.idToIndex[moveId];
if (mLastTouch.pointers[oldIndex] != mCurrentTouch.pointers[newIndex]) {
interimTouch.pointers[oldIndex] = mCurrentTouch.pointers[newIndex];
moveNeeded = true;
}
}
// Dispatch pointer up events using the interim pointer locations.
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, &interimTouch,
oldActiveIdBits, upId, pointerCount, motionEventAction);
pointerCount -= 1;
}
// Dispatch move events if any of the remaining pointers moved from their old locations.
// Although applications receive new locations as part of individual pointer up
// events, they do not generally handle them except when presented in a move event.
if (moveNeeded) {
dispatchTouch(when, policyFlags, &mCurrentTouch,
activeIdBits, -1, pointerCount, AMOTION_EVENT_ACTION_MOVE);
}
// Dispatch pointer down events using the new pointer locations.
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.toolSizeCalibration) {
case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC:
toolMajor = in.toolMajor * mLocked.geometricScale;
if (mRawAxes.toolMinor.valid) {
toolMinor = in.toolMinor * mLocked.geometricScale;
} else {
toolMinor = toolMajor;
}
break;
case Calibration::TOOL_SIZE_CALIBRATION_LINEAR:
toolMajor = in.toolMajor != 0
? in.toolMajor * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias
: 0;
if (mRawAxes.toolMinor.valid) {
toolMinor = in.toolMinor != 0
? in.toolMinor * mLocked.toolSizeLinearScale
+ mLocked.toolSizeLinearBias
: 0;
} else {
toolMinor = toolMajor;
}
break;
case Calibration::TOOL_SIZE_CALIBRATION_AREA:
if (in.toolMajor != 0) {
float diameter = sqrtf(in.toolMajor
* mLocked.toolSizeAreaScale + mLocked.toolSizeAreaBias);
toolMajor = diameter * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias;
} else {
toolMajor = 0;
}
toolMinor = toolMajor;
break;
default:
toolMajor = 0;
toolMinor = 0;
break;
}
if (mCalibration.haveToolSizeIsSummed && mCalibration.toolSizeIsSummed) {
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.touchSizeCalibration) {
case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC:
touchMajor = in.touchMajor * mLocked.geometricScale;
if (mRawAxes.touchMinor.valid) {
touchMinor = in.touchMinor * mLocked.geometricScale;
} else {
touchMinor = touchMajor;
}
break;
case Calibration::TOUCH_SIZE_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(), getSources(), 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 pressure to indicate
// a pointer going 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",
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