blob: 1ed5ad729fc24bb1aeadf6a16b601fe6f3ed07db [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "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
// Log debug messages about gesture detection.
#define DEBUG_GESTURES 0
#include "InputReader.h"
#include <cutils/atomic.h>
#include <cutils/log.h>
#include <ui/Keyboard.h>
#include <ui/VirtualKeyMap.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 distance(float x1, float y1, float x2, float y2) {
return hypotf(x1 - x2, y1 - y2);
}
inline static int32_t signExtendNybble(int32_t value) {
return value >= 8 ? value - 16 : value;
}
static inline const char* toString(bool value) {
return value ? "true" : "false";
}
static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation,
const int32_t map[][4], size_t mapSize) {
if (orientation != DISPLAY_ORIENTATION_0) {
for (size_t i = 0; i < mapSize; i++) {
if (value == map[i][0]) {
return map[i][orientation];
}
}
}
return value;
}
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 size_t keyCodeRotationMapSize =
sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
return rotateValueUsingRotationMap(keyCode, orientation,
keyCodeRotationMap, keyCodeRotationMapSize);
}
static const int32_t edgeFlagRotationMap[][4] = {
// edge flags enumerated counter-clockwise with the original (unrotated) edge flag first
// no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
{ AMOTION_EVENT_EDGE_FLAG_BOTTOM, AMOTION_EVENT_EDGE_FLAG_RIGHT,
AMOTION_EVENT_EDGE_FLAG_TOP, AMOTION_EVENT_EDGE_FLAG_LEFT },
{ AMOTION_EVENT_EDGE_FLAG_RIGHT, AMOTION_EVENT_EDGE_FLAG_TOP,
AMOTION_EVENT_EDGE_FLAG_LEFT, AMOTION_EVENT_EDGE_FLAG_BOTTOM },
{ AMOTION_EVENT_EDGE_FLAG_TOP, AMOTION_EVENT_EDGE_FLAG_LEFT,
AMOTION_EVENT_EDGE_FLAG_BOTTOM, AMOTION_EVENT_EDGE_FLAG_RIGHT },
{ AMOTION_EVENT_EDGE_FLAG_LEFT, AMOTION_EVENT_EDGE_FLAG_BOTTOM,
AMOTION_EVENT_EDGE_FLAG_RIGHT, AMOTION_EVENT_EDGE_FLAG_TOP },
};
static const size_t edgeFlagRotationMapSize =
sizeof(edgeFlagRotationMap) / sizeof(edgeFlagRotationMap[0]);
static int32_t rotateEdgeFlag(int32_t edgeFlag, int32_t orientation) {
return rotateValueUsingRotationMap(edgeFlag, orientation,
edgeFlagRotationMap, edgeFlagRotationMapSize);
}
static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0;
}
static uint32_t getButtonStateForScanCode(int32_t scanCode) {
// Currently all buttons are mapped to the primary button.
switch (scanCode) {
case BTN_LEFT:
case BTN_RIGHT:
case BTN_MIDDLE:
case BTN_SIDE:
case BTN_EXTRA:
case BTN_FORWARD:
case BTN_BACK:
case BTN_TASK:
return BUTTON_STATE_PRIMARY;
default:
return 0;
}
}
// Returns true if the pointer should be reported as being down given the specified
// button states.
static bool isPointerDown(uint32_t buttonState) {
return buttonState & BUTTON_STATE_PRIMARY;
}
static int32_t calculateEdgeFlagsUsingPointerBounds(
const sp<PointerControllerInterface>& pointerController, float x, float y) {
int32_t edgeFlags = 0;
float minX, minY, maxX, maxY;
if (pointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
if (x <= minX) {
edgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT;
} else if (x >= maxX) {
edgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT;
}
if (y <= minY) {
edgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP;
} else if (y >= maxY) {
edgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM;
}
}
return edgeFlags;
}
static float calculateCommonVector(float a, float b) {
if (a > 0 && b > 0) {
return a < b ? a : b;
} else if (a < 0 && b < 0) {
return a > b ? a : b;
} else {
return 0;
}
}
// --- InputReader ---
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputDispatcherInterface>& dispatcher) :
mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mRefreshConfiguration(0) {
configure(true /*firstTime*/);
updateGlobalMetaState();
updateInputConfiguration();
}
InputReader::~InputReader() {
for (size_t i = 0; i < mDevices.size(); i++) {
delete mDevices.valueAt(i);
}
}
void InputReader::loopOnce() {
if (android_atomic_acquire_load(&mRefreshConfiguration)) {
android_atomic_release_store(0, &mRefreshConfiguration);
configure(false /*firstTime*/);
}
int32_t timeoutMillis = -1;
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
}
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
if (count) {
processEvents(mEventBuffer, count);
}
if (!count || timeoutMillis == 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
#if DEBUG_RAW_EVENTS
LOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
mNextTimeout = LLONG_MAX;
timeoutExpired(now);
}
}
void InputReader::processEvents(const RawEvent* rawEvents, size_t count) {
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
size_t batchSize = 1;
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
int32_t deviceId = rawEvent->deviceId;
while (batchSize < count) {
if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
|| rawEvent[batchSize].deviceId != deviceId) {
break;
}
batchSize += 1;
}
#if DEBUG_RAW_EVENTS
LOGD("BatchSize: %d Count: %d", batchSize, count);
#endif
processEventsForDevice(deviceId, rawEvent, batchSize);
} else {
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:
assert(false); // can't happen
break;
}
}
count -= batchSize;
rawEvent += batchSize;
}
}
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=%d, name='%s' (ignored non-input device)", deviceId, name.string());
} else {
LOGI("Device added: id=%d, name='%s', sources=0x%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=%d, name='%s' (ignored non-input device)",
device->getId(), device->getName().string());
} else {
LOGI("Device removed: id=%d, name='%s', sources=0x%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);
// External devices.
if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
device->setExternal(true);
}
// Switch-like devices.
if (classes & INPUT_DEVICE_CLASS_SWITCH) {
device->addMapper(new SwitchInputMapper(device));
}
// Keyboard-like devices.
uint32_t keyboardSource = 0;
int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
keyboardSource |= AINPUT_SOURCE_KEYBOARD;
}
if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
}
if (classes & INPUT_DEVICE_CLASS_DPAD) {
keyboardSource |= AINPUT_SOURCE_DPAD;
}
if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
keyboardSource |= AINPUT_SOURCE_GAMEPAD;
}
if (keyboardSource != 0) {
device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
}
// Cursor-like devices.
if (classes & INPUT_DEVICE_CLASS_CURSOR) {
device->addMapper(new CursorInputMapper(device));
}
// Touchscreens and touchpad devices.
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
device->addMapper(new MultiTouchInputMapper(device));
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
device->addMapper(new SingleTouchInputMapper(device));
}
// Joystick-like devices.
if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
device->addMapper(new JoystickInputMapper(device));
}
return device;
}
void InputReader::processEventsForDevice(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
{ // 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(rawEvents, count);
} // release device registry reader lock
}
void InputReader::timeoutExpired(nsecs_t when) {
{ // acquire device registry reader lock
RWLock::AutoRLock _rl(mDeviceRegistryLock);
for (size_t i = 0; i < mDevices.size(); i++) {
InputDevice* device = mDevices.valueAt(i);
if (!device->isIgnored()) {
device->timeoutExpired(when);
}
}
} // 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::configure(bool firstTime) {
mPolicy->getReaderConfiguration(&mConfig);
mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
if (!firstTime) {
mEventHub->reopenDevices();
}
}
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::disableVirtualKeysUntil(nsecs_t time) {
mDisableVirtualKeysTimeout = time;
}
bool InputReader::shouldDropVirtualKey(nsecs_t now,
InputDevice* device, int32_t keyCode, int32_t scanCode) {
if (now < mDisableVirtualKeysTimeout) {
LOGI("Dropping virtual key from device %s because virtual keys are "
"temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d",
device->getName().string(),
(mDisableVirtualKeysTimeout - now) * 0.000001,
keyCode, scanCode);
return true;
} else {
return false;
}
}
void InputReader::fadePointer() {
{ // acquire device registry reader lock
RWLock::AutoRLock _rl(mDeviceRegistryLock);
for (size_t i = 0; i < mDevices.size(); i++) {
InputDevice* device = mDevices.valueAt(i);
device->fadePointer();
}
} // release device registry reader lock
}
void InputReader::requestTimeoutAtTime(nsecs_t when) {
if (when < mNextTimeout) {
mNextTimeout = when;
}
}
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::refreshConfiguration() {
android_atomic_release_store(1, &mRefreshConfiguration);
}
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
dump.append(INDENT "Configuration:\n");
dump.append(INDENT2 "ExcludedDeviceNames: [");
for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) {
if (i != 0) {
dump.append(", ");
}
dump.append(mConfig.excludedDeviceNames.itemAt(i).string());
}
dump.append("]\n");
dump.appendFormat(INDENT2 "FilterTouchEvents: %s\n",
toString(mConfig.filterTouchEvents));
dump.appendFormat(INDENT2 "FilterJumpyTouchEvents: %s\n",
toString(mConfig.filterJumpyTouchEvents));
dump.appendFormat(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
mConfig.virtualKeyQuietTime * 0.000001f);
dump.appendFormat(INDENT2 "PointerVelocityControlParameters: "
"scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n",
mConfig.pointerVelocityControlParameters.scale,
mConfig.pointerVelocityControlParameters.lowThreshold,
mConfig.pointerVelocityControlParameters.highThreshold,
mConfig.pointerVelocityControlParameters.acceleration);
dump.appendFormat(INDENT2 "WheelVelocityControlParameters: "
"scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n",
mConfig.wheelVelocityControlParameters.scale,
mConfig.wheelVelocityControlParameters.lowThreshold,
mConfig.wheelVelocityControlParameters.highThreshold,
mConfig.wheelVelocityControlParameters.acceleration);
dump.appendFormat(INDENT2 "PointerGesture:\n");
dump.appendFormat(INDENT3 "QuietInterval: %0.1fms\n",
mConfig.pointerGestureQuietInterval * 0.000001f);
dump.appendFormat(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n",
mConfig.pointerGestureDragMinSwitchSpeed);
dump.appendFormat(INDENT3 "TapInterval: %0.1fms\n",
mConfig.pointerGestureTapInterval * 0.000001f);
dump.appendFormat(INDENT3 "TapDragInterval: %0.1fms\n",
mConfig.pointerGestureTapDragInterval * 0.000001f);
dump.appendFormat(INDENT3 "TapSlop: %0.1fpx\n",
mConfig.pointerGestureTapSlop);
dump.appendFormat(INDENT3 "MultitouchSettleInterval: %0.1fms\n",
mConfig.pointerGestureMultitouchSettleInterval * 0.000001f);
dump.appendFormat(INDENT3 "MultitouchMinDistance: %0.1fpx\n",
mConfig.pointerGestureMultitouchMinDistance);
dump.appendFormat(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n",
mConfig.pointerGestureSwipeTransitionAngleCosine);
dump.appendFormat(INDENT3 "SwipeMaxWidthRatio: %0.1f\n",
mConfig.pointerGestureSwipeMaxWidthRatio);
dump.appendFormat(INDENT3 "MovementSpeedRatio: %0.1f\n",
mConfig.pointerGestureMovementSpeedRatio);
dump.appendFormat(INDENT3 "ZoomSpeedRatio: %0.1f\n",
mConfig.pointerGestureZoomSpeedRatio);
}
// --- 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), mIsExternal(false) {
}
InputDevice::~InputDevice() {
size_t numMappers = mMappers.size();
for (size_t i = 0; i < numMappers; i++) {
delete mMappers[i];
}
mMappers.clear();
}
void InputDevice::dump(String8& dump) {
InputDeviceInfo deviceInfo;
getDeviceInfo(& deviceInfo);
dump.appendFormat(INDENT "Device %d: %s\n", deviceInfo.getId(),
deviceInfo.getName().string());
dump.appendFormat(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
if (!ranges.isEmpty()) {
dump.append(INDENT2 "Motion Ranges:\n");
for (size_t i = 0; i < ranges.size(); i++) {
const InputDeviceInfo::MotionRange& range = ranges.itemAt(i);
const char* label = getAxisLabel(range.axis);
char name[32];
if (label) {
strncpy(name, label, sizeof(name));
name[sizeof(name) - 1] = '\0';
} else {
snprintf(name, sizeof(name), "%d", range.axis);
}
dump.appendFormat(INDENT3 "%s: source=0x%08x, "
"min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n",
name, range.source, range.min, range.max, range.flat, range.fuzz);
}
}
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->getEventHub()->getConfiguration(mId, &mConfiguration);
}
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* rawEvents, size_t count) {
// Process all of the events in order for each mapper.
// We cannot simply ask each mapper to process them in bulk because mappers may
// have side-effects that must be interleaved. For example, joystick movement events and
// gamepad button presses are handled by different mappers but they should be dispatched
// in the order received.
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
#if DEBUG_RAW_EVENTS
LOGD("Input event: device=%d type=0x%04x scancode=0x%04x "
"keycode=0x%04x value=0x%04x flags=0x%08x",
rawEvent->deviceId, rawEvent->type, rawEvent->scanCode, rawEvent->keyCode,
rawEvent->value, rawEvent->flags);
#endif
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);
}
}
}
void InputDevice::timeoutExpired(nsecs_t when) {
size_t numMappers = mMappers.size();
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->timeoutExpired(when);
}
}
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;
}
void InputDevice::fadePointer() {
size_t numMappers = mMappers.size();
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->fadePointer();
}
}
// --- 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() {
}
void InputMapper::timeoutExpired(nsecs_t when) {
}
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;
}
void InputMapper::fadePointer() {
}
void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump,
const 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);
}
}
// --- SwitchInputMapper ---
SwitchInputMapper::SwitchInputMapper(InputDevice* device) :
InputMapper(device) {
}
SwitchInputMapper::~SwitchInputMapper() {
}
uint32_t SwitchInputMapper::getSources() {
return AINPUT_SOURCE_SWITCH;
}
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,
uint32_t source, int32_t keyboardType) :
InputMapper(device), mSource(source),
mKeyboardType(keyboardType) {
initializeLocked();
}
KeyboardInputMapper::~KeyboardInputMapper() {
}
void KeyboardInputMapper::initializeLocked() {
mLocked.metaState = AMETA_NONE;
mLocked.downTime = 0;
}
uint32_t KeyboardInputMapper::getSources() {
return mSource;
}
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");
dumpParameters(dump);
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::configure() {
InputMapper::configure();
// Configure basic parameters.
configureParameters();
// Reset LEDs.
{
AutoMutex _l(mLock);
resetLedStateLocked();
}
}
void KeyboardInputMapper::configureParameters() {
mParameters.orientationAware = false;
getDevice()->getConfiguration().tryGetProperty(String8("keyboard.orientationAware"),
mParameters.orientationAware);
mParameters.associatedDisplayId = mParameters.orientationAware ? 0 : -1;
}
void KeyboardInputMapper::dumpParameters(String8& dump) {
dump.append(INDENT3 "Parameters:\n");
dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
mParameters.associatedDisplayId);
dump.appendFormat(INDENT4 "OrientationAware: %s\n",
toString(mParameters.orientationAware));
}
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();
resetLedStateLocked();
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_MISC && scanCode < BTN_MOUSE)
|| (scanCode >= BTN_JOYSTICK && 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 (mParameters.orientationAware && mParameters.associatedDisplayId >= 0) {
int32_t orientation;
if (!getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
NULL, NULL, & orientation)) {
orientation = DISPLAY_ORIENTATION_0;
}
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
if ((policyFlags & POLICY_FLAG_VIRTUAL)
&& mContext->shouldDropVirtualKey(when,
getDevice(), keyCode, scanCode)) {
return;
}
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;
updateLedStateLocked(false);
}
downTime = mLocked.downTime;
} // release lock
// Key down on external an keyboard should wake the device.
// We don't do this for internal keyboards to prevent them from waking up in your pocket.
// For internal keyboards, the key layout file should specify the policy flags for
// each wake key individually.
// TODO: Use the input device configuration to control this behavior more finely.
if (down && getDevice()->isExternal()
&& !(policyFlags & (POLICY_FLAG_WAKE | POLICY_FLAG_WAKE_DROPPED))) {
policyFlags |= POLICY_FLAG_WAKE_DROPPED;
}
if (metaStateChanged) {
getContext()->updateGlobalMetaState();
}
if (down && !isMetaKey(keyCode)) {
getContext()->fadePointer();
}
getDispatcher()->notifyKey(when, getDeviceId(), mSource, 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
}
void KeyboardInputMapper::resetLedStateLocked() {
initializeLedStateLocked(mLocked.capsLockLedState, LED_CAPSL);
initializeLedStateLocked(mLocked.numLockLedState, LED_NUML);
initializeLedStateLocked(mLocked.scrollLockLedState, LED_SCROLLL);
updateLedStateLocked(true);
}
void KeyboardInputMapper::initializeLedStateLocked(LockedState::LedState& ledState, int32_t led) {
ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
ledState.on = false;
}
void KeyboardInputMapper::updateLedStateLocked(bool reset) {
updateLedStateForModifierLocked(mLocked.capsLockLedState, LED_CAPSL,
AMETA_CAPS_LOCK_ON, reset);
updateLedStateForModifierLocked(mLocked.numLockLedState, LED_NUML,
AMETA_NUM_LOCK_ON, reset);
updateLedStateForModifierLocked(mLocked.scrollLockLedState, LED_SCROLLL,
AMETA_SCROLL_LOCK_ON, reset);
}
void KeyboardInputMapper::updateLedStateForModifierLocked(LockedState::LedState& ledState,
int32_t led, int32_t modifier, bool reset) {
if (ledState.avail) {
bool desiredState = (mLocked.metaState & modifier) != 0;
if (reset || ledState.on != desiredState) {
getEventHub()->setLedState(getDeviceId(), led, desiredState);
ledState.on = desiredState;
}
}
}
// --- CursorInputMapper ---
CursorInputMapper::CursorInputMapper(InputDevice* device) :
InputMapper(device) {
initializeLocked();
}
CursorInputMapper::~CursorInputMapper() {
}
uint32_t CursorInputMapper::getSources() {
return mSource;
}
void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
if (mParameters.mode == Parameters::MODE_POINTER) {
float minX, minY, maxX, maxY;
if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f);
info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f);
}
} else {
info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale);
info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale);
}
info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f);
if (mHaveVWheel) {
info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
}
if (mHaveHWheel) {
info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
}
}
void CursorInputMapper::dump(String8& dump) {
{ // acquire lock
AutoMutex _l(mLock);
dump.append(INDENT2 "Cursor Input Mapper:\n");
dumpParameters(dump);
dump.appendFormat(INDENT3 "XScale: %0.3f\n", mXScale);
dump.appendFormat(INDENT3 "YScale: %0.3f\n", mYScale);
dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
dump.appendFormat(INDENT3 "HaveVWheel: %s\n", toString(mHaveVWheel));
dump.appendFormat(INDENT3 "HaveHWheel: %s\n", toString(mHaveHWheel));
dump.appendFormat(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
dump.appendFormat(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
dump.appendFormat(INDENT3 "ButtonState: 0x%08x\n", mLocked.buttonState);
dump.appendFormat(INDENT3 "Down: %s\n", toString(isPointerDown(mLocked.buttonState)));
dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
} // release lock
}
void CursorInputMapper::configure() {
InputMapper::configure();
// Configure basic parameters.
configureParameters();
// Configure device mode.
switch (mParameters.mode) {
case Parameters::MODE_POINTER:
mSource = AINPUT_SOURCE_MOUSE;
mXPrecision = 1.0f;
mYPrecision = 1.0f;
mXScale = 1.0f;
mYScale = 1.0f;
mPointerController = getPolicy()->obtainPointerController(getDeviceId());
break;
case Parameters::MODE_NAVIGATION:
mSource = AINPUT_SOURCE_TRACKBALL;
mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
break;
}
mVWheelScale = 1.0f;
mHWheelScale = 1.0f;
mHaveVWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_WHEEL);
mHaveHWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_HWHEEL);
mPointerVelocityControl.setParameters(getConfig()->pointerVelocityControlParameters);
mWheelXVelocityControl.setParameters(getConfig()->wheelVelocityControlParameters);
mWheelYVelocityControl.setParameters(getConfig()->wheelVelocityControlParameters);
}
void CursorInputMapper::configureParameters() {
mParameters.mode = Parameters::MODE_POINTER;
String8 cursorModeString;
if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) {
if (cursorModeString == "navigation") {
mParameters.mode = Parameters::MODE_NAVIGATION;
} else if (cursorModeString != "pointer" && cursorModeString != "default") {
LOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
}
}
mParameters.orientationAware = false;
getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
mParameters.orientationAware);
mParameters.associatedDisplayId = mParameters.mode == Parameters::MODE_POINTER
|| mParameters.orientationAware ? 0 : -1;
}
void CursorInputMapper::dumpParameters(String8& dump) {
dump.append(INDENT3 "Parameters:\n");
dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
mParameters.associatedDisplayId);
switch (mParameters.mode) {
case Parameters::MODE_POINTER:
dump.append(INDENT4 "Mode: pointer\n");
break;
case Parameters::MODE_NAVIGATION:
dump.append(INDENT4 "Mode: navigation\n");
break;
default:
assert(false);
}
dump.appendFormat(INDENT4 "OrientationAware: %s\n",
toString(mParameters.orientationAware));
}
void CursorInputMapper::initializeLocked() {
mAccumulator.clear();
mLocked.buttonState = 0;
mLocked.downTime = 0;
}
void CursorInputMapper::reset() {
for (;;) {
uint32_t buttonState;
{ // acquire lock
AutoMutex _l(mLock);
buttonState = mLocked.buttonState;
if (!buttonState) {
initializeLocked();
break; // done
}
} // release lock
// Reset velocity.
mPointerVelocityControl.reset();
mWheelXVelocityControl.reset();
mWheelYVelocityControl.reset();
// Synthesize button up event on reset.
nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
mAccumulator.clear();
mAccumulator.buttonDown = 0;
mAccumulator.buttonUp = buttonState;
mAccumulator.fields = Accumulator::FIELD_BUTTONS;
sync(when);
}
InputMapper::reset();
}
void CursorInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_KEY: {
uint32_t buttonState = getButtonStateForScanCode(rawEvent->scanCode);
if (buttonState) {
if (rawEvent->value) {
mAccumulator.buttonDown = buttonState;
mAccumulator.buttonUp = 0;
} else {
mAccumulator.buttonDown = 0;
mAccumulator.buttonUp = buttonState;
}
mAccumulator.fields |= Accumulator::FIELD_BUTTONS;
// 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;
case REL_WHEEL:
mAccumulator.fields |= Accumulator::FIELD_REL_WHEEL;
mAccumulator.relWheel = rawEvent->value;
break;
case REL_HWHEEL:
mAccumulator.fields |= Accumulator::FIELD_REL_HWHEEL;
mAccumulator.relHWheel = rawEvent->value;
break;
}
break;
case EV_SYN:
switch (rawEvent->scanCode) {
case SYN_REPORT:
sync(rawEvent->when);
break;
}
break;
}
}
void CursorInputMapper::sync(nsecs_t when) {
uint32_t fields = mAccumulator.fields;
if (fields == 0) {
return; // no new state changes, so nothing to do
}
int32_t motionEventAction;
int32_t motionEventEdgeFlags;
PointerCoords pointerCoords;
nsecs_t downTime;
float vscroll, hscroll;
{ // acquire lock
AutoMutex _l(mLock);
bool down, downChanged;
bool wasDown = isPointerDown(mLocked.buttonState);
bool buttonsChanged = fields & Accumulator::FIELD_BUTTONS;
if (buttonsChanged) {
mLocked.buttonState = (mLocked.buttonState | mAccumulator.buttonDown)
& ~mAccumulator.buttonUp;
down = isPointerDown(mLocked.buttonState);
if (!wasDown && down) {
mLocked.downTime = when;
downChanged = true;
} else if (wasDown && !down) {
downChanged = true;
} else {
downChanged = false;
}
} else {
down = wasDown;
downChanged = false;
}
downTime = mLocked.downTime;
float deltaX = fields & Accumulator::FIELD_REL_X ? mAccumulator.relX * mXScale : 0.0f;
float deltaY = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY * mYScale : 0.0f;
if (downChanged) {
motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
} else if (down || mPointerController == NULL) {
motionEventAction = AMOTION_EVENT_ACTION_MOVE;
} else {
motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
}
if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0
&& (deltaX != 0.0f || deltaY != 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(mParameters.associatedDisplayId,
NULL, NULL, & orientation)) {
orientation = DISPLAY_ORIENTATION_0;
}
float temp;
switch (orientation) {
case DISPLAY_ORIENTATION_90:
temp = deltaX;
deltaX = deltaY;
deltaY = -temp;
break;
case DISPLAY_ORIENTATION_180:
deltaX = -deltaX;
deltaY = -deltaY;
break;
case DISPLAY_ORIENTATION_270:
temp = deltaX;
deltaX = -deltaY;
deltaY = temp;
break;
}
}
pointerCoords.clear();
motionEventEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
if (mHaveVWheel && (fields & Accumulator::FIELD_REL_WHEEL)) {
vscroll = mAccumulator.relWheel;
} else {
vscroll = 0;
}
mWheelYVelocityControl.move(when, NULL, &vscroll);
if (mHaveHWheel && (fields & Accumulator::FIELD_REL_HWHEEL)) {
hscroll = mAccumulator.relHWheel;
} else {
hscroll = 0;
}
mWheelXVelocityControl.move(when, &hscroll, NULL);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
if (mPointerController != NULL) {
if (deltaX != 0 || deltaY != 0 || vscroll != 0 || hscroll != 0
|| buttonsChanged) {
mPointerController->setPresentation(
PointerControllerInterface::PRESENTATION_POINTER);
if (deltaX != 0 || deltaY != 0) {
mPointerController->move(deltaX, deltaY);
}
if (buttonsChanged) {
mPointerController->setButtonState(mLocked.buttonState);
}
mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
}
float x, y;
mPointerController->getPosition(&x, &y);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) {
motionEventEdgeFlags = calculateEdgeFlagsUsingPointerBounds(
mPointerController, x, y);
}
} else {
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
}
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
} // release lock
// Moving an external trackball or mouse should wake the device.
// We don't do this for internal cursor devices to prevent them from waking up
// the device in your pocket.
// TODO: Use the input device configuration to control this behavior more finely.
uint32_t policyFlags = 0;
if (getDevice()->isExternal()) {
policyFlags |= POLICY_FLAG_WAKE_DROPPED;
}
int32_t metaState = mContext->getGlobalMetaState();
int32_t pointerId = 0;
getDispatcher()->notifyMotion(when, getDeviceId(), mSource, policyFlags,
motionEventAction, 0, metaState, motionEventEdgeFlags,
1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
mAccumulator.clear();
if (vscroll != 0 || hscroll != 0) {
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
getDispatcher()->notifyMotion(when, getDeviceId(), mSource, policyFlags,
AMOTION_EVENT_ACTION_SCROLL, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
}
}
int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
} else {
return AKEY_STATE_UNKNOWN;
}
}
void CursorInputMapper::fadePointer() {
{ // acquire lock
AutoMutex _l(mLock);
if (mPointerController != NULL) {
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
}
} // release lock
}
// --- TouchInputMapper ---
TouchInputMapper::TouchInputMapper(InputDevice* device) :
InputMapper(device) {
mConfig = getConfig();
mLocked.surfaceOrientation = -1;
mLocked.surfaceWidth = -1;
mLocked.surfaceHeight = -1;
initializeLocked();
}
TouchInputMapper::~TouchInputMapper() {
}
uint32_t TouchInputMapper::getSources() {
return mTouchSource | mPointerSource;
}
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.
if (!configureSurfaceLocked()) {
return;
}
info->addMotionRange(mLocked.orientedRanges.x);
info->addMotionRange(mLocked.orientedRanges.y);
if (mLocked.orientedRanges.havePressure) {
info->addMotionRange(mLocked.orientedRanges.pressure);
}
if (mLocked.orientedRanges.haveSize) {
info->addMotionRange(mLocked.orientedRanges.size);
}
if (mLocked.orientedRanges.haveTouchSize) {
info->addMotionRange(mLocked.orientedRanges.touchMajor);
info->addMotionRange(mLocked.orientedRanges.touchMinor);
}
if (mLocked.orientedRanges.haveToolSize) {
info->addMotionRange(mLocked.orientedRanges.toolMajor);
info->addMotionRange(mLocked.orientedRanges.toolMinor);
}
if (mLocked.orientedRanges.haveOrientation) {
info->addMotionRange(mLocked.orientedRanges.orientation);
}
if (mPointerController != NULL) {
float minX, minY, maxX, maxY;
if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
info->addMotionRange(AMOTION_EVENT_AXIS_X, mPointerSource,
minX, maxX, 0.0f, 0.0f);
info->addMotionRange(AMOTION_EVENT_AXIS_Y, mPointerSource,
minY, maxY, 0.0f, 0.0f);
}
info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mPointerSource,
0.0f, 1.0f, 0.0f, 0.0f);
}
} // release lock
}
void TouchInputMapper::dump(String8& dump) {
{ // acquire lock
AutoMutex _l(mLock);
dump.append(INDENT2 "Touch Input Mapper:\n");
dumpParameters(dump);
dumpVirtualKeysLocked(dump);
dumpRawAxes(dump);
dumpCalibration(dump);
dumpSurfaceLocked(dump);
dump.appendFormat(INDENT3 "Translation and Scaling Factors:\n");
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);
dump.appendFormat(INDENT3 "Last Touch:\n");
dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount);
dump.appendFormat(INDENT4 "Button State: 0x%08x\n", mLastTouch.buttonState);
if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
dump.appendFormat(INDENT3 "Pointer Gesture Detector:\n");
dump.appendFormat(INDENT4 "XMovementScale: %0.3f\n",
mLocked.pointerGestureXMovementScale);
dump.appendFormat(INDENT4 "YMovementScale: %0.3f\n",
mLocked.pointerGestureYMovementScale);
dump.appendFormat(INDENT4 "XZoomScale: %0.3f\n",
mLocked.pointerGestureXZoomScale);
dump.appendFormat(INDENT4 "YZoomScale: %0.3f\n",
mLocked.pointerGestureYZoomScale);
dump.appendFormat(INDENT4 "MaxSwipeWidth: %f\n",
mLocked.pointerGestureMaxSwipeWidth);
}
} // 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;
mPointerGesture.reset();
mPointerGesture.pointerVelocityControl.setParameters(mConfig->pointerVelocityControlParameters);
}
void TouchInputMapper::configure() {
InputMapper::configure();
// Configure basic parameters.
configureParameters();
// Configure sources.
switch (mParameters.deviceType) {
case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
mTouchSource = AINPUT_SOURCE_TOUCHSCREEN;
mPointerSource = 0;
break;
case Parameters::DEVICE_TYPE_TOUCH_PAD:
mTouchSource = AINPUT_SOURCE_TOUCHPAD;
mPointerSource = 0;
break;
case Parameters::DEVICE_TYPE_POINTER:
mTouchSource = AINPUT_SOURCE_TOUCHPAD;
mPointerSource = AINPUT_SOURCE_MOUSE;
break;
default:
assert(false);
}
// 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 = mConfig->filterTouchEvents;
mParameters.useAveragingTouchFilter = mConfig->filterTouchEvents;
mParameters.useJumpyTouchFilter = mConfig->filterJumpyTouchEvents;
// TODO: select the default gesture mode based on whether the device supports
// distinct multitouch
mParameters.gestureMode = Parameters::GESTURE_MODE_SPOTS;
String8 gestureModeString;
if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"),
gestureModeString)) {
if (gestureModeString == "pointer") {
mParameters.gestureMode = Parameters::GESTURE_MODE_POINTER;
} else if (gestureModeString == "spots") {
mParameters.gestureMode = Parameters::GESTURE_MODE_SPOTS;
} else if (gestureModeString != "default") {
LOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
}
}
if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X)
|| getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) {
// The device is a cursor device with a touch pad attached.
// By default don't use the touch pad to move the pointer.
mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
} else {
// The device is just a touch pad.
// By default use the touch pad to move the pointer and to perform related gestures.
mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
}
String8 deviceTypeString;
if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
deviceTypeString)) {
if (deviceTypeString == "touchScreen") {
mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
} else if (deviceTypeString == "touchPad") {
mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
} else if (deviceTypeString == "pointer") {
mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
} else if (deviceTypeString != "default") {
LOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
}
}
mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"),
mParameters.orientationAware);
mParameters.associatedDisplayId = mParameters.orientationAware
|| mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
|| mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER
? 0 : -1;
}
void TouchInputMapper::dumpParameters(String8& dump) {
dump.append(INDENT3 "Parameters:\n");
switch (mParameters.gestureMode) {
case Parameters::GESTURE_MODE_POINTER:
dump.append(INDENT4 "GestureMode: pointer\n");
break;
case Parameters::GESTURE_MODE_SPOTS:
dump.append(INDENT4 "GestureMode: spots\n");
break;
default:
assert(false);
}
switch (mParameters.deviceType) {
case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
dump.append(INDENT4 "DeviceType: touchScreen\n");
break;
case Parameters::DEVICE_TYPE_TOUCH_PAD:
dump.append(INDENT4 "DeviceType: touchPad\n");
break;
case Parameters::DEVICE_TYPE_POINTER:
dump.append(INDENT4 "DeviceType: pointer\n");
break;
default:
assert(false);
}
dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
mParameters.associatedDisplayId);
dump.appendFormat(INDENT4 "OrientationAware: %s\n",
toString(mParameters.orientationAware));
dump.appendFormat(INDENT4 "UseBadTouchFilter: %s\n",
toString(mParameters.useBadTouchFilter));
dump.appendFormat(INDENT4 "UseAveragingTouchFilter: %s\n",
toString(mParameters.useAveragingTouchFilter));
dump.appendFormat(INDENT4 "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();
}
void TouchInputMapper::dumpRawAxes(String8& dump) {
dump.append(INDENT3 "Raw Axes:\n");
dumpRawAbsoluteAxisInfo(dump, mRawAxes.x, "X");
dumpRawAbsoluteAxisInfo(dump, mRawAxes.y, "Y");
dumpRawAbsoluteAxisInfo(dump, mRawAxes.pressure, "Pressure");
dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor");
dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor");
dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor");
dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor");
dumpRawAbsoluteAxisInfo(dump, mRawAxes.orientation, "Orientation");
}
bool TouchInputMapper::configureSurfaceLocked() {
// Ensure we have valid X and Y axes.
if (!mRawAxes.x.valid || !mRawAxes.y.valid) {
LOGW(INDENT "Touch device '%s' did not report support for X or Y axis! "
"The device will be inoperable.", getDeviceName().string());
return false;
}
// Update orientation and dimensions if needed.
int32_t orientation = DISPLAY_ORIENTATION_0;
int32_t width = mRawAxes.x.maxValue - mRawAxes.x.minValue + 1;
int32_t height = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1;
if (mParameters.associatedDisplayId >= 0) {
// Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
&mLocked.associatedDisplayWidth, &mLocked.associatedDisplayHeight,
&mLocked.associatedDisplayOrientation)) {
return false;
}
// A touch screen inherits the dimensions of the display.
if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
width = mLocked.associatedDisplayWidth;
height = mLocked.associatedDisplayHeight;
}
// The device inherits the orientation of the display if it is orientation aware.
if (mParameters.orientationAware) {
orientation = mLocked.associatedDisplayOrientation;
}
}
if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER
&& mPointerController == NULL) {
mPointerController = getPolicy()->obtainPointerController(getDeviceId());
}
bool orientationChanged = mLocked.surfaceOrientation != orientation;
if (orientationChanged) {
mLocked.surfaceOrientation = orientation;
}
bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
if (sizeChanged) {
LOGI("Device reconfigured: id=%d, name='%s', surface size is now %dx%d",
getDeviceId(), getDeviceName().string(), width, height);
mLocked.surfaceWidth = width;
mLocked.surfaceHeight = height;
// Configure X and Y factors.
mLocked.xScale = float(width) / (mRawAxes.x.maxValue - mRawAxes.x.minValue + 1);
mLocked.yScale = float(height) / (mRawAxes.y.maxValue - mRawAxes.y.minValue + 1);
mLocked.xPrecision = 1.0f / mLocked.xScale;
mLocked.yPrecision = 1.0f / mLocked.yScale;
mLocked.orientedRanges.x.axis = AMOTION_EVENT_AXIS_X;
mLocked.orientedRanges.x.source = mTouchSource;
mLocked.orientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
mLocked.orientedRanges.y.source = mTouchSource;
configureVirtualKeysLocked();
// 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 = hypotf(width, height);
// TouchMajor and TouchMinor factors.
if (mCalibration.touchSizeCalibration != Calibration::TOUCH_SIZE_CALIBRATION_NONE) {
mLocked.orientedRanges.haveTouchSize = true;
mLocked.orientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR;
mLocked.orientedRanges.touchMajor.source = mTouchSource;
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;
mLocked.orientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
}
// 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.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR;
mLocked.orientedRanges.toolMajor.source = mTouchSource;
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;
mLocked.orientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
}
// 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.axis = AMOTION_EVENT_AXIS_PRESSURE;
mLocked.orientedRanges.pressure.source = mTouchSource;
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.axis = AMOTION_EVENT_AXIS_SIZE;
mLocked.orientedRanges.size.source = mTouchSource;
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.haveOrientation = true;
mLocked.orientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
mLocked.orientedRanges.orientation.source = mTouchSource;
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, scales and ranges.
// Note that the maximum value reported is an inclusive maximum value so it is one
// unit less than the total width or height of surface.
switch (mLocked.surfaceOrientation) {
case DISPLAY_ORIENTATION_90:
case DISPLAY_ORIENTATION_270:
mLocked.orientedSurfaceWidth = mLocked.surfaceHeight;
mLocked.orientedSurfaceHeight = mLocked.surfaceWidth;
mLocked.orientedXPrecision = mLocked.yPrecision;
mLocked.orientedYPrecision = mLocked.xPrecision;
mLocked.orientedRanges.x.min = 0;
mLocked.orientedRanges.x.max = (mRawAxes.y.maxValue - mRawAxes.y.minValue)
* mLocked.yScale;
mLocked.orientedRanges.x.flat = 0;
mLocked.orientedRanges.x.fuzz = mLocked.yScale;
mLocked.orientedRanges.y.min = 0;
mLocked.orientedRanges.y.max = (mRawAxes.x.maxValue - mRawAxes.x.minValue)
* mLocked.xScale;
mLocked.orientedRanges.y.flat = 0;
mLocked.orientedRanges.y.fuzz = mLocked.xScale;
break;
default:
mLocked.orientedSurfaceWidth = mLocked.surfaceWidth;
mLocked.orientedSurfaceHeight = mLocked.surfaceHeight;
mLocked.orientedXPrecision = mLocked.xPrecision;
mLocked.orientedYPrecision = mLocked.yPrecision;
mLocked.orientedRanges.x.min = 0;
mLocked.orientedRanges.x.max = (mRawAxes.x.maxValue - mRawAxes.x.minValue)
* mLocked.xScale;
mLocked.orientedRanges.x.flat = 0;
mLocked.orientedRanges.x.fuzz = mLocked.xScale;
mLocked.orientedRanges.y.min = 0;
mLocked.orientedRanges.y.max = (mRawAxes.y.maxValue - mRawAxes.y.minValue)
* mLocked.yScale;
mLocked.orientedRanges.y.flat = 0;
mLocked.orientedRanges.y.fuzz = mLocked.yScale;
break;
}
// Compute pointer gesture detection parameters.
// TODO: These factors should not be hardcoded.
if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
int32_t rawWidth = mRawAxes.x.maxValue - mRawAxes.x.minValue + 1;
int32_t rawHeight = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1;
float rawDiagonal = hypotf(rawWidth, rawHeight);
float displayDiagonal = hypotf(mLocked.associatedDisplayWidth,
mLocked.associatedDisplayHeight);
// Scale movements such that one whole swipe of the touch pad covers a
// given area relative to the diagonal size of the display when no acceleration
// is applied.
// Assume that the touch pad has a square aspect ratio such that movements in
// X and Y of the same number of raw units cover the same physical distance.
mLocked.pointerGestureXMovementScale = mConfig->pointerGestureMovementSpeedRatio
* displayDiagonal / rawDiagonal;
mLocked.pointerGestureYMovementScale = mLocked.pointerGestureXMovementScale;
// Scale zooms to cover a smaller range of the display than movements do.
// This value determines the area around the pointer that is affected by freeform
// pointer gestures.
mLocked.pointerGestureXZoomScale = mConfig->pointerGestureZoomSpeedRatio
* displayDiagonal / rawDiagonal;
mLocked.pointerGestureYZoomScale = mLocked.pointerGestureXZoomScale;
// Max width between pointers to detect a swipe gesture is more than some fraction
// of the diagonal axis of the touch pad. Touches that are wider than this are
// translated into freeform gestures.
mLocked.pointerGestureMaxSwipeWidth =
mConfig->pointerGestureSwipeMaxWidthRatio * rawDiagonal;
// Reset the current pointer gesture.
mPointerGesture.reset();
// Remove any current spots.
if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
mPointerController->clearSpots();
}
}
}
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() {
Vector<VirtualKeyDefinition> virtualKeyDefinitions;
getEventHub()->getVirtualKeyDefinitions(getDeviceId(), 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.maxValue - mRawAxes.x.minValue + 1;
int32_t touchScreenHeight = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1;
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()->mapKey(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 PropertyMap& in = getDevice()->getConfiguration();
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 == "vector") {
out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR;
} 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: %s\n",
toString(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;
case Calibration::ORIENTATION_CALIBRATION_VECTOR:
dump.append(INDENT4 "touch.orientation.calibration: vector\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();
if (mPointerController != NULL
&& mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
mPointerController->clearSpots();
}
} // release lock
InputMapper::reset();
}
void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
#if DEBUG_RAW_EVENTS
if (!havePointerIds) {
LOGD("syncTouch: pointerCount=%d, no pointer ids", mCurrentTouch.pointerCount);
} else {
LOGD("syncTouch: pointerCount=%d, up=0x%08x, down=0x%08x, move=0x%08x, "
"last=0x%08x, current=0x%08x", mCurrentTouch.pointerCount,
mLastTouch.idBits.value & ~mCurrentTouch.idBits.value,
mCurrentTouch.idBits.value & ~mLastTouch.idBits.value,
mLastTouch.idBits.value & mCurrentTouch.idBits.value,
mLastTouch.idBits.value, mCurrentTouch.idBits.value);
}
#endif
// 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;
}
uint32_t policyFlags = 0;
if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) {
if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
// If this is a touch screen, hide the pointer on an initial down.
getContext()->fadePointer();
}
// Initial downs on external touch devices should wake the device.
// We don't do this for internal touch screens to prevent them from waking
// up in your pocket.
// TODO: Use the input device configuration to control this behavior more finely.
if (getDevice()->isExternal()) {
policyFlags |= POLICY_FLAG_WAKE_DROPPED;
}
}
TouchResult touchResult;
if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount == 0
&& mLastTouch.buttonState == mCurrentTouch.buttonState) {
// Drop spurious syncs.
touchResult = DROP_STROKE;
} else {
// Process touches and virtual keys.
touchResult = consumeOffScreenTouches(when, policyFlags);
if (touchResult == DISPATCH_TOUCH) {
suppressSwipeOntoVirtualKeys(when);
if (mPointerController != NULL) {
dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
}
dispatchTouches(when, policyFlags);
}
}
// Copy current touch to last touch in preparation for the next cycle.
// Keep the button state so we can track edge-triggered button state changes.
if (touchResult == DROP_STROKE) {
mLastTouch.clear();
mLastTouch.buttonState = savedTouch->buttonState;
} else {
mLastTouch.copyFrom(*savedTouch);
}
}
void TouchInputMapper::timeoutExpired(nsecs_t when) {
if (mPointerController != NULL) {
dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
}
}
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()) {