mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-27 21:00:50 +00:00
Bug 908797 - Update libui to the latest input code from JB MR2, r=m1
This commit is contained in:
parent
4527beabfe
commit
77f40ba664
@ -28,6 +28,7 @@ DEFINES += -DHAVE_OFF64_T -DSK_BUILD_FOR_ANDROID_NDK
|
||||
LOCAL_INCLUDES += \
|
||||
-I$(ANDROID_SOURCE)/hardware/libhardware/include \
|
||||
-I$(ANDROID_SOURCE)/hardware/libhardware_legacy/include \
|
||||
-I$(ANDROID_SOURCE)/frameworks/native/opengl/include \
|
||||
-I$(topsrcdir)/widget/xpwidgets \
|
||||
-I$(topsrcdir)/widget/shared \
|
||||
-I$(topsrcdir)/dom/system/android \
|
||||
|
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef UTILS_BITSET_H
|
||||
#define UTILS_BITSET_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Contains some bit manipulation helpers.
|
||||
*/
|
||||
|
||||
namespace android {
|
||||
|
||||
// A simple set of 32 bits that can be individually marked or cleared.
|
||||
struct BitSet32 {
|
||||
uint32_t value;
|
||||
|
||||
inline BitSet32() : value(0) { }
|
||||
explicit inline BitSet32(uint32_t value) : value(value) { }
|
||||
|
||||
// Gets the value associated with a particular bit index.
|
||||
static inline uint32_t valueForBit(uint32_t n) { return 0x80000000 >> n; }
|
||||
|
||||
// Clears the bit set.
|
||||
inline void clear() { value = 0; }
|
||||
|
||||
// Returns the number of marked bits in the set.
|
||||
inline uint32_t count() const { return __builtin_popcount(value); }
|
||||
|
||||
// Returns true if the bit set does not contain any marked bits.
|
||||
inline bool isEmpty() const { return ! value; }
|
||||
|
||||
// Returns true if the bit set does not contain any unmarked bits.
|
||||
inline bool isFull() const { return value == 0xffffffff; }
|
||||
|
||||
// Returns true if the specified bit is marked.
|
||||
inline bool hasBit(uint32_t n) const { return value & valueForBit(n); }
|
||||
|
||||
// Marks the specified bit.
|
||||
inline void markBit(uint32_t n) { value |= valueForBit(n); }
|
||||
|
||||
// Clears the specified bit.
|
||||
inline void clearBit(uint32_t n) { value &= ~ valueForBit(n); }
|
||||
|
||||
// Finds the first marked bit in the set.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t firstMarkedBit() const { return __builtin_clz(value); }
|
||||
|
||||
// Finds the first unmarked bit in the set.
|
||||
// Result is undefined if all bits are marked.
|
||||
inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); }
|
||||
|
||||
// Finds the last marked bit in the set.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); }
|
||||
|
||||
// Finds the first marked bit in the set and clears it. Returns the bit index.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t clearFirstMarkedBit() {
|
||||
uint32_t n = firstMarkedBit();
|
||||
clearBit(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Finds the first unmarked bit in the set and marks it. Returns the bit index.
|
||||
// Result is undefined if all bits are marked.
|
||||
inline uint32_t markFirstUnmarkedBit() {
|
||||
uint32_t n = firstUnmarkedBit();
|
||||
markBit(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Finds the last marked bit in the set and clears it. Returns the bit index.
|
||||
// Result is undefined if all bits are unmarked.
|
||||
inline uint32_t clearLastMarkedBit() {
|
||||
uint32_t n = lastMarkedBit();
|
||||
clearBit(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Gets the index of the specified bit in the set, which is the number of
|
||||
// marked bits that appear before the specified bit.
|
||||
inline uint32_t getIndexOfBit(uint32_t n) const {
|
||||
return __builtin_popcount(value & ~(0xffffffffUL >> n));
|
||||
}
|
||||
|
||||
inline bool operator== (const BitSet32& other) const { return value == other.value; }
|
||||
inline bool operator!= (const BitSet32& other) const { return value != other.value; }
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // UTILS_BITSET_H
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ANDROID_UI_DISPLAY_INFO_H
|
||||
#define ANDROID_UI_DISPLAY_INFO_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "PixelFormat.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
struct DisplayInfo {
|
||||
uint32_t w;
|
||||
uint32_t h;
|
||||
PixelFormatInfo pixelFormatInfo;
|
||||
uint8_t orientation;
|
||||
uint8_t reserved[3];
|
||||
float fps;
|
||||
float density;
|
||||
float xdpi;
|
||||
float ydpi;
|
||||
};
|
||||
|
||||
/* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
|
||||
enum {
|
||||
DISPLAY_ORIENTATION_0 = 0,
|
||||
DISPLAY_ORIENTATION_90 = 1,
|
||||
DISPLAY_ORIENTATION_180 = 2,
|
||||
DISPLAY_ORIENTATION_270 = 3
|
||||
};
|
||||
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_COMPOSER_DISPLAY_INFO_H
|
||||
|
@ -17,14 +17,15 @@
|
||||
#define LOG_TAG "EventHub"
|
||||
|
||||
// #define LOG_NDEBUG 0
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include "utils_Log.h"
|
||||
#include "EventHub.h"
|
||||
|
||||
#include <hardware_legacy/power.h>
|
||||
|
||||
#include <cutils/properties.h>
|
||||
#include "Timers.h"
|
||||
#include "cutils_log.h"
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/threads.h>
|
||||
#include <utils/Errors.h>
|
||||
|
||||
@ -48,6 +49,7 @@
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/limits.h>
|
||||
#include <sha1.h>
|
||||
|
||||
/* this macro is used to tell if "bit" is set in "array"
|
||||
* it selects a byte from the array, and does a boolean AND
|
||||
@ -78,6 +80,48 @@ static inline const char* toString(bool value) {
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
static String8 sha1(const String8& in) {
|
||||
SHA1_CTX ctx;
|
||||
SHA1Init(&ctx);
|
||||
SHA1Update(&ctx, reinterpret_cast<const u_char*>(in.string()), in.size());
|
||||
u_char digest[SHA1_DIGEST_LENGTH];
|
||||
SHA1Final(digest, &ctx);
|
||||
|
||||
String8 out;
|
||||
for (size_t i = 0; i < SHA1_DIGEST_LENGTH; i++) {
|
||||
out.appendFormat("%02x", digest[i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void setDescriptor(InputDeviceIdentifier& identifier) {
|
||||
// Compute a device descriptor that uniquely identifies the device.
|
||||
// The descriptor is assumed to be a stable identifier. Its value should not
|
||||
// change between reboots, reconnections, firmware updates or new releases of Android.
|
||||
// Ideally, we also want the descriptor to be short and relatively opaque.
|
||||
String8 rawDescriptor;
|
||||
rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor, identifier.product);
|
||||
if (!identifier.uniqueId.isEmpty()) {
|
||||
rawDescriptor.append("uniqueId:");
|
||||
rawDescriptor.append(identifier.uniqueId);
|
||||
} if (identifier.vendor == 0 && identifier.product == 0) {
|
||||
// If we don't know the vendor and product id, then the device is probably
|
||||
// built-in so we need to rely on other information to uniquely identify
|
||||
// the input device. Usually we try to avoid relying on the device name or
|
||||
// location but for built-in input device, they are unlikely to ever change.
|
||||
if (!identifier.name.isEmpty()) {
|
||||
rawDescriptor.append("name:");
|
||||
rawDescriptor.append(identifier.name);
|
||||
} else if (!identifier.location.isEmpty()) {
|
||||
rawDescriptor.append("location:");
|
||||
rawDescriptor.append(identifier.location);
|
||||
}
|
||||
}
|
||||
identifier.descriptor = sha1(rawDescriptor);
|
||||
ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.string(),
|
||||
identifier.descriptor.string());
|
||||
}
|
||||
|
||||
// --- Global Functions ---
|
||||
|
||||
uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
|
||||
@ -118,12 +162,15 @@ EventHub::Device::Device(int fd, int32_t id, const String8& path,
|
||||
const InputDeviceIdentifier& identifier) :
|
||||
next(NULL),
|
||||
fd(fd), id(id), path(path), identifier(identifier),
|
||||
classes(0), configuration(NULL), virtualKeyMap(NULL) {
|
||||
classes(0), configuration(NULL), virtualKeyMap(NULL),
|
||||
ffEffectPlaying(false), ffEffectId(-1),
|
||||
timestampOverrideSec(0), timestampOverrideUsec(0) {
|
||||
memset(keyBitmask, 0, sizeof(keyBitmask));
|
||||
memset(absBitmask, 0, sizeof(absBitmask));
|
||||
memset(relBitmask, 0, sizeof(relBitmask));
|
||||
memset(swBitmask, 0, sizeof(swBitmask));
|
||||
memset(ledBitmask, 0, sizeof(ledBitmask));
|
||||
memset(ffBitmask, 0, sizeof(ffBitmask));
|
||||
memset(propBitmask, 0, sizeof(propBitmask));
|
||||
}
|
||||
|
||||
@ -149,15 +196,13 @@ const int EventHub::EPOLL_SIZE_HINT;
|
||||
const int EventHub::EPOLL_MAX_EVENTS;
|
||||
|
||||
EventHub::EventHub(void) :
|
||||
mBuiltInKeyboardId(-1), mNextDeviceId(1),
|
||||
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1),
|
||||
mOpeningDevices(0), mClosingDevices(0),
|
||||
mNeedToSendFinishedDeviceScan(false),
|
||||
mNeedToReopenDevices(false), mNeedToScanDevices(true),
|
||||
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
|
||||
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
|
||||
|
||||
mNumCpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
|
||||
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
|
||||
|
||||
@ -211,11 +256,11 @@ EventHub::~EventHub(void) {
|
||||
release_wake_lock(WAKE_LOCK_ID);
|
||||
}
|
||||
|
||||
String8 EventHub::getDeviceName(int32_t deviceId) const {
|
||||
InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device == NULL) return String8();
|
||||
return device->identifier.name;
|
||||
if (device == NULL) return InputDeviceIdentifier();
|
||||
return device->identifier;
|
||||
}
|
||||
|
||||
uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
|
||||
@ -243,7 +288,7 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device && test_bit(axis, device->absBitmask)) {
|
||||
if (device && !device->isVirtual() && test_bit(axis, device->absBitmask)) {
|
||||
struct input_absinfo info;
|
||||
if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
|
||||
ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
|
||||
@ -294,7 +339,7 @@ int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device && test_bit(scanCode, device->keyBitmask)) {
|
||||
if (device && !device->isVirtual() && test_bit(scanCode, device->keyBitmask)) {
|
||||
uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
|
||||
memset(keyState, 0, sizeof(keyState));
|
||||
if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
|
||||
@ -309,7 +354,7 @@ int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device && device->keyMap.haveKeyLayout()) {
|
||||
if (device && !device->isVirtual() && device->keyMap.haveKeyLayout()) {
|
||||
Vector<int32_t> scanCodes;
|
||||
device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes);
|
||||
if (scanCodes.size() != 0) {
|
||||
@ -334,7 +379,7 @@ int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device && test_bit(sw, device->swBitmask)) {
|
||||
if (device && !device->isVirtual() && test_bit(sw, device->swBitmask)) {
|
||||
uint8_t swState[sizeof_bit_array(SW_MAX + 1)];
|
||||
memset(swState, 0, sizeof(swState));
|
||||
if (ioctl(device->fd, EVIOCGSW(sizeof(swState)), swState) >= 0) {
|
||||
@ -352,7 +397,7 @@ status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t*
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device && test_bit(axis, device->absBitmask)) {
|
||||
if (device && !device->isVirtual() && test_bit(axis, device->absBitmask)) {
|
||||
struct input_absinfo info;
|
||||
if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
|
||||
ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
|
||||
@ -395,58 +440,46 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
|
||||
return false;
|
||||
}
|
||||
|
||||
status_t EventHub::mapKey(int32_t deviceId, int scancode,
|
||||
int32_t* outKeycode, uint32_t* outFlags) const
|
||||
{
|
||||
status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
|
||||
int32_t* outKeycode, uint32_t* outFlags) const {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
|
||||
if (device && device->keyMap.haveKeyLayout()) {
|
||||
status_t err = device->keyMap.keyLayoutMap->mapKey(scancode, outKeycode, outFlags);
|
||||
if (err == NO_ERROR) {
|
||||
return NO_ERROR;
|
||||
|
||||
if (device) {
|
||||
// Check the key character map first.
|
||||
sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
|
||||
if (kcm != NULL) {
|
||||
if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
|
||||
*outFlags = 0;
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mBuiltInKeyboardId != -1) {
|
||||
device = getDeviceLocked(mBuiltInKeyboardId);
|
||||
|
||||
if (device && device->keyMap.haveKeyLayout()) {
|
||||
status_t err = device->keyMap.keyLayoutMap->mapKey(scancode, outKeycode, outFlags);
|
||||
if (err == NO_ERROR) {
|
||||
|
||||
// Check the key layout next.
|
||||
if (device->keyMap.haveKeyLayout()) {
|
||||
if (!device->keyMap.keyLayoutMap->mapKey(
|
||||
scanCode, usageCode, outKeycode, outFlags)) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*outKeycode = 0;
|
||||
*outFlags = 0;
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
status_t EventHub::mapAxis(int32_t deviceId, int scancode, AxisInfo* outAxisInfo) const
|
||||
{
|
||||
status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
|
||||
if (device && device->keyMap.haveKeyLayout()) {
|
||||
status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxisInfo);
|
||||
status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo);
|
||||
if (err == NO_ERROR) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (mBuiltInKeyboardId != -1) {
|
||||
device = getDeviceLocked(mBuiltInKeyboardId);
|
||||
|
||||
if (device && device->keyMap.haveKeyLayout()) {
|
||||
status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxisInfo);
|
||||
if (err == NO_ERROR) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
@ -481,7 +514,7 @@ bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
|
||||
void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device && led >= 0 && led <= LED_MAX) {
|
||||
if (device && !device->isVirtual() && led >= 0 && led <= LED_MAX) {
|
||||
struct input_event ev;
|
||||
ev.time.tv_sec = 0;
|
||||
ev.time.tv_usec = 0;
|
||||
@ -507,17 +540,88 @@ void EventHub::getVirtualKeyDefinitions(int32_t deviceId,
|
||||
}
|
||||
}
|
||||
|
||||
String8 EventHub::getKeyCharacterMapFile(int32_t deviceId) const {
|
||||
sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device) {
|
||||
return device->keyMap.keyCharacterMapFile;
|
||||
return device->getKeyCharacterMap();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId,
|
||||
const sp<KeyCharacterMap>& map) {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device) {
|
||||
if (map != device->overlayKeyMap) {
|
||||
device->overlayKeyMap = map;
|
||||
device->combinedKeyMap = KeyCharacterMap::combine(
|
||||
device->keyMap.keyCharacterMap, map);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device && !device->isVirtual()) {
|
||||
ff_effect effect;
|
||||
memset(&effect, 0, sizeof(effect));
|
||||
effect.type = FF_RUMBLE;
|
||||
effect.id = device->ffEffectId;
|
||||
effect.u.rumble.strong_magnitude = 0xc000;
|
||||
effect.u.rumble.weak_magnitude = 0xc000;
|
||||
effect.replay.length = (duration + 999999LL) / 1000000LL;
|
||||
effect.replay.delay = 0;
|
||||
if (ioctl(device->fd, EVIOCSFF, &effect)) {
|
||||
ALOGW("Could not upload force feedback effect to device %s due to error %d.",
|
||||
device->identifier.name.string(), errno);
|
||||
return;
|
||||
}
|
||||
device->ffEffectId = effect.id;
|
||||
|
||||
struct input_event ev;
|
||||
ev.time.tv_sec = 0;
|
||||
ev.time.tv_usec = 0;
|
||||
ev.type = EV_FF;
|
||||
ev.code = device->ffEffectId;
|
||||
ev.value = 1;
|
||||
if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
|
||||
ALOGW("Could not start force feedback effect on device %s due to error %d.",
|
||||
device->identifier.name.string(), errno);
|
||||
return;
|
||||
}
|
||||
device->ffEffectPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
void EventHub::cancelVibrate(int32_t deviceId) {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device && !device->isVirtual()) {
|
||||
if (device->ffEffectPlaying) {
|
||||
device->ffEffectPlaying = false;
|
||||
|
||||
struct input_event ev;
|
||||
ev.time.tv_sec = 0;
|
||||
ev.time.tv_usec = 0;
|
||||
ev.type = EV_FF;
|
||||
ev.code = device->ffEffectId;
|
||||
ev.value = 0;
|
||||
if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
|
||||
ALOGW("Could not stop force feedback effect on device %s due to error %d.",
|
||||
device->identifier.name.string(), errno);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return String8();
|
||||
}
|
||||
|
||||
EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
|
||||
if (deviceId == 0) {
|
||||
if (deviceId == BUILT_IN_KEYBOARD_ID) {
|
||||
deviceId = mBuiltInKeyboardId;
|
||||
}
|
||||
ssize_t index = mDevices.indexOfKey(deviceId);
|
||||
@ -565,7 +669,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
|
||||
device->id, device->path.string());
|
||||
mClosingDevices = device->next;
|
||||
event->when = now;
|
||||
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
|
||||
event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
|
||||
event->type = DEVICE_REMOVED;
|
||||
event += 1;
|
||||
delete device;
|
||||
@ -648,8 +752,9 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
|
||||
sizeof(struct input_event) * capacity);
|
||||
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
|
||||
// Device was removed before INotify noticed.
|
||||
ALOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d capacity: %d errno: %d)\n",
|
||||
device->fd, readSize, bufferSize, capacity, errno);
|
||||
ALOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d "
|
||||
"capacity: %d errno: %d)\n",
|
||||
device->fd, readSize, bufferSize, capacity, errno);
|
||||
deviceChanged = true;
|
||||
closeDeviceLocked(device);
|
||||
} else if (readSize < 0) {
|
||||
@ -663,12 +768,37 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
|
||||
|
||||
size_t count = size_t(readSize) / sizeof(struct input_event);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
const struct input_event& iev = readBuffer[i];
|
||||
ALOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",
|
||||
struct input_event& iev = readBuffer[i];
|
||||
ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
|
||||
device->path.string(),
|
||||
(int) iev.time.tv_sec, (int) iev.time.tv_usec,
|
||||
iev.type, iev.code, iev.value);
|
||||
|
||||
// Some input devices may have a better concept of the time
|
||||
// when an input event was actually generated than the kernel
|
||||
// which simply timestamps all events on entry to evdev.
|
||||
// This is a custom Android extension of the input protocol
|
||||
// mainly intended for use with uinput based device drivers.
|
||||
if (iev.type == EV_MSC) {
|
||||
if (iev.code == MSC_ANDROID_TIME_SEC) {
|
||||
device->timestampOverrideSec = iev.value;
|
||||
continue;
|
||||
} else if (iev.code == MSC_ANDROID_TIME_USEC) {
|
||||
device->timestampOverrideUsec = iev.value;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (device->timestampOverrideSec || device->timestampOverrideUsec) {
|
||||
iev.time.tv_sec = device->timestampOverrideSec;
|
||||
iev.time.tv_usec = device->timestampOverrideUsec;
|
||||
if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
|
||||
device->timestampOverrideSec = 0;
|
||||
device->timestampOverrideUsec = 0;
|
||||
}
|
||||
ALOGV("applied override time %d.%06d",
|
||||
int(iev.time.tv_sec), int(iev.time.tv_usec));
|
||||
}
|
||||
|
||||
#ifdef HAVE_POSIX_CLOCKS
|
||||
// Use the time specified in the event instead of the current time
|
||||
// so that downstream code can get more accurate estimates of
|
||||
@ -684,24 +814,50 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
|
||||
event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
|
||||
+ nsecs_t(iev.time.tv_usec) * 1000LL;
|
||||
ALOGV("event time %lld, now %lld", event->when, now);
|
||||
|
||||
// Bug 7291243: Add a guard in case the kernel generates timestamps
|
||||
// that appear to be far into the future because they were generated
|
||||
// using the wrong clock source.
|
||||
//
|
||||
// This can happen because when the input device is initially opened
|
||||
// it has a default clock source of CLOCK_REALTIME. Any input events
|
||||
// enqueued right after the device is opened will have timestamps
|
||||
// generated using CLOCK_REALTIME. We later set the clock source
|
||||
// to CLOCK_MONOTONIC but it is already too late.
|
||||
//
|
||||
// Invalid input event timestamps can result in ANRs, crashes and
|
||||
// and other issues that are hard to track down. We must not let them
|
||||
// propagate through the system.
|
||||
//
|
||||
// Log a warning so that we notice the problem and recover gracefully.
|
||||
if (event->when >= now + 10 * 1000000000LL) {
|
||||
// Double-check. Time may have moved on.
|
||||
nsecs_t time = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
if (event->when > time) {
|
||||
ALOGW("An input event from %s has a timestamp that appears to "
|
||||
"have been generated using the wrong clock source "
|
||||
"(expected CLOCK_MONOTONIC): "
|
||||
"event time %lld, current time %lld, call time %lld. "
|
||||
"Using current time instead.",
|
||||
device->path.string(), event->when, time, now);
|
||||
event->when = time;
|
||||
} else {
|
||||
ALOGV("Event time is ok but failed the fast path and required "
|
||||
"an extra call to systemTime: "
|
||||
"event time %lld, current time %lld, call time %lld.",
|
||||
event->when, time, now);
|
||||
}
|
||||
}
|
||||
#else
|
||||
event->when = now;
|
||||
#endif
|
||||
event->deviceId = deviceId;
|
||||
event->type = iev.type;
|
||||
event->scanCode = iev.code;
|
||||
event->code = iev.code;
|
||||
event->value = iev.value;
|
||||
event->keyCode = AKEYCODE_UNKNOWN;
|
||||
event->flags = 0;
|
||||
if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {
|
||||
status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
|
||||
&event->keyCode, &event->flags);
|
||||
ALOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
|
||||
iev.code, event->keyCode, event->flags, err);
|
||||
}
|
||||
event += 1;
|
||||
capacity -= 1;
|
||||
}
|
||||
capacity -= count;
|
||||
if (capacity == 0) {
|
||||
// The result buffer is full. Reset the pending event index
|
||||
// so we will try to read the device again on the next iteration.
|
||||
@ -709,6 +865,11 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (eventItem.events & EPOLLHUP) {
|
||||
ALOGI("Removing device %s due to epoll hang-up event.",
|
||||
device->identifier.name.string());
|
||||
deviceChanged = true;
|
||||
closeDeviceLocked(device);
|
||||
} else {
|
||||
ALOGW("Received unexpected epoll event 0x%08x for device %s.",
|
||||
eventItem.events, device->identifier.name.string());
|
||||
@ -774,19 +935,6 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
|
||||
} else {
|
||||
// Some events occurred.
|
||||
mPendingEventCount = size_t(pollResult);
|
||||
|
||||
// On an SMP system, it is possible for the framework to read input events
|
||||
// faster than the kernel input device driver can produce a complete packet.
|
||||
// Because poll() wakes up as soon as the first input event becomes available,
|
||||
// the framework will often end up reading one event at a time until the
|
||||
// packet is complete. Instead of one call to read() returning 71 events,
|
||||
// it could take 71 calls to read() each returning 1 event.
|
||||
//
|
||||
// Sleep for a short period of time after waking up from the poll() to give
|
||||
// the kernel time to finish writing the entire packet of input events.
|
||||
if (mNumCpus > 1) {
|
||||
usleep(250);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -812,6 +960,9 @@ void EventHub::scanDevicesLocked() {
|
||||
if(res < 0) {
|
||||
ALOGE("scan dir failed for %s\n", DEVICE_PATH);
|
||||
}
|
||||
if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
|
||||
createVirtualKeyboardLocked();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -845,7 +996,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
|
||||
|
||||
ALOGV("Opening device: %s", devicePath);
|
||||
|
||||
int fd = open(devicePath, O_RDWR);
|
||||
int fd = open(devicePath, O_RDWR | O_CLOEXEC);
|
||||
if(fd < 0) {
|
||||
ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
|
||||
return -1;
|
||||
@ -907,6 +1058,9 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
|
||||
identifier.uniqueId.setTo(buffer);
|
||||
}
|
||||
|
||||
// Fill in the descriptor.
|
||||
setDescriptor(identifier);
|
||||
|
||||
// Make file descriptor non-blocking for use with poll().
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
|
||||
ALOGE("Error %d making device file descriptor non-blocking.", errno);
|
||||
@ -918,19 +1072,18 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
|
||||
int32_t deviceId = mNextDeviceId++;
|
||||
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
|
||||
|
||||
#if 0
|
||||
ALOGI("add device %d: %s\n", deviceId, devicePath);
|
||||
ALOGI(" bus: %04x\n"
|
||||
" vendor %04x\n"
|
||||
" product %04x\n"
|
||||
" version %04x\n",
|
||||
ALOGV("add device %d: %s\n", deviceId, devicePath);
|
||||
ALOGV(" bus: %04x\n"
|
||||
" vendor %04x\n"
|
||||
" product %04x\n"
|
||||
" version %04x\n",
|
||||
identifier.bus, identifier.vendor, identifier.product, identifier.version);
|
||||
ALOGI(" name: \"%s\"\n", identifier.name.string());
|
||||
ALOGI(" location: \"%s\"\n", identifier.location.string());
|
||||
ALOGI(" unique id: \"%s\"\n", identifier.uniqueId.string());
|
||||
ALOGI(" driver: v%d.%d.%d\n",
|
||||
ALOGV(" name: \"%s\"\n", identifier.name.string());
|
||||
ALOGV(" location: \"%s\"\n", identifier.location.string());
|
||||
ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.string());
|
||||
ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.string());
|
||||
ALOGV(" driver: v%d.%d.%d\n",
|
||||
driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
|
||||
#endif
|
||||
|
||||
// Load the configuration file for the device.
|
||||
loadConfigurationLocked(device);
|
||||
@ -941,6 +1094,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
|
||||
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
|
||||
ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
|
||||
ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
|
||||
ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
|
||||
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
|
||||
|
||||
// See if this is a keyboard. Ignore everything in the button range except for
|
||||
@ -970,10 +1124,9 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
|
||||
// Some joysticks such as the PS3 controller report axes that conflict
|
||||
// with the ABS_MT range. Try to confirm that the device really is
|
||||
// a touch screen.
|
||||
// Mozilla Bug 741038 - support GB touchscreen drivers
|
||||
//if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
|
||||
if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
|
||||
device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
|
||||
//}
|
||||
}
|
||||
// Is this an old style single-touch driver?
|
||||
} else if (test_bit(BTN_TOUCH, device->keyBitmask)
|
||||
&& test_bit(ABS_X, device->absBitmask)
|
||||
@ -1003,6 +1156,11 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether this device supports the vibrator.
|
||||
if (test_bit(FF_RUMBLE, device->ffBitmask)) {
|
||||
device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
|
||||
}
|
||||
|
||||
// Configure virtual keys.
|
||||
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
|
||||
// Load the virtual keys for the touch screen, if any.
|
||||
@ -1025,7 +1183,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
|
||||
if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
|
||||
// Register the keyboard as a built-in keyboard if it is eligible.
|
||||
if (!keyMapStatus
|
||||
&& mBuiltInKeyboardId == -1
|
||||
&& mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD
|
||||
&& isEligibleBuiltInKeyboard(device->identifier,
|
||||
device->configuration, &device->keyMap)) {
|
||||
mBuiltInKeyboardId = device->id;
|
||||
@ -1052,6 +1210,12 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable kernel key repeat since we handle it ourselves
|
||||
unsigned int repeatRate[] = {0,0};
|
||||
if (ioctl(fd, EVIOCSREP, repeatRate)) {
|
||||
ALOGW("Unable to disable kernel key repeat for %s: %s", devicePath, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
// If the device isn't recognized as something we handle, don't monitor it.
|
||||
@ -1078,20 +1242,62 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Enable wake-lock behavior on kernels that support it.
|
||||
// TODO: Only need this for devices that can really wake the system.
|
||||
bool usingSuspendBlockIoctl = !ioctl(fd, EVIOCSSUSPENDBLOCK, 1);
|
||||
|
||||
// Tell the kernel that we want to use the monotonic clock for reporting timestamps
|
||||
// associated with input events. This is important because the input system
|
||||
// uses the timestamps extensively and assumes they were recorded using the monotonic
|
||||
// clock.
|
||||
//
|
||||
// In older kernel, before Linux 3.4, there was no way to tell the kernel which
|
||||
// clock to use to input event timestamps. The standard kernel behavior was to
|
||||
// record a real time timestamp, which isn't what we want. Android kernels therefore
|
||||
// contained a patch to the evdev_event() function in drivers/input/evdev.c to
|
||||
// replace the call to do_gettimeofday() with ktime_get_ts() to cause the monotonic
|
||||
// clock to be used instead of the real time clock.
|
||||
//
|
||||
// As of Linux 3.4, there is a new EVIOCSCLOCKID ioctl to set the desired clock.
|
||||
// Therefore, we no longer require the Android-specific kernel patch described above
|
||||
// as long as we make sure to set select the monotonic clock. We do that here.
|
||||
int clockId = CLOCK_MONOTONIC;
|
||||
bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);
|
||||
|
||||
ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
|
||||
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s",
|
||||
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, "
|
||||
"usingSuspendBlockIoctl=%s, usingClockIoctl=%s",
|
||||
deviceId, fd, devicePath, device->identifier.name.string(),
|
||||
device->classes,
|
||||
device->configurationFile.string(),
|
||||
device->keyMap.keyLayoutFile.string(),
|
||||
device->keyMap.keyCharacterMapFile.string(),
|
||||
toString(mBuiltInKeyboardId == deviceId));
|
||||
toString(mBuiltInKeyboardId == deviceId),
|
||||
toString(usingSuspendBlockIoctl), toString(usingClockIoctl));
|
||||
|
||||
mDevices.add(deviceId, device);
|
||||
addDeviceLocked(device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EventHub::createVirtualKeyboardLocked() {
|
||||
InputDeviceIdentifier identifier;
|
||||
identifier.name = "Virtual";
|
||||
identifier.uniqueId = "<virtual>";
|
||||
setDescriptor(identifier);
|
||||
|
||||
Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, String8("<virtual>"), identifier);
|
||||
device->classes = INPUT_DEVICE_CLASS_KEYBOARD
|
||||
| INPUT_DEVICE_CLASS_ALPHAKEY
|
||||
| INPUT_DEVICE_CLASS_DPAD
|
||||
| INPUT_DEVICE_CLASS_VIRTUAL;
|
||||
loadKeyMapLocked(device);
|
||||
addDeviceLocked(device);
|
||||
}
|
||||
|
||||
void EventHub::addDeviceLocked(Device* device) {
|
||||
mDevices.add(device->id, device);
|
||||
device->next = mOpeningDevices;
|
||||
mOpeningDevices = device;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EventHub::loadConfigurationLocked(Device* device) {
|
||||
@ -1178,11 +1384,13 @@ void EventHub::closeDeviceLocked(Device* device) {
|
||||
if (device->id == mBuiltInKeyboardId) {
|
||||
ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
|
||||
device->path.string(), mBuiltInKeyboardId);
|
||||
mBuiltInKeyboardId = -1;
|
||||
mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
|
||||
}
|
||||
|
||||
if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) {
|
||||
ALOGW("Could not remove device fd from epoll instance. errno=%d", errno);
|
||||
if (!device->isVirtual()) {
|
||||
if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) {
|
||||
ALOGW("Could not remove device fd from epoll instance. errno=%d", errno);
|
||||
}
|
||||
}
|
||||
|
||||
mDevices.removeItem(device->id);
|
||||
@ -1312,6 +1520,7 @@ void EventHub::dump(String8& dump) {
|
||||
}
|
||||
dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
|
||||
dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
|
||||
dump.appendFormat(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.string());
|
||||
dump.appendFormat(INDENT3 "Location: %s\n", device->identifier.location.string());
|
||||
dump.appendFormat(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string());
|
||||
dump.appendFormat(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
|
||||
@ -1324,6 +1533,8 @@ void EventHub::dump(String8& dump) {
|
||||
device->keyMap.keyCharacterMapFile.string());
|
||||
dump.appendFormat(INDENT3 "ConfigurationFile: %s\n",
|
||||
device->configurationFile.string());
|
||||
dump.appendFormat(INDENT3 "HaveKeyboardLayoutOverlay: %s\n",
|
||||
toString(device->overlayKeyMap != NULL));
|
||||
}
|
||||
} // release lock
|
||||
}
|
||||
|
@ -18,18 +18,19 @@
|
||||
#ifndef _RUNTIME_EVENT_HUB_H
|
||||
#define _RUNTIME_EVENT_HUB_H
|
||||
|
||||
#include "utils_Log.h"
|
||||
#include "Input.h"
|
||||
#include "InputDevice.h"
|
||||
#include "Keyboard.h"
|
||||
#include "KeyLayoutMap.h"
|
||||
#include "KeyCharacterMap.h"
|
||||
#include "VirtualKeyMap.h"
|
||||
#include "String8.h"
|
||||
#include <utils/String8.h>
|
||||
#include <utils/threads.h>
|
||||
#include "cutils_log.h"
|
||||
#include <utils/threads.h>
|
||||
#include <utils/List.h>
|
||||
#include <utils/Errors.h>
|
||||
#include "PropertyMap.h"
|
||||
#include <utils/PropertyMap.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
|
||||
@ -38,11 +39,32 @@
|
||||
|
||||
/* Convenience constants. */
|
||||
|
||||
#define BTN_FIRST 0x100 // first button scancode
|
||||
#define BTN_LAST 0x15f // last button scancode
|
||||
#define BTN_FIRST 0x100 // first button code
|
||||
#define BTN_LAST 0x15f // last button code
|
||||
|
||||
/*
|
||||
* These constants are used privately in Android to pass raw timestamps
|
||||
* through evdev from uinput device drivers because there is currently no
|
||||
* other way to transfer this information. The evdev driver automatically
|
||||
* timestamps all input events with the time they were posted and clobbers
|
||||
* whatever information was passed in.
|
||||
*
|
||||
* For the purposes of this hack, the timestamp is specified in the
|
||||
* CLOCK_MONOTONIC timebase and is split into two EV_MSC events specifying
|
||||
* seconds and microseconds.
|
||||
*/
|
||||
#define MSC_ANDROID_TIME_SEC 0x6
|
||||
#define MSC_ANDROID_TIME_USEC 0x7
|
||||
|
||||
namespace android {
|
||||
|
||||
enum {
|
||||
// Device id of a special "virtual" keyboard that is always present.
|
||||
VIRTUAL_KEYBOARD_ID = -1,
|
||||
// Device id of the "built-in" keyboard if there is one.
|
||||
BUILT_IN_KEYBOARD_ID = 0,
|
||||
};
|
||||
|
||||
/*
|
||||
* A raw event as retrieved from the EventHub.
|
||||
*/
|
||||
@ -50,10 +72,8 @@ struct RawEvent {
|
||||
nsecs_t when;
|
||||
int32_t deviceId;
|
||||
int32_t type;
|
||||
int32_t scanCode;
|
||||
int32_t keyCode;
|
||||
int32_t code;
|
||||
int32_t value;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/* Describes an absolute axis. */
|
||||
@ -107,6 +127,12 @@ enum {
|
||||
/* The input device is a joystick (implies gamepad, has joystick absolute axes). */
|
||||
INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
|
||||
|
||||
/* The input device has a vibrator (supports FF_RUMBLE). */
|
||||
INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200,
|
||||
|
||||
/* The input device is virtual (not a real device, not part of UI configuration). */
|
||||
INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000,
|
||||
|
||||
/* The input device is external (not built-in). */
|
||||
INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000,
|
||||
};
|
||||
@ -151,7 +177,7 @@ public:
|
||||
|
||||
virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
|
||||
|
||||
virtual String8 getDeviceName(int32_t deviceId) const = 0;
|
||||
virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const = 0;
|
||||
|
||||
virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0;
|
||||
|
||||
@ -162,10 +188,10 @@ public:
|
||||
|
||||
virtual bool hasInputProperty(int32_t deviceId, int property) const = 0;
|
||||
|
||||
virtual status_t mapKey(int32_t deviceId, int scancode,
|
||||
virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
|
||||
int32_t* outKeycode, uint32_t* outFlags) const = 0;
|
||||
|
||||
virtual status_t mapAxis(int32_t deviceId, int scancode,
|
||||
virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
|
||||
AxisInfo* outAxisInfo) const = 0;
|
||||
|
||||
// Sets devices that are excluded from opening.
|
||||
@ -208,7 +234,12 @@ public:
|
||||
virtual void getVirtualKeyDefinitions(int32_t deviceId,
|
||||
Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
|
||||
|
||||
virtual String8 getKeyCharacterMapFile(int32_t deviceId) const = 0;
|
||||
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
|
||||
virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
|
||||
|
||||
/* Control the vibrator. */
|
||||
virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0;
|
||||
virtual void cancelVibrate(int32_t deviceId) = 0;
|
||||
|
||||
/* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
|
||||
virtual void requestReopenDevices() = 0;
|
||||
@ -230,7 +261,7 @@ public:
|
||||
|
||||
virtual uint32_t getDeviceClasses(int32_t deviceId) const;
|
||||
|
||||
virtual String8 getDeviceName(int32_t deviceId) const;
|
||||
virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const;
|
||||
|
||||
virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const;
|
||||
|
||||
@ -241,10 +272,10 @@ public:
|
||||
|
||||
virtual bool hasInputProperty(int32_t deviceId, int property) const;
|
||||
|
||||
virtual status_t mapKey(int32_t deviceId, int scancode,
|
||||
virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
|
||||
int32_t* outKeycode, uint32_t* outFlags) const;
|
||||
|
||||
virtual status_t mapAxis(int32_t deviceId, int scancode,
|
||||
virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
|
||||
AxisInfo* outAxisInfo) const;
|
||||
|
||||
virtual void setExcludedDevices(const Vector<String8>& devices);
|
||||
@ -266,7 +297,11 @@ public:
|
||||
virtual void getVirtualKeyDefinitions(int32_t deviceId,
|
||||
Vector<VirtualKeyDefinition>& outVirtualKeys) const;
|
||||
|
||||
virtual String8 getKeyCharacterMapFile(int32_t deviceId) const;
|
||||
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
|
||||
virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map);
|
||||
|
||||
virtual void vibrate(int32_t deviceId, nsecs_t duration);
|
||||
virtual void cancelVibrate(int32_t deviceId);
|
||||
|
||||
virtual void requestReopenDevices();
|
||||
|
||||
@ -282,7 +317,7 @@ private:
|
||||
struct Device {
|
||||
Device* next;
|
||||
|
||||
int fd;
|
||||
int fd; // may be -1 if device is virtual
|
||||
const int32_t id;
|
||||
const String8 path;
|
||||
const InputDeviceIdentifier identifier;
|
||||
@ -294,6 +329,7 @@ private:
|
||||
uint8_t relBitmask[(REL_MAX + 1) / 8];
|
||||
uint8_t swBitmask[(SW_MAX + 1) / 8];
|
||||
uint8_t ledBitmask[(LED_MAX + 1) / 8];
|
||||
uint8_t ffBitmask[(FF_MAX + 1) / 8];
|
||||
uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
|
||||
|
||||
String8 configurationFile;
|
||||
@ -301,15 +337,35 @@ private:
|
||||
VirtualKeyMap* virtualKeyMap;
|
||||
KeyMap keyMap;
|
||||
|
||||
sp<KeyCharacterMap> overlayKeyMap;
|
||||
sp<KeyCharacterMap> combinedKeyMap;
|
||||
|
||||
bool ffEffectPlaying;
|
||||
int16_t ffEffectId; // initially -1
|
||||
|
||||
int32_t timestampOverrideSec;
|
||||
int32_t timestampOverrideUsec;
|
||||
|
||||
Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
|
||||
~Device();
|
||||
|
||||
void close();
|
||||
|
||||
inline bool isVirtual() const { return fd < 0; }
|
||||
|
||||
const sp<KeyCharacterMap>& getKeyCharacterMap() const {
|
||||
if (combinedKeyMap != NULL) {
|
||||
return combinedKeyMap;
|
||||
}
|
||||
return keyMap.keyCharacterMap;
|
||||
}
|
||||
};
|
||||
|
||||
status_t openDeviceLocked(const char *devicePath);
|
||||
status_t closeDeviceByPathLocked(const char *devicePath);
|
||||
void createVirtualKeyboardLocked();
|
||||
void addDeviceLocked(Device* device);
|
||||
|
||||
status_t closeDeviceByPathLocked(const char *devicePath);
|
||||
void closeDeviceLocked(Device* device);
|
||||
void closeAllDevicesLocked();
|
||||
|
||||
@ -331,8 +387,13 @@ private:
|
||||
// Protect all internal state.
|
||||
mutable Mutex mLock;
|
||||
|
||||
// The actual id of the built-in keyboard, or -1 if none.
|
||||
// The actual id of the built-in keyboard, or NO_BUILT_IN_KEYBOARD if none.
|
||||
// EventHub remaps the built-in keyboard to id 0 externally as required by the API.
|
||||
enum {
|
||||
// Must not conflict with any other assigned device ids, including
|
||||
// the virtual keyboard id (-1).
|
||||
NO_BUILT_IN_KEYBOARD = -2,
|
||||
};
|
||||
int32_t mBuiltInKeyboardId;
|
||||
|
||||
int32_t mNextDeviceId;
|
||||
@ -367,9 +428,6 @@ private:
|
||||
size_t mPendingEventCount;
|
||||
size_t mPendingEventIndex;
|
||||
bool mPendingINotify;
|
||||
|
||||
// Set to the number of CPUs.
|
||||
int32_t mNumCpus;
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
@ -1,35 +1,28 @@
|
||||
//
|
||||
// Copyright 2010 The Android Open Source Project
|
||||
//
|
||||
// Provides a pipe-based transport for native events in the NDK.
|
||||
//
|
||||
/*
|
||||
* 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 "Input"
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
// Log debug messages about keymap probing.
|
||||
#define DEBUG_PROBE 0
|
||||
|
||||
// Log debug messages about velocity tracking.
|
||||
#define DEBUG_VELOCITY 0
|
||||
|
||||
// Log debug messages about least squares fitting.
|
||||
#define DEBUG_LEAST_SQUARES 0
|
||||
|
||||
// Log debug messages about acceleration.
|
||||
#define DEBUG_ACCELERATION 0
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "utils_Log.h"
|
||||
#include "Input.h"
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "Input.h"
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
#include <binder/Parcel.h>
|
||||
|
||||
@ -40,106 +33,6 @@
|
||||
|
||||
namespace android {
|
||||
|
||||
static const char* CONFIGURATION_FILE_DIR[] = {
|
||||
"idc/",
|
||||
"keylayout/",
|
||||
"keychars/",
|
||||
};
|
||||
|
||||
static const char* CONFIGURATION_FILE_EXTENSION[] = {
|
||||
".idc",
|
||||
".kl",
|
||||
".kcm",
|
||||
};
|
||||
|
||||
static bool isValidNameChar(char ch) {
|
||||
return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_');
|
||||
}
|
||||
|
||||
static void appendInputDeviceConfigurationFileRelativePath(String8& path,
|
||||
const String8& name, InputDeviceConfigurationFileType type) {
|
||||
path.append(CONFIGURATION_FILE_DIR[type]);
|
||||
for (size_t i = 0; i < name.length(); i++) {
|
||||
char ch = name[i];
|
||||
if (!isValidNameChar(ch)) {
|
||||
ch = '_';
|
||||
}
|
||||
path.append(&ch, 1);
|
||||
}
|
||||
path.append(CONFIGURATION_FILE_EXTENSION[type]);
|
||||
}
|
||||
|
||||
String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
|
||||
const InputDeviceIdentifier& deviceIdentifier,
|
||||
InputDeviceConfigurationFileType type) {
|
||||
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
|
||||
if (deviceIdentifier.version != 0) {
|
||||
// Try vendor product version.
|
||||
String8 versionPath(getInputDeviceConfigurationFilePathByName(
|
||||
String8::format("Vendor_%04x_Product_%04x_Version_%04x",
|
||||
deviceIdentifier.vendor, deviceIdentifier.product,
|
||||
deviceIdentifier.version),
|
||||
type));
|
||||
if (!versionPath.isEmpty()) {
|
||||
return versionPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Try vendor product.
|
||||
String8 productPath(getInputDeviceConfigurationFilePathByName(
|
||||
String8::format("Vendor_%04x_Product_%04x",
|
||||
deviceIdentifier.vendor, deviceIdentifier.product),
|
||||
type));
|
||||
if (!productPath.isEmpty()) {
|
||||
return productPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Try device name.
|
||||
return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
|
||||
}
|
||||
|
||||
String8 getInputDeviceConfigurationFilePathByName(
|
||||
const String8& name, InputDeviceConfigurationFileType type) {
|
||||
// Search system repository.
|
||||
String8 path;
|
||||
path.setTo(getenv("ANDROID_ROOT"));
|
||||
path.append("/usr/");
|
||||
appendInputDeviceConfigurationFileRelativePath(path, name, type);
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());
|
||||
#endif
|
||||
if (!access(path.string(), R_OK)) {
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Found");
|
||||
#endif
|
||||
return path;
|
||||
}
|
||||
|
||||
// Search user repository.
|
||||
// TODO Should only look here if not in safe mode.
|
||||
path.setTo(getenv("ANDROID_DATA"));
|
||||
path.append("/system/devices/");
|
||||
appendInputDeviceConfigurationFileRelativePath(path, name, type);
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Probing for system user input device configuration file: path='%s'", path.string());
|
||||
#endif
|
||||
if (!access(path.string(), R_OK)) {
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Found");
|
||||
#endif
|
||||
return path;
|
||||
}
|
||||
|
||||
// Not found.
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",
|
||||
name.string(), type);
|
||||
#endif
|
||||
return String8();
|
||||
}
|
||||
|
||||
|
||||
// --- InputEvent ---
|
||||
|
||||
void InputEvent::initialize(int32_t deviceId, int32_t source) {
|
||||
@ -180,6 +73,8 @@ bool KeyEvent::hasDefaultAction(int32_t keyCode) {
|
||||
case AKEYCODE_MEDIA_RECORD:
|
||||
case AKEYCODE_MEDIA_FAST_FORWARD:
|
||||
case AKEYCODE_MUTE:
|
||||
case AKEYCODE_BRIGHTNESS_DOWN:
|
||||
case AKEYCODE_BRIGHTNESS_UP:
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -216,6 +111,8 @@ bool KeyEvent::isSystemKey(int32_t keyCode) {
|
||||
case AKEYCODE_CAMERA:
|
||||
case AKEYCODE_FOCUS:
|
||||
case AKEYCODE_SEARCH:
|
||||
case AKEYCODE_BRIGHTNESS_DOWN:
|
||||
case AKEYCODE_BRIGHTNESS_UP:
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -329,7 +226,7 @@ status_t PointerCoords::readFromParcel(Parcel* parcel) {
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
values[i] = parcel->readInt32();
|
||||
values[i] = parcel->readFloat();
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
@ -339,7 +236,7 @@ status_t PointerCoords::writeToParcel(Parcel* parcel) const {
|
||||
|
||||
uint32_t count = __builtin_popcountll(bits);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
parcel->writeInt32(values[i]);
|
||||
parcel->writeFloat(values[i]);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
@ -684,541 +581,55 @@ bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
|
||||
}
|
||||
|
||||
|
||||
// --- VelocityTracker ---
|
||||
// --- PooledInputEventFactory ---
|
||||
|
||||
const uint32_t VelocityTracker::DEFAULT_DEGREE;
|
||||
const nsecs_t VelocityTracker::DEFAULT_HORIZON;
|
||||
const uint32_t VelocityTracker::HISTORY_SIZE;
|
||||
|
||||
static inline float vectorDot(const float* a, const float* b, uint32_t m) {
|
||||
float r = 0;
|
||||
while (m--) {
|
||||
r += *(a++) * *(b++);
|
||||
}
|
||||
return r;
|
||||
PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
|
||||
mMaxPoolSize(maxPoolSize) {
|
||||
}
|
||||
|
||||
static inline float vectorNorm(const float* a, uint32_t m) {
|
||||
float r = 0;
|
||||
while (m--) {
|
||||
float t = *(a++);
|
||||
r += t * t;
|
||||
PooledInputEventFactory::~PooledInputEventFactory() {
|
||||
for (size_t i = 0; i < mKeyEventPool.size(); i++) {
|
||||
delete mKeyEventPool.itemAt(i);
|
||||
}
|
||||
for (size_t i = 0; i < mMotionEventPool.size(); i++) {
|
||||
delete mMotionEventPool.itemAt(i);
|
||||
}
|
||||
return sqrtf(r);
|
||||
}
|
||||
|
||||
#if DEBUG_LEAST_SQUARES || DEBUG_VELOCITY
|
||||
static String8 vectorToString(const float* a, uint32_t m) {
|
||||
String8 str;
|
||||
str.append("[");
|
||||
while (m--) {
|
||||
str.appendFormat(" %f", *(a++));
|
||||
if (m) {
|
||||
str.append(",");
|
||||
KeyEvent* PooledInputEventFactory::createKeyEvent() {
|
||||
if (!mKeyEventPool.isEmpty()) {
|
||||
KeyEvent* event = mKeyEventPool.top();
|
||||
mKeyEventPool.pop();
|
||||
return event;
|
||||
}
|
||||
return new KeyEvent();
|
||||
}
|
||||
|
||||
MotionEvent* PooledInputEventFactory::createMotionEvent() {
|
||||
if (!mMotionEventPool.isEmpty()) {
|
||||
MotionEvent* event = mMotionEventPool.top();
|
||||
mMotionEventPool.pop();
|
||||
return event;
|
||||
}
|
||||
return new MotionEvent();
|
||||
}
|
||||
|
||||
void PooledInputEventFactory::recycle(InputEvent* event) {
|
||||
switch (event->getType()) {
|
||||
case AINPUT_EVENT_TYPE_KEY:
|
||||
if (mKeyEventPool.size() < mMaxPoolSize) {
|
||||
mKeyEventPool.push(static_cast<KeyEvent*>(event));
|
||||
return;
|
||||
}
|
||||
}
|
||||
str.append(" ]");
|
||||
return str;
|
||||
}
|
||||
|
||||
static String8 matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
|
||||
String8 str;
|
||||
str.append("[");
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
if (i) {
|
||||
str.append(",");
|
||||
}
|
||||
str.append(" [");
|
||||
for (size_t j = 0; j < n; j++) {
|
||||
if (j) {
|
||||
str.append(",");
|
||||
}
|
||||
str.appendFormat(" %f", a[rowMajor ? i * n + j : j * m + i]);
|
||||
}
|
||||
str.append(" ]");
|
||||
}
|
||||
str.append(" ]");
|
||||
return str;
|
||||
}
|
||||
#endif
|
||||
|
||||
VelocityTracker::VelocityTracker() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void VelocityTracker::clear() {
|
||||
mIndex = 0;
|
||||
mMovements[0].idBits.clear();
|
||||
mActivePointerId = -1;
|
||||
}
|
||||
|
||||
void VelocityTracker::clearPointers(BitSet32 idBits) {
|
||||
BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
|
||||
mMovements[mIndex].idBits = remainingIdBits;
|
||||
|
||||
if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) {
|
||||
mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1;
|
||||
}
|
||||
}
|
||||
|
||||
void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
|
||||
if (++mIndex == HISTORY_SIZE) {
|
||||
mIndex = 0;
|
||||
}
|
||||
|
||||
while (idBits.count() > MAX_POINTERS) {
|
||||
idBits.clearLastMarkedBit();
|
||||
}
|
||||
|
||||
Movement& movement = mMovements[mIndex];
|
||||
movement.eventTime = eventTime;
|
||||
movement.idBits = idBits;
|
||||
uint32_t count = idBits.count();
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
movement.positions[i] = positions[i];
|
||||
}
|
||||
|
||||
if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) {
|
||||
mActivePointerId = count != 0 ? idBits.firstMarkedBit() : -1;
|
||||
}
|
||||
|
||||
#if DEBUG_VELOCITY
|
||||
ALOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d",
|
||||
eventTime, idBits.value, mActivePointerId);
|
||||
for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
|
||||
uint32_t id = iterBits.firstMarkedBit();
|
||||
uint32_t index = idBits.getIndexOfBit(id);
|
||||
iterBits.clearBit(id);
|
||||
Estimator estimator;
|
||||
getEstimator(id, DEFAULT_DEGREE, DEFAULT_HORIZON, &estimator);
|
||||
ALOGD(" %d: position (%0.3f, %0.3f), "
|
||||
"estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
|
||||
id, positions[index].x, positions[index].y,
|
||||
int(estimator.degree),
|
||||
vectorToString(estimator.xCoeff, estimator.degree).string(),
|
||||
vectorToString(estimator.yCoeff, estimator.degree).string(),
|
||||
estimator.confidence);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VelocityTracker::addMovement(const MotionEvent* event) {
|
||||
int32_t actionMasked = event->getActionMasked();
|
||||
|
||||
switch (actionMasked) {
|
||||
case AMOTION_EVENT_ACTION_DOWN:
|
||||
case AMOTION_EVENT_ACTION_HOVER_ENTER:
|
||||
// Clear all pointers on down before adding the new movement.
|
||||
clear();
|
||||
break;
|
||||
case AMOTION_EVENT_ACTION_POINTER_DOWN: {
|
||||
// Start a new movement trace for a pointer that just went down.
|
||||
// We do this on down instead of on up because the client may want to query the
|
||||
// final velocity for a pointer that just went up.
|
||||
BitSet32 downIdBits;
|
||||
downIdBits.markBit(event->getPointerId(event->getActionIndex()));
|
||||
clearPointers(downIdBits);
|
||||
case AINPUT_EVENT_TYPE_MOTION:
|
||||
if (mMotionEventPool.size() < mMaxPoolSize) {
|
||||
mMotionEventPool.push(static_cast<MotionEvent*>(event));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AMOTION_EVENT_ACTION_MOVE:
|
||||
case AMOTION_EVENT_ACTION_HOVER_MOVE:
|
||||
break;
|
||||
default:
|
||||
// Ignore all other actions because they do not convey any new information about
|
||||
// pointer movement. We also want to preserve the last known velocity of the pointers.
|
||||
// Note that ACTION_UP and ACTION_POINTER_UP always report the last known position
|
||||
// of the pointers that went up. ACTION_POINTER_UP does include the new position of
|
||||
// pointers that remained down but we will also receive an ACTION_MOVE with this
|
||||
// information if any of them actually moved. Since we don't know how many pointers
|
||||
// will be going up at once it makes sense to just wait for the following ACTION_MOVE
|
||||
// before adding the movement.
|
||||
return;
|
||||
}
|
||||
|
||||
size_t pointerCount = event->getPointerCount();
|
||||
if (pointerCount > MAX_POINTERS) {
|
||||
pointerCount = MAX_POINTERS;
|
||||
}
|
||||
|
||||
BitSet32 idBits;
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
idBits.markBit(event->getPointerId(i));
|
||||
}
|
||||
|
||||
nsecs_t eventTime;
|
||||
Position positions[pointerCount];
|
||||
|
||||
size_t historySize = event->getHistorySize();
|
||||
for (size_t h = 0; h < historySize; h++) {
|
||||
eventTime = event->getHistoricalEventTime(h);
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
positions[i].x = event->getHistoricalX(i, h);
|
||||
positions[i].y = event->getHistoricalY(i, h);
|
||||
}
|
||||
addMovement(eventTime, idBits, positions);
|
||||
}
|
||||
|
||||
eventTime = event->getEventTime();
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
positions[i].x = event->getX(i);
|
||||
positions[i].y = event->getY(i);
|
||||
}
|
||||
addMovement(eventTime, idBits, positions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Solves a linear least squares problem to obtain a N degree polynomial that fits
|
||||
* the specified input data as nearly as possible.
|
||||
*
|
||||
* Returns true if a solution is found, false otherwise.
|
||||
*
|
||||
* The input consists of two vectors of data points X and Y with indices 0..m-1.
|
||||
* The output is a vector B with indices 0..n-1 that describes a polynomial
|
||||
* that fits the data, such the sum of abs(Y[i] - (B[0] + B[1] X[i] + B[2] X[i]^2 ... B[n] X[i]^n))
|
||||
* for all i between 0 and m-1 is minimized.
|
||||
*
|
||||
* That is to say, the function that generated the input data can be approximated
|
||||
* by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n.
|
||||
*
|
||||
* The coefficient of determination (R^2) is also returned to describe the goodness
|
||||
* of fit of the model for the given data. It is a value between 0 and 1, where 1
|
||||
* indicates perfect correspondence.
|
||||
*
|
||||
* This function first expands the X vector to a m by n matrix A such that
|
||||
* A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n.
|
||||
*
|
||||
* Then it calculates the QR decomposition of A yielding an m by m orthonormal matrix Q
|
||||
* and an m by n upper triangular matrix R. Because R is upper triangular (lower
|
||||
* part is all zeroes), we can simplify the decomposition into an m by n matrix
|
||||
* Q1 and a n by n matrix R1 such that A = Q1 R1.
|
||||
*
|
||||
* Finally we solve the system of linear equations given by R1 B = (Qtranspose Y)
|
||||
* to find B.
|
||||
*
|
||||
* For efficiency, we lay out A and Q column-wise in memory because we frequently
|
||||
* operate on the column vectors. Conversely, we lay out R row-wise.
|
||||
*
|
||||
* http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
|
||||
* http://en.wikipedia.org/wiki/Gram-Schmidt
|
||||
*/
|
||||
static bool solveLeastSquares(const float* x, const float* y, uint32_t m, uint32_t n,
|
||||
float* outB, float* outDet) {
|
||||
#if DEBUG_LEAST_SQUARES
|
||||
ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s", int(m), int(n),
|
||||
vectorToString(x, m).string(), vectorToString(y, m).string());
|
||||
#endif
|
||||
|
||||
// Expand the X vector to a matrix A.
|
||||
float a[n][m]; // column-major order
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
a[0][h] = 1;
|
||||
for (uint32_t i = 1; i < n; i++) {
|
||||
a[i][h] = a[i - 1][h] * x[h];
|
||||
}
|
||||
}
|
||||
#if DEBUG_LEAST_SQUARES
|
||||
ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).string());
|
||||
#endif
|
||||
|
||||
// Apply the Gram-Schmidt process to A to obtain its QR decomposition.
|
||||
float q[n][m]; // orthonormal basis, column-major order
|
||||
float r[n][n]; // upper triangular matrix, row-major order
|
||||
for (uint32_t j = 0; j < n; j++) {
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
q[j][h] = a[j][h];
|
||||
}
|
||||
for (uint32_t i = 0; i < j; i++) {
|
||||
float dot = vectorDot(&q[j][0], &q[i][0], m);
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
q[j][h] -= dot * q[i][h];
|
||||
}
|
||||
}
|
||||
|
||||
float norm = vectorNorm(&q[j][0], m);
|
||||
if (norm < 0.000001f) {
|
||||
// vectors are linearly dependent or zero so no solution
|
||||
#if DEBUG_LEAST_SQUARES
|
||||
ALOGD(" - no solution, norm=%f", norm);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
float invNorm = 1.0f / norm;
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
q[j][h] *= invNorm;
|
||||
}
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m);
|
||||
}
|
||||
}
|
||||
#if DEBUG_LEAST_SQUARES
|
||||
ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).string());
|
||||
ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).string());
|
||||
|
||||
// calculate QR, if we factored A correctly then QR should equal A
|
||||
float qr[n][m];
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
qr[i][h] = 0;
|
||||
for (uint32_t j = 0; j < n; j++) {
|
||||
qr[i][h] += q[j][h] * r[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).string());
|
||||
#endif
|
||||
|
||||
// Solve R B = Qt Y to find B. This is easy because R is upper triangular.
|
||||
// We just work from bottom-right to top-left calculating B's coefficients.
|
||||
for (uint32_t i = n; i-- != 0; ) {
|
||||
outB[i] = vectorDot(&q[i][0], y, m);
|
||||
for (uint32_t j = n - 1; j > i; j--) {
|
||||
outB[i] -= r[i][j] * outB[j];
|
||||
}
|
||||
outB[i] /= r[i][i];
|
||||
}
|
||||
#if DEBUG_LEAST_SQUARES
|
||||
ALOGD(" - b=%s", vectorToString(outB, n).string());
|
||||
#endif
|
||||
|
||||
// Calculate the coefficient of determination as 1 - (SSerr / SStot) where
|
||||
// SSerr is the residual sum of squares (squared variance of the error),
|
||||
// and SStot is the total sum of squares (squared variance of the data).
|
||||
float ymean = 0;
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
ymean += y[h];
|
||||
}
|
||||
ymean /= m;
|
||||
|
||||
float sserr = 0;
|
||||
float sstot = 0;
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
float err = y[h] - outB[0];
|
||||
float term = 1;
|
||||
for (uint32_t i = 1; i < n; i++) {
|
||||
term *= x[h];
|
||||
err -= term * outB[i];
|
||||
}
|
||||
sserr += err * err;
|
||||
float var = y[h] - ymean;
|
||||
sstot += var * var;
|
||||
}
|
||||
*outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
|
||||
#if DEBUG_LEAST_SQUARES
|
||||
ALOGD(" - sserr=%f", sserr);
|
||||
ALOGD(" - sstot=%f", sstot);
|
||||
ALOGD(" - det=%f", *outDet);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
|
||||
Estimator estimator;
|
||||
if (getEstimator(id, DEFAULT_DEGREE, DEFAULT_HORIZON, &estimator)) {
|
||||
if (estimator.degree >= 1) {
|
||||
*outVx = estimator.xCoeff[1];
|
||||
*outVy = estimator.yCoeff[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*outVx = 0;
|
||||
*outVy = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VelocityTracker::getEstimator(uint32_t id, uint32_t degree, nsecs_t horizon,
|
||||
Estimator* outEstimator) const {
|
||||
outEstimator->clear();
|
||||
|
||||
// Iterate over movement samples in reverse time order and collect samples.
|
||||
float x[HISTORY_SIZE];
|
||||
float y[HISTORY_SIZE];
|
||||
float time[HISTORY_SIZE];
|
||||
uint32_t m = 0;
|
||||
uint32_t index = mIndex;
|
||||
const Movement& newestMovement = mMovements[mIndex];
|
||||
do {
|
||||
const Movement& movement = mMovements[index];
|
||||
if (!movement.idBits.hasBit(id)) {
|
||||
break;
|
||||
}
|
||||
|
||||
nsecs_t age = newestMovement.eventTime - movement.eventTime;
|
||||
if (age > horizon) {
|
||||
break;
|
||||
}
|
||||
|
||||
const Position& position = movement.getPosition(id);
|
||||
x[m] = position.x;
|
||||
y[m] = position.y;
|
||||
time[m] = -age * 0.000000001f;
|
||||
index = (index == 0 ? HISTORY_SIZE : index) - 1;
|
||||
} while (++m < HISTORY_SIZE);
|
||||
|
||||
if (m == 0) {
|
||||
return false; // no data
|
||||
}
|
||||
|
||||
// Calculate a least squares polynomial fit.
|
||||
if (degree > Estimator::MAX_DEGREE) {
|
||||
degree = Estimator::MAX_DEGREE;
|
||||
}
|
||||
if (degree > m - 1) {
|
||||
degree = m - 1;
|
||||
}
|
||||
if (degree >= 1) {
|
||||
float xdet, ydet;
|
||||
uint32_t n = degree + 1;
|
||||
if (solveLeastSquares(time, x, m, n, outEstimator->xCoeff, &xdet)
|
||||
&& solveLeastSquares(time, y, m, n, outEstimator->yCoeff, &ydet)) {
|
||||
outEstimator->degree = degree;
|
||||
outEstimator->confidence = xdet * ydet;
|
||||
#if DEBUG_LEAST_SQUARES
|
||||
ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
|
||||
int(outEstimator->degree),
|
||||
vectorToString(outEstimator->xCoeff, n).string(),
|
||||
vectorToString(outEstimator->yCoeff, n).string(),
|
||||
outEstimator->confidence);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No velocity data available for this pointer, but we do have its current position.
|
||||
outEstimator->xCoeff[0] = x[0];
|
||||
outEstimator->yCoeff[0] = y[0];
|
||||
outEstimator->degree = 0;
|
||||
outEstimator->confidence = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// --- VelocityControl ---
|
||||
|
||||
const nsecs_t VelocityControl::STOP_TIME;
|
||||
|
||||
VelocityControl::VelocityControl() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void VelocityControl::setParameters(const VelocityControlParameters& parameters) {
|
||||
mParameters = parameters;
|
||||
reset();
|
||||
}
|
||||
|
||||
void VelocityControl::reset() {
|
||||
mLastMovementTime = LLONG_MIN;
|
||||
mRawPosition.x = 0;
|
||||
mRawPosition.y = 0;
|
||||
mVelocityTracker.clear();
|
||||
}
|
||||
|
||||
void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) {
|
||||
if ((deltaX && *deltaX) || (deltaY && *deltaY)) {
|
||||
if (eventTime >= mLastMovementTime + STOP_TIME) {
|
||||
#if DEBUG_ACCELERATION
|
||||
ALOGD("VelocityControl: stopped, last movement was %0.3fms ago",
|
||||
(eventTime - mLastMovementTime) * 0.000001f);
|
||||
#endif
|
||||
reset();
|
||||
}
|
||||
|
||||
mLastMovementTime = eventTime;
|
||||
if (deltaX) {
|
||||
mRawPosition.x += *deltaX;
|
||||
}
|
||||
if (deltaY) {
|
||||
mRawPosition.y += *deltaY;
|
||||
}
|
||||
mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition);
|
||||
|
||||
float vx, vy;
|
||||
float scale = mParameters.scale;
|
||||
if (mVelocityTracker.getVelocity(0, &vx, &vy)) {
|
||||
float speed = hypotf(vx, vy) * scale;
|
||||
if (speed >= mParameters.highThreshold) {
|
||||
// Apply full acceleration above the high speed threshold.
|
||||
scale *= mParameters.acceleration;
|
||||
} else if (speed > mParameters.lowThreshold) {
|
||||
// Linearly interpolate the acceleration to apply between the low and high
|
||||
// speed thresholds.
|
||||
scale *= 1 + (speed - mParameters.lowThreshold)
|
||||
/ (mParameters.highThreshold - mParameters.lowThreshold)
|
||||
* (mParameters.acceleration - 1);
|
||||
}
|
||||
|
||||
#if DEBUG_ACCELERATION
|
||||
ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): "
|
||||
"vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
|
||||
mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
|
||||
mParameters.acceleration,
|
||||
vx, vy, speed, scale / mParameters.scale);
|
||||
#endif
|
||||
} else {
|
||||
#if DEBUG_ACCELERATION
|
||||
ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity",
|
||||
mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
|
||||
mParameters.acceleration);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (deltaX) {
|
||||
*deltaX *= scale;
|
||||
}
|
||||
if (deltaY) {
|
||||
*deltaY *= scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- InputDeviceInfo ---
|
||||
|
||||
InputDeviceInfo::InputDeviceInfo() {
|
||||
initialize(-1, String8("uninitialized device info"));
|
||||
}
|
||||
|
||||
InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
|
||||
mId(other.mId), mName(other.mName), mSources(other.mSources),
|
||||
mKeyboardType(other.mKeyboardType),
|
||||
mMotionRanges(other.mMotionRanges) {
|
||||
}
|
||||
|
||||
InputDeviceInfo::~InputDeviceInfo() {
|
||||
}
|
||||
|
||||
void InputDeviceInfo::initialize(int32_t id, const String8& name) {
|
||||
mId = id;
|
||||
mName = name;
|
||||
mSources = 0;
|
||||
mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
|
||||
mMotionRanges.clear();
|
||||
}
|
||||
|
||||
const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(
|
||||
int32_t axis, uint32_t source) const {
|
||||
size_t numRanges = mMotionRanges.size();
|
||||
for (size_t i = 0; i < numRanges; i++) {
|
||||
const MotionRange& range = mMotionRanges.itemAt(i);
|
||||
if (range.axis == axis && range.source == source) {
|
||||
return ⦥
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void InputDeviceInfo::addSource(uint32_t source) {
|
||||
mSources |= source;
|
||||
}
|
||||
|
||||
void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max,
|
||||
float flat, float fuzz) {
|
||||
MotionRange range = { axis, source, min, max, flat, fuzz };
|
||||
mMotionRanges.add(range);
|
||||
}
|
||||
|
||||
void InputDeviceInfo::addMotionRange(const MotionRange& range) {
|
||||
mMotionRanges.add(range);
|
||||
delete event;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _UI_INPUT_H
|
||||
#define _UI_INPUT_H
|
||||
#ifndef _ANDROIDFW_INPUT_H
|
||||
#define _ANDROIDFW_INPUT_H
|
||||
|
||||
/**
|
||||
* Native input event structures.
|
||||
@ -24,10 +24,9 @@
|
||||
#include "android_input.h"
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include "Timers.h"
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include "String8.h"
|
||||
#include "BitSet.h"
|
||||
#include <utils/String8.h>
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
class SkMatrix;
|
||||
@ -37,6 +36,9 @@ class SkMatrix;
|
||||
* Additional private constants not defined in ndk/ui/input.h.
|
||||
*/
|
||||
enum {
|
||||
/* Signifies that the key is being predispatched */
|
||||
AKEY_EVENT_FLAG_PREDISPATCH = 0x20000000,
|
||||
|
||||
/* Private control to determine when an app is tracking a key sequence. */
|
||||
AKEY_EVENT_FLAG_START_TRACKING = 0x40000000,
|
||||
|
||||
@ -49,6 +51,15 @@ enum {
|
||||
AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
|
||||
};
|
||||
|
||||
enum {
|
||||
/* Used when a motion event is not associated with any display.
|
||||
* Typically used for non-pointer events. */
|
||||
ADISPLAY_ID_NONE = -1,
|
||||
|
||||
/* The default display id. */
|
||||
ADISPLAY_ID_DEFAULT = 0,
|
||||
};
|
||||
|
||||
enum {
|
||||
/*
|
||||
* Indicates that an input device has switches.
|
||||
@ -156,37 +167,6 @@ enum {
|
||||
POLICY_FLAG_PASS_TO_USER = 0x40000000,
|
||||
};
|
||||
|
||||
/*
|
||||
* Describes the basic configuration of input devices that are present.
|
||||
*/
|
||||
struct InputConfiguration {
|
||||
enum {
|
||||
TOUCHSCREEN_UNDEFINED = 0,
|
||||
TOUCHSCREEN_NOTOUCH = 1,
|
||||
TOUCHSCREEN_STYLUS = 2,
|
||||
TOUCHSCREEN_FINGER = 3
|
||||
};
|
||||
|
||||
enum {
|
||||
KEYBOARD_UNDEFINED = 0,
|
||||
KEYBOARD_NOKEYS = 1,
|
||||
KEYBOARD_QWERTY = 2,
|
||||
KEYBOARD_12KEY = 3
|
||||
};
|
||||
|
||||
enum {
|
||||
NAVIGATION_UNDEFINED = 0,
|
||||
NAVIGATION_NONAV = 1,
|
||||
NAVIGATION_DPAD = 2,
|
||||
NAVIGATION_TRACKBALL = 3,
|
||||
NAVIGATION_WHEEL = 4
|
||||
};
|
||||
|
||||
int32_t touchScreen;
|
||||
int32_t keyboard;
|
||||
int32_t navigation;
|
||||
};
|
||||
|
||||
/*
|
||||
* Pointer coordinate data.
|
||||
*/
|
||||
@ -292,6 +272,8 @@ public:
|
||||
|
||||
inline int32_t getFlags() const { return mFlags; }
|
||||
|
||||
inline void setFlags(int32_t flags) { mFlags = flags; }
|
||||
|
||||
inline int32_t getKeyCode() const { return mKeyCode; }
|
||||
|
||||
inline int32_t getScanCode() const { return mScanCode; }
|
||||
@ -616,282 +598,25 @@ private:
|
||||
};
|
||||
|
||||
/*
|
||||
* Calculates the velocity of pointer movements over time.
|
||||
* An input event factory implementation that maintains a pool of input events.
|
||||
*/
|
||||
class VelocityTracker {
|
||||
class PooledInputEventFactory : public InputEventFactoryInterface {
|
||||
public:
|
||||
// Default polynomial degree. (used by getVelocity)
|
||||
static const uint32_t DEFAULT_DEGREE = 2;
|
||||
PooledInputEventFactory(size_t maxPoolSize = 20);
|
||||
virtual ~PooledInputEventFactory();
|
||||
|
||||
// Default sample horizon. (used by getVelocity)
|
||||
// We don't use too much history by default since we want to react to quick
|
||||
// changes in direction.
|
||||
static const nsecs_t DEFAULT_HORIZON = 100 * 1000000; // 100 ms
|
||||
virtual KeyEvent* createKeyEvent();
|
||||
virtual MotionEvent* createMotionEvent();
|
||||
|
||||
struct Position {
|
||||
float x, y;
|
||||
};
|
||||
|
||||
struct Estimator {
|
||||
static const size_t MAX_DEGREE = 2;
|
||||
|
||||
// Polynomial coefficients describing motion in X and Y.
|
||||
float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1];
|
||||
|
||||
// Polynomial degree (number of coefficients), or zero if no information is
|
||||
// available.
|
||||
uint32_t degree;
|
||||
|
||||
// Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
|
||||
float confidence;
|
||||
|
||||
inline void clear() {
|
||||
degree = 0;
|
||||
confidence = 0;
|
||||
for (size_t i = 0; i <= MAX_DEGREE; i++) {
|
||||
xCoeff[i] = 0;
|
||||
yCoeff[i] = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VelocityTracker();
|
||||
|
||||
// Resets the velocity tracker state.
|
||||
void clear();
|
||||
|
||||
// Resets the velocity tracker state for specific pointers.
|
||||
// Call this method when some pointers have changed and may be reusing
|
||||
// an id that was assigned to a different pointer earlier.
|
||||
void clearPointers(BitSet32 idBits);
|
||||
|
||||
// Adds movement information for a set of pointers.
|
||||
// The idBits bitfield specifies the pointer ids of the pointers whose positions
|
||||
// are included in the movement.
|
||||
// The positions array contains position information for each pointer in order by
|
||||
// increasing id. Its size should be equal to the number of one bits in idBits.
|
||||
void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);
|
||||
|
||||
// Adds movement information for all pointers in a MotionEvent, including historical samples.
|
||||
void addMovement(const MotionEvent* event);
|
||||
|
||||
// Gets the velocity of the specified pointer id in position units per second.
|
||||
// Returns false and sets the velocity components to zero if there is
|
||||
// insufficient movement information for the pointer.
|
||||
bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
|
||||
|
||||
// Gets a quadratic estimator for the movements of the specified pointer id.
|
||||
// Returns false and clears the estimator if there is no information available
|
||||
// about the pointer.
|
||||
bool getEstimator(uint32_t id, uint32_t degree, nsecs_t horizon,
|
||||
Estimator* outEstimator) const;
|
||||
|
||||
// Gets the active pointer id, or -1 if none.
|
||||
inline int32_t getActivePointerId() const { return mActivePointerId; }
|
||||
|
||||
// Gets a bitset containing all pointer ids from the most recent movement.
|
||||
inline BitSet32 getCurrentPointerIdBits() const { return mMovements[mIndex].idBits; }
|
||||
void recycle(InputEvent* event);
|
||||
|
||||
private:
|
||||
// Number of samples to keep.
|
||||
static const uint32_t HISTORY_SIZE = 20;
|
||||
const size_t mMaxPoolSize;
|
||||
|
||||
struct Movement {
|
||||
nsecs_t eventTime;
|
||||
BitSet32 idBits;
|
||||
Position positions[MAX_POINTERS];
|
||||
|
||||
inline const Position& getPosition(uint32_t id) const {
|
||||
return positions[idBits.getIndexOfBit(id)];
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t mIndex;
|
||||
Movement mMovements[HISTORY_SIZE];
|
||||
int32_t mActivePointerId;
|
||||
Vector<KeyEvent*> mKeyEventPool;
|
||||
Vector<MotionEvent*> mMotionEventPool;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Specifies parameters that govern pointer or wheel acceleration.
|
||||
*/
|
||||
struct VelocityControlParameters {
|
||||
// A scale factor that is multiplied with the raw velocity deltas
|
||||
// prior to applying any other velocity control factors. The scale
|
||||
// factor should be used to adapt the input device resolution
|
||||
// (eg. counts per inch) to the output device resolution (eg. pixels per inch).
|
||||
//
|
||||
// Must be a positive value.
|
||||
// Default is 1.0 (no scaling).
|
||||
float scale;
|
||||
|
||||
// The scaled speed at which acceleration begins to be applied.
|
||||
// This value establishes the upper bound of a low speed regime for
|
||||
// small precise motions that are performed without any acceleration.
|
||||
//
|
||||
// Must be a non-negative value.
|
||||
// Default is 0.0 (no low threshold).
|
||||
float lowThreshold;
|
||||
|
||||
// The scaled speed at which maximum acceleration is applied.
|
||||
// The difference between highThreshold and lowThreshold controls
|
||||
// the range of speeds over which the acceleration factor is interpolated.
|
||||
// The wider the range, the smoother the acceleration.
|
||||
//
|
||||
// Must be a non-negative value greater than or equal to lowThreshold.
|
||||
// Default is 0.0 (no high threshold).
|
||||
float highThreshold;
|
||||
|
||||
// The acceleration factor.
|
||||
// When the speed is above the low speed threshold, the velocity will scaled
|
||||
// by an interpolated value between 1.0 and this amount.
|
||||
//
|
||||
// Must be a positive greater than or equal to 1.0.
|
||||
// Default is 1.0 (no acceleration).
|
||||
float acceleration;
|
||||
|
||||
VelocityControlParameters() :
|
||||
scale(1.0f), lowThreshold(0.0f), highThreshold(0.0f), acceleration(1.0f) {
|
||||
}
|
||||
|
||||
VelocityControlParameters(float scale, float lowThreshold,
|
||||
float highThreshold, float acceleration) :
|
||||
scale(scale), lowThreshold(lowThreshold),
|
||||
highThreshold(highThreshold), acceleration(acceleration) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Implements mouse pointer and wheel speed control and acceleration.
|
||||
*/
|
||||
class VelocityControl {
|
||||
public:
|
||||
VelocityControl();
|
||||
|
||||
/* Sets the various parameters. */
|
||||
void setParameters(const VelocityControlParameters& parameters);
|
||||
|
||||
/* Resets the current movement counters to zero.
|
||||
* This has the effect of nullifying any acceleration. */
|
||||
void reset();
|
||||
|
||||
/* Translates a raw movement delta into an appropriately
|
||||
* scaled / accelerated delta based on the current velocity. */
|
||||
void move(nsecs_t eventTime, float* deltaX, float* deltaY);
|
||||
|
||||
private:
|
||||
// If no movements are received within this amount of time,
|
||||
// we assume the movement has stopped and reset the movement counters.
|
||||
static const nsecs_t STOP_TIME = 500 * 1000000; // 500 ms
|
||||
|
||||
VelocityControlParameters mParameters;
|
||||
|
||||
nsecs_t mLastMovementTime;
|
||||
VelocityTracker::Position mRawPosition;
|
||||
VelocityTracker mVelocityTracker;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Describes the characteristics and capabilities of an input device.
|
||||
*/
|
||||
class InputDeviceInfo {
|
||||
public:
|
||||
InputDeviceInfo();
|
||||
InputDeviceInfo(const InputDeviceInfo& other);
|
||||
~InputDeviceInfo();
|
||||
|
||||
struct MotionRange {
|
||||
int32_t axis;
|
||||
uint32_t source;
|
||||
float min;
|
||||
float max;
|
||||
float flat;
|
||||
float fuzz;
|
||||
};
|
||||
|
||||
void initialize(int32_t id, const String8& name);
|
||||
|
||||
inline int32_t getId() const { return mId; }
|
||||
inline const String8 getName() const { return mName; }
|
||||
inline uint32_t getSources() const { return mSources; }
|
||||
|
||||
const MotionRange* getMotionRange(int32_t axis, uint32_t source) const;
|
||||
|
||||
void addSource(uint32_t source);
|
||||
void addMotionRange(int32_t axis, uint32_t source,
|
||||
float min, float max, float flat, float fuzz);
|
||||
void addMotionRange(const MotionRange& range);
|
||||
|
||||
inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
|
||||
inline int32_t getKeyboardType() const { return mKeyboardType; }
|
||||
|
||||
inline void setKeyCharacterMapFile(const String8& value) { mKeyCharacterMapFile = value; }
|
||||
inline const String8& getKeyCharacterMapFile() const { return mKeyCharacterMapFile; }
|
||||
|
||||
inline const Vector<MotionRange>& getMotionRanges() const {
|
||||
return mMotionRanges;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t mId;
|
||||
String8 mName;
|
||||
uint32_t mSources;
|
||||
int32_t mKeyboardType;
|
||||
String8 mKeyCharacterMapFile;
|
||||
|
||||
Vector<MotionRange> mMotionRanges;
|
||||
};
|
||||
|
||||
/*
|
||||
* Identifies a device.
|
||||
*/
|
||||
struct InputDeviceIdentifier {
|
||||
inline InputDeviceIdentifier() :
|
||||
bus(0), vendor(0), product(0), version(0) {
|
||||
}
|
||||
|
||||
String8 name;
|
||||
String8 location;
|
||||
String8 uniqueId;
|
||||
uint16_t bus;
|
||||
uint16_t vendor;
|
||||
uint16_t product;
|
||||
uint16_t version;
|
||||
};
|
||||
|
||||
/* Types of input device configuration files. */
|
||||
enum InputDeviceConfigurationFileType {
|
||||
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */
|
||||
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */
|
||||
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */
|
||||
};
|
||||
|
||||
/*
|
||||
* Gets the path of an input device configuration file, if one is available.
|
||||
* Considers both system provided and user installed configuration files.
|
||||
*
|
||||
* The device identifier is used to construct several default configuration file
|
||||
* names to try based on the device name, vendor, product, and version.
|
||||
*
|
||||
* Returns an empty string if not found.
|
||||
*/
|
||||
extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
|
||||
const InputDeviceIdentifier& deviceIdentifier,
|
||||
InputDeviceConfigurationFileType type);
|
||||
|
||||
/*
|
||||
* Gets the path of an input device configuration file, if one is available.
|
||||
* Considers both system provided and user installed configuration files.
|
||||
*
|
||||
* The name is case-sensitive and is used to construct the filename to resolve.
|
||||
* All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores.
|
||||
*
|
||||
* Returns an empty string if not found.
|
||||
*/
|
||||
extern String8 getInputDeviceConfigurationFilePathByName(
|
||||
const String8& name, InputDeviceConfigurationFileType type);
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UI_INPUT_H
|
||||
#endif // _ANDROIDFW_INPUT_H
|
||||
|
@ -20,8 +20,8 @@
|
||||
#include "Input.h"
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
#include "Timers.h"
|
||||
#include "String8.h"
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
|
184
widget/gonk/libui/InputDevice.cpp
Normal file
184
widget/gonk/libui/InputDevice.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 "InputDevice"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "InputDevice.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
static const char* CONFIGURATION_FILE_DIR[] = {
|
||||
"idc/",
|
||||
"keylayout/",
|
||||
"keychars/",
|
||||
};
|
||||
|
||||
static const char* CONFIGURATION_FILE_EXTENSION[] = {
|
||||
".idc",
|
||||
".kl",
|
||||
".kcm",
|
||||
};
|
||||
|
||||
static bool isValidNameChar(char ch) {
|
||||
return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_');
|
||||
}
|
||||
|
||||
static void appendInputDeviceConfigurationFileRelativePath(String8& path,
|
||||
const String8& name, InputDeviceConfigurationFileType type) {
|
||||
path.append(CONFIGURATION_FILE_DIR[type]);
|
||||
for (size_t i = 0; i < name.length(); i++) {
|
||||
char ch = name[i];
|
||||
if (!isValidNameChar(ch)) {
|
||||
ch = '_';
|
||||
}
|
||||
path.append(&ch, 1);
|
||||
}
|
||||
path.append(CONFIGURATION_FILE_EXTENSION[type]);
|
||||
}
|
||||
|
||||
String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
|
||||
const InputDeviceIdentifier& deviceIdentifier,
|
||||
InputDeviceConfigurationFileType type) {
|
||||
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
|
||||
if (deviceIdentifier.version != 0) {
|
||||
// Try vendor product version.
|
||||
String8 versionPath(getInputDeviceConfigurationFilePathByName(
|
||||
String8::format("Vendor_%04x_Product_%04x_Version_%04x",
|
||||
deviceIdentifier.vendor, deviceIdentifier.product,
|
||||
deviceIdentifier.version),
|
||||
type));
|
||||
if (!versionPath.isEmpty()) {
|
||||
return versionPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Try vendor product.
|
||||
String8 productPath(getInputDeviceConfigurationFilePathByName(
|
||||
String8::format("Vendor_%04x_Product_%04x",
|
||||
deviceIdentifier.vendor, deviceIdentifier.product),
|
||||
type));
|
||||
if (!productPath.isEmpty()) {
|
||||
return productPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Try device name.
|
||||
return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
|
||||
}
|
||||
|
||||
String8 getInputDeviceConfigurationFilePathByName(
|
||||
const String8& name, InputDeviceConfigurationFileType type) {
|
||||
// Search system repository.
|
||||
String8 path;
|
||||
path.setTo(getenv("ANDROID_ROOT"));
|
||||
path.append("/usr/");
|
||||
appendInputDeviceConfigurationFileRelativePath(path, name, type);
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());
|
||||
#endif
|
||||
if (!access(path.string(), R_OK)) {
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Found");
|
||||
#endif
|
||||
return path;
|
||||
}
|
||||
|
||||
// Search user repository.
|
||||
// TODO Should only look here if not in safe mode.
|
||||
path.setTo(getenv("ANDROID_DATA"));
|
||||
path.append("/system/devices/");
|
||||
appendInputDeviceConfigurationFileRelativePath(path, name, type);
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Probing for system user input device configuration file: path='%s'", path.string());
|
||||
#endif
|
||||
if (!access(path.string(), R_OK)) {
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Found");
|
||||
#endif
|
||||
return path;
|
||||
}
|
||||
|
||||
// Not found.
|
||||
#if DEBUG_PROBE
|
||||
ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",
|
||||
name.string(), type);
|
||||
#endif
|
||||
return String8();
|
||||
}
|
||||
|
||||
|
||||
// --- InputDeviceInfo ---
|
||||
|
||||
InputDeviceInfo::InputDeviceInfo() {
|
||||
initialize(-1, -1, InputDeviceIdentifier(), String8(), false);
|
||||
}
|
||||
|
||||
InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
|
||||
mId(other.mId), mGeneration(other.mGeneration), mIdentifier(other.mIdentifier),
|
||||
mAlias(other.mAlias), mIsExternal(other.mIsExternal), mSources(other.mSources),
|
||||
mKeyboardType(other.mKeyboardType),
|
||||
mKeyCharacterMap(other.mKeyCharacterMap),
|
||||
mHasVibrator(other.mHasVibrator),
|
||||
mMotionRanges(other.mMotionRanges) {
|
||||
}
|
||||
|
||||
InputDeviceInfo::~InputDeviceInfo() {
|
||||
}
|
||||
|
||||
void InputDeviceInfo::initialize(int32_t id, int32_t generation,
|
||||
const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal) {
|
||||
mId = id;
|
||||
mGeneration = generation;
|
||||
mIdentifier = identifier;
|
||||
mAlias = alias;
|
||||
mIsExternal = isExternal;
|
||||
mSources = 0;
|
||||
mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
|
||||
mHasVibrator = false;
|
||||
mMotionRanges.clear();
|
||||
}
|
||||
|
||||
const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(
|
||||
int32_t axis, uint32_t source) const {
|
||||
size_t numRanges = mMotionRanges.size();
|
||||
for (size_t i = 0; i < numRanges; i++) {
|
||||
const MotionRange& range = mMotionRanges.itemAt(i);
|
||||
if (range.axis == axis && range.source == source) {
|
||||
return ⦥
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void InputDeviceInfo::addSource(uint32_t source) {
|
||||
mSources |= source;
|
||||
}
|
||||
|
||||
void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max,
|
||||
float flat, float fuzz, float resolution) {
|
||||
MotionRange range = { axis, source, min, max, flat, fuzz, resolution };
|
||||
mMotionRanges.add(range);
|
||||
}
|
||||
|
||||
void InputDeviceInfo::addMotionRange(const MotionRange& range) {
|
||||
mMotionRanges.add(range);
|
||||
}
|
||||
|
||||
} // namespace android
|
156
widget/gonk/libui/InputDevice.h
Normal file
156
widget/gonk/libui/InputDevice.h
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef _ANDROIDFW_INPUT_DEVICE_H
|
||||
#define _ANDROIDFW_INPUT_DEVICE_H
|
||||
|
||||
#include "Input.h"
|
||||
#include "KeyCharacterMap.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Identifies a device.
|
||||
*/
|
||||
struct InputDeviceIdentifier {
|
||||
inline InputDeviceIdentifier() :
|
||||
bus(0), vendor(0), product(0), version(0) {
|
||||
}
|
||||
|
||||
// Information provided by the kernel.
|
||||
String8 name;
|
||||
String8 location;
|
||||
String8 uniqueId;
|
||||
uint16_t bus;
|
||||
uint16_t vendor;
|
||||
uint16_t product;
|
||||
uint16_t version;
|
||||
|
||||
// A composite input device descriptor string that uniquely identifies the device
|
||||
// even across reboots or reconnections. The value of this field is used by
|
||||
// upper layers of the input system to associate settings with individual devices.
|
||||
// It is hashed from whatever kernel provided information is available.
|
||||
// Ideally, the way this value is computed should not change between Android releases
|
||||
// because that would invalidate persistent settings that rely on it.
|
||||
String8 descriptor;
|
||||
};
|
||||
|
||||
/*
|
||||
* Describes the characteristics and capabilities of an input device.
|
||||
*/
|
||||
class InputDeviceInfo {
|
||||
public:
|
||||
InputDeviceInfo();
|
||||
InputDeviceInfo(const InputDeviceInfo& other);
|
||||
~InputDeviceInfo();
|
||||
|
||||
struct MotionRange {
|
||||
int32_t axis;
|
||||
uint32_t source;
|
||||
float min;
|
||||
float max;
|
||||
float flat;
|
||||
float fuzz;
|
||||
float resolution;
|
||||
};
|
||||
|
||||
void initialize(int32_t id, int32_t generation, const InputDeviceIdentifier& identifier,
|
||||
const String8& alias, bool isExternal);
|
||||
|
||||
inline int32_t getId() const { return mId; }
|
||||
inline int32_t getGeneration() const { return mGeneration; }
|
||||
inline const InputDeviceIdentifier& getIdentifier() const { return mIdentifier; }
|
||||
inline const String8& getAlias() const { return mAlias; }
|
||||
inline const String8& getDisplayName() const {
|
||||
return mAlias.isEmpty() ? mIdentifier.name : mAlias;
|
||||
}
|
||||
inline bool isExternal() const { return mIsExternal; }
|
||||
inline uint32_t getSources() const { return mSources; }
|
||||
|
||||
const MotionRange* getMotionRange(int32_t axis, uint32_t source) const;
|
||||
|
||||
void addSource(uint32_t source);
|
||||
void addMotionRange(int32_t axis, uint32_t source,
|
||||
float min, float max, float flat, float fuzz, float resolution);
|
||||
void addMotionRange(const MotionRange& range);
|
||||
|
||||
inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
|
||||
inline int32_t getKeyboardType() const { return mKeyboardType; }
|
||||
|
||||
inline void setKeyCharacterMap(const sp<KeyCharacterMap>& value) {
|
||||
mKeyCharacterMap = value;
|
||||
}
|
||||
|
||||
inline sp<KeyCharacterMap> getKeyCharacterMap() const {
|
||||
return mKeyCharacterMap;
|
||||
}
|
||||
|
||||
inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; }
|
||||
inline bool hasVibrator() const { return mHasVibrator; }
|
||||
|
||||
inline const Vector<MotionRange>& getMotionRanges() const {
|
||||
return mMotionRanges;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t mId;
|
||||
int32_t mGeneration;
|
||||
InputDeviceIdentifier mIdentifier;
|
||||
String8 mAlias;
|
||||
bool mIsExternal;
|
||||
uint32_t mSources;
|
||||
int32_t mKeyboardType;
|
||||
sp<KeyCharacterMap> mKeyCharacterMap;
|
||||
bool mHasVibrator;
|
||||
|
||||
Vector<MotionRange> mMotionRanges;
|
||||
};
|
||||
|
||||
/* Types of input device configuration files. */
|
||||
enum InputDeviceConfigurationFileType {
|
||||
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */
|
||||
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */
|
||||
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */
|
||||
};
|
||||
|
||||
/*
|
||||
* Gets the path of an input device configuration file, if one is available.
|
||||
* Considers both system provided and user installed configuration files.
|
||||
*
|
||||
* The device identifier is used to construct several default configuration file
|
||||
* names to try based on the device name, vendor, product, and version.
|
||||
*
|
||||
* Returns an empty string if not found.
|
||||
*/
|
||||
extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
|
||||
const InputDeviceIdentifier& deviceIdentifier,
|
||||
InputDeviceConfigurationFileType type);
|
||||
|
||||
/*
|
||||
* Gets the path of an input device configuration file, if one is available.
|
||||
* Considers both system provided and user installed configuration files.
|
||||
*
|
||||
* The name is case-sensitive and is used to construct the filename to resolve.
|
||||
* All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores.
|
||||
*
|
||||
* Returns an empty string if not found.
|
||||
*/
|
||||
extern String8 getInputDeviceConfigurationFilePathByName(
|
||||
const String8& name, InputDeviceConfigurationFileType type);
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_INPUT_DEVICE_H
|
File diff suppressed because it is too large
Load Diff
@ -17,17 +17,17 @@
|
||||
#ifndef _UI_INPUT_DISPATCHER_H
|
||||
#define _UI_INPUT_DISPATCHER_H
|
||||
|
||||
#include "cutils_log.h"
|
||||
#include "Input.h"
|
||||
#include "InputTransport.h"
|
||||
#include <utils/KeyedVector.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/threads.h>
|
||||
#include "Timers.h"
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include "String8.h"
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Looper.h>
|
||||
#include "BitSet.h"
|
||||
#include <utils/BitSet.h>
|
||||
#include <cutils/atomic.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
@ -165,6 +165,8 @@ struct InputTarget {
|
||||
* Input dispatcher configuration.
|
||||
*
|
||||
* Specifies various options that modify the behavior of the input dispatcher.
|
||||
* The values provided here are merely defaults. The actual values will come from ViewConfiguration
|
||||
* and are passed into the dispatcher during initialization.
|
||||
*/
|
||||
struct InputDispatcherConfiguration {
|
||||
// The key repeat initial timeout.
|
||||
@ -173,15 +175,9 @@ struct InputDispatcherConfiguration {
|
||||
// The key repeat inter-key delay.
|
||||
nsecs_t keyRepeatDelay;
|
||||
|
||||
// The maximum suggested event delivery rate per second.
|
||||
// This value is used to throttle motion event movement actions on a per-device
|
||||
// basis. It is not intended to be a hard limit.
|
||||
int32_t maxEventsPerSecond;
|
||||
|
||||
InputDispatcherConfiguration() :
|
||||
keyRepeatTimeout(500 * 1000000LL),
|
||||
keyRepeatDelay(50 * 1000000LL),
|
||||
maxEventsPerSecond(60) { }
|
||||
keyRepeatDelay(50 * 1000000LL) { }
|
||||
};
|
||||
|
||||
|
||||
@ -254,7 +250,7 @@ public:
|
||||
/* Notifies the policy about switch events.
|
||||
*/
|
||||
virtual void notifySwitch(nsecs_t when,
|
||||
int32_t switchCode, int32_t switchValue, uint32_t policyFlags) = 0;
|
||||
uint32_t switchValues, uint32_t switchMask, uint32_t policyFlags) = 0;
|
||||
|
||||
/* Poke user activity for an event dispatched to a window. */
|
||||
virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
|
||||
@ -405,6 +401,9 @@ private:
|
||||
struct Link {
|
||||
T* next;
|
||||
T* prev;
|
||||
|
||||
protected:
|
||||
inline Link() : next(NULL), prev(NULL) { }
|
||||
};
|
||||
|
||||
struct InjectionState {
|
||||
@ -443,6 +442,8 @@ private:
|
||||
|
||||
void release();
|
||||
|
||||
virtual void appendDescription(String8& msg) const = 0;
|
||||
|
||||
protected:
|
||||
EventEntry(int32_t type, nsecs_t eventTime, uint32_t policyFlags);
|
||||
virtual ~EventEntry();
|
||||
@ -451,6 +452,7 @@ private:
|
||||
|
||||
struct ConfigurationChangedEntry : EventEntry {
|
||||
ConfigurationChangedEntry(nsecs_t eventTime);
|
||||
virtual void appendDescription(String8& msg) const;
|
||||
|
||||
protected:
|
||||
virtual ~ConfigurationChangedEntry();
|
||||
@ -460,6 +462,7 @@ private:
|
||||
int32_t deviceId;
|
||||
|
||||
DeviceResetEntry(nsecs_t eventTime, int32_t deviceId);
|
||||
virtual void appendDescription(String8& msg) const;
|
||||
|
||||
protected:
|
||||
virtual ~DeviceResetEntry();
|
||||
@ -491,24 +494,15 @@ private:
|
||||
int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action,
|
||||
int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
|
||||
int32_t repeatCount, nsecs_t downTime);
|
||||
virtual void appendDescription(String8& msg) const;
|
||||
void recycle();
|
||||
|
||||
protected:
|
||||
virtual ~KeyEntry();
|
||||
};
|
||||
|
||||
struct MotionSample {
|
||||
MotionSample* next;
|
||||
|
||||
nsecs_t eventTime; // may be updated during coalescing
|
||||
nsecs_t eventTimeBeforeCoalescing; // not updated during coalescing
|
||||
PointerCoords pointerCoords[MAX_POINTERS];
|
||||
|
||||
MotionSample(nsecs_t eventTime, const PointerCoords* pointerCoords,
|
||||
uint32_t pointerCount);
|
||||
};
|
||||
|
||||
struct MotionEntry : EventEntry {
|
||||
nsecs_t eventTime;
|
||||
int32_t deviceId;
|
||||
uint32_t source;
|
||||
int32_t action;
|
||||
@ -519,27 +513,19 @@ private:
|
||||
float xPrecision;
|
||||
float yPrecision;
|
||||
nsecs_t downTime;
|
||||
int32_t displayId;
|
||||
uint32_t pointerCount;
|
||||
PointerProperties pointerProperties[MAX_POINTERS];
|
||||
|
||||
// Linked list of motion samples associated with this motion event.
|
||||
MotionSample firstSample;
|
||||
MotionSample* lastSample;
|
||||
PointerCoords pointerCoords[MAX_POINTERS];
|
||||
|
||||
MotionEntry(nsecs_t eventTime,
|
||||
int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action,
|
||||
int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
|
||||
int32_t deviceId, uint32_t source, uint32_t policyFlags,
|
||||
int32_t action, int32_t flags,
|
||||
int32_t metaState, int32_t buttonState, int32_t edgeFlags,
|
||||
float xPrecision, float yPrecision,
|
||||
nsecs_t downTime, uint32_t pointerCount,
|
||||
nsecs_t downTime, int32_t displayId, uint32_t pointerCount,
|
||||
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
|
||||
|
||||
uint32_t countSamples() const;
|
||||
|
||||
// Checks whether we can append samples, assuming the device id and source are the same.
|
||||
bool canAppendSamples(int32_t action, uint32_t pointerCount,
|
||||
const PointerProperties* pointerProperties) const;
|
||||
|
||||
void appendSample(nsecs_t eventTime, const PointerCoords* pointerCoords);
|
||||
virtual void appendDescription(String8& msg) const;
|
||||
|
||||
protected:
|
||||
virtual ~MotionEntry();
|
||||
@ -547,32 +533,19 @@ private:
|
||||
|
||||
// Tracks the progress of dispatching a particular event to a particular connection.
|
||||
struct DispatchEntry : Link<DispatchEntry> {
|
||||
const uint32_t seq; // unique sequence number, never 0
|
||||
|
||||
EventEntry* eventEntry; // the event to dispatch
|
||||
int32_t targetFlags;
|
||||
float xOffset;
|
||||
float yOffset;
|
||||
float scaleFactor;
|
||||
|
||||
// True if dispatch has started.
|
||||
bool inProgress;
|
||||
nsecs_t deliveryTime; // time when the event was actually delivered
|
||||
|
||||
// Set to the resolved action and flags when the event is enqueued.
|
||||
int32_t resolvedAction;
|
||||
int32_t resolvedFlags;
|
||||
|
||||
// For motion events:
|
||||
// Pointer to the first motion sample to dispatch in this cycle.
|
||||
// Usually NULL to indicate that the list of motion samples begins at
|
||||
// MotionEntry::firstSample. Otherwise, some samples were dispatched in a previous
|
||||
// cycle and this pointer indicates the location of the first remainining sample
|
||||
// to dispatch during the current cycle.
|
||||
MotionSample* headMotionSample;
|
||||
// Pointer to a motion sample to dispatch in the next cycle if the dispatcher was
|
||||
// unable to send all motion samples during this cycle. On the next cycle,
|
||||
// headMotionSample will be initialized to tailMotionSample and tailMotionSample
|
||||
// will be set to NULL.
|
||||
MotionSample* tailMotionSample;
|
||||
|
||||
DispatchEntry(EventEntry* eventEntry,
|
||||
int32_t targetFlags, float xOffset, float yOffset, float scaleFactor);
|
||||
~DispatchEntry();
|
||||
@ -584,6 +557,11 @@ private:
|
||||
inline bool isSplit() const {
|
||||
return targetFlags & InputTarget::FLAG_SPLIT;
|
||||
}
|
||||
|
||||
private:
|
||||
static volatile int32_t sNextSeqAtomic;
|
||||
|
||||
static uint32_t nextSeq();
|
||||
};
|
||||
|
||||
// A command entry captures state and behavior for an action to be performed in the
|
||||
@ -619,6 +597,7 @@ private:
|
||||
sp<InputApplicationHandle> inputApplicationHandle;
|
||||
sp<InputWindowHandle> inputWindowHandle;
|
||||
int32_t userActivityEventType;
|
||||
uint32_t seq;
|
||||
bool handled;
|
||||
};
|
||||
|
||||
@ -721,7 +700,7 @@ private:
|
||||
|
||||
// Returns true if the specified source is known to have received a hover enter
|
||||
// motion event.
|
||||
bool isHovering(int32_t deviceId, uint32_t source) const;
|
||||
bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const;
|
||||
|
||||
// Records tracking information for a key event that has just been published.
|
||||
// Returns true if the event should be delivered, false if it is inconsistent
|
||||
@ -764,8 +743,10 @@ private:
|
||||
uint32_t source;
|
||||
int32_t keyCode;
|
||||
int32_t scanCode;
|
||||
int32_t metaState;
|
||||
int32_t flags;
|
||||
nsecs_t downTime;
|
||||
uint32_t policyFlags;
|
||||
};
|
||||
|
||||
struct MotionMemento {
|
||||
@ -775,10 +756,12 @@ private:
|
||||
float xPrecision;
|
||||
float yPrecision;
|
||||
nsecs_t downTime;
|
||||
int32_t displayId;
|
||||
uint32_t pointerCount;
|
||||
PointerProperties pointerProperties[MAX_POINTERS];
|
||||
PointerCoords pointerCoords[MAX_POINTERS];
|
||||
bool hovering;
|
||||
uint32_t policyFlags;
|
||||
|
||||
void setPointers(const MotionEntry* entry);
|
||||
};
|
||||
@ -820,33 +803,27 @@ private:
|
||||
bool monitor;
|
||||
InputPublisher inputPublisher;
|
||||
InputState inputState;
|
||||
|
||||
// True if the socket is full and no further events can be published until
|
||||
// the application consumes some of the input.
|
||||
bool inputPublisherBlocked;
|
||||
|
||||
// Queue of events that need to be published to the connection.
|
||||
Queue<DispatchEntry> outboundQueue;
|
||||
|
||||
nsecs_t lastEventTime; // the time when the event was originally captured
|
||||
nsecs_t lastDispatchTime; // the time when the last event was dispatched
|
||||
// Queue of events that have been published to the connection but that have not
|
||||
// yet received a "finished" response from the application.
|
||||
Queue<DispatchEntry> waitQueue;
|
||||
|
||||
explicit Connection(const sp<InputChannel>& inputChannel,
|
||||
const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
|
||||
|
||||
inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
|
||||
|
||||
const char* getWindowName() const;
|
||||
const char* getStatusLabel() const;
|
||||
|
||||
// Finds a DispatchEntry in the outbound queue associated with the specified event.
|
||||
// Returns NULL if not found.
|
||||
DispatchEntry* findQueuedDispatchEntryForEvent(const EventEntry* eventEntry) const;
|
||||
|
||||
// Gets the time since the current event was originally obtained from the input driver.
|
||||
inline double getEventLatencyMillis(nsecs_t currentTime) const {
|
||||
return (currentTime - lastEventTime) / 1000000.0;
|
||||
}
|
||||
|
||||
// Gets the time since the current event entered the outbound dispatch queue.
|
||||
inline double getDispatchLatencyMillis(nsecs_t currentTime) const {
|
||||
return (currentTime - lastDispatchTime) / 1000000.0;
|
||||
}
|
||||
|
||||
status_t initialize();
|
||||
DispatchEntry* findWaitQueueEntry(uint32_t seq);
|
||||
};
|
||||
|
||||
enum DropReason {
|
||||
@ -863,21 +840,15 @@ private:
|
||||
|
||||
Mutex mLock;
|
||||
|
||||
Condition mDispatcherIsAliveCondition;
|
||||
|
||||
sp<Looper> mLooper;
|
||||
|
||||
EventEntry* mPendingEvent;
|
||||
Queue<EventEntry> mInboundQueue;
|
||||
Queue<CommandEntry> mCommandQueue;
|
||||
|
||||
Vector<EventEntry*> mTempCancelationEvents;
|
||||
|
||||
void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime);
|
||||
void dispatchIdleLocked();
|
||||
|
||||
// Batches a new sample onto a motion entry.
|
||||
// Assumes that the we have already checked that we can append samples.
|
||||
void batchMotionLocked(MotionEntry* entry, nsecs_t eventTime, int32_t metaState,
|
||||
const PointerCoords* pointerCoords, const char* eventDescription);
|
||||
|
||||
// Enqueues an inbound event. Returns true if mLooper->wake() should be called.
|
||||
bool enqueueInboundEventLocked(EventEntry* entry);
|
||||
@ -901,19 +872,13 @@ private:
|
||||
// to transfer focus to a new application.
|
||||
EventEntry* mNextUnblockedEvent;
|
||||
|
||||
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t x, int32_t y);
|
||||
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y);
|
||||
|
||||
// All registered connections mapped by receive pipe file descriptor.
|
||||
KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
|
||||
// All registered connections mapped by channel file descriptor.
|
||||
KeyedVector<int, sp<Connection> > mConnectionsByFd;
|
||||
|
||||
ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
|
||||
|
||||
// Active connections are connections that have a non-empty outbound queue.
|
||||
// We don't use a ref-counted pointer here because we explicitly abort connections
|
||||
// during unregistration which causes the connection's outbound queue to be cleared
|
||||
// and the connection itself to be deactivated.
|
||||
Vector<Connection*> mActiveConnections;
|
||||
|
||||
// Input channels that will receive a copy of all input events.
|
||||
Vector<sp<InputChannel> > mMonitoringChannels;
|
||||
|
||||
@ -926,17 +891,6 @@ private:
|
||||
void incrementPendingForegroundDispatchesLocked(EventEntry* entry);
|
||||
void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
|
||||
|
||||
// Throttling state.
|
||||
struct ThrottleState {
|
||||
nsecs_t minTimeBetweenEvents;
|
||||
|
||||
nsecs_t lastEventTime;
|
||||
int32_t lastDeviceId;
|
||||
uint32_t lastSource;
|
||||
|
||||
uint32_t originalSampleCount; // only collected during debugging
|
||||
} mThrottleState;
|
||||
|
||||
// Key repeat tracking.
|
||||
struct KeyRepeatState {
|
||||
KeyEntry* lastKeyEntry; // or null if no repeat
|
||||
@ -947,9 +901,14 @@ private:
|
||||
KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime);
|
||||
|
||||
// Deferred command processing.
|
||||
bool haveCommandsLocked() const;
|
||||
bool runCommandsLockedInterruptible();
|
||||
CommandEntry* postCommandLocked(Command command);
|
||||
|
||||
// Input filter processing.
|
||||
bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args);
|
||||
bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args);
|
||||
|
||||
// Inbound event processing.
|
||||
void drainInboundQueueLocked();
|
||||
void releasePendingEventLocked();
|
||||
@ -979,6 +938,7 @@ private:
|
||||
bool split;
|
||||
int32_t deviceId; // id of the device that is currently down, others are rejected
|
||||
uint32_t source; // source of the device that is current down, others are rejected
|
||||
int32_t displayId; // id to the display that currently has a touch, others are rejected
|
||||
Vector<TouchedWindow> windows;
|
||||
|
||||
TouchState();
|
||||
@ -987,6 +947,7 @@ private:
|
||||
void copyFrom(const TouchState& other);
|
||||
void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
|
||||
int32_t targetFlags, BitSet32 pointerIds);
|
||||
void removeWindow(const sp<InputWindowHandle>& windowHandle);
|
||||
void filterNonAsIsTouchWindows();
|
||||
sp<InputWindowHandle> getFirstForegroundWindowHandle() const;
|
||||
bool isSlippery() const;
|
||||
@ -998,6 +959,9 @@ private:
|
||||
// Focused application.
|
||||
sp<InputApplicationHandle> mFocusedApplicationHandle;
|
||||
|
||||
// Dispatcher state at time of last ANR.
|
||||
String8 mLastANRState;
|
||||
|
||||
// Dispatch inbound events.
|
||||
bool dispatchConfigurationChangedLocked(
|
||||
nsecs_t currentTime, ConfigurationChangedEntry* entry);
|
||||
@ -1009,16 +973,13 @@ private:
|
||||
bool dispatchMotionLocked(
|
||||
nsecs_t currentTime, MotionEntry* entry,
|
||||
DropReason* dropReason, nsecs_t* nextWakeupTime);
|
||||
void dispatchEventToCurrentInputTargetsLocked(
|
||||
nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample);
|
||||
void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
|
||||
const Vector<InputTarget>& inputTargets);
|
||||
|
||||
void logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry);
|
||||
void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry);
|
||||
|
||||
// The input targets that were most recently identified for dispatch.
|
||||
bool mCurrentInputTargetsValid; // false while targets are being recomputed
|
||||
Vector<InputTarget> mCurrentInputTargets;
|
||||
|
||||
// Keeping track of ANR timeouts.
|
||||
enum InputTargetWaitCause {
|
||||
INPUT_TARGET_WAIT_CAUSE_NONE,
|
||||
INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
|
||||
@ -1035,32 +996,32 @@ private:
|
||||
sp<InputWindowHandle> mLastHoverWindowHandle;
|
||||
|
||||
// Finding targets for input events.
|
||||
void resetTargetsLocked();
|
||||
void commitTargetsLocked();
|
||||
int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
|
||||
const sp<InputApplicationHandle>& applicationHandle,
|
||||
const sp<InputWindowHandle>& windowHandle,
|
||||
nsecs_t* nextWakeupTime);
|
||||
nsecs_t* nextWakeupTime, const char* reason);
|
||||
void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
|
||||
const sp<InputChannel>& inputChannel);
|
||||
nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
|
||||
void resetANRTimeoutsLocked();
|
||||
|
||||
int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
|
||||
nsecs_t* nextWakeupTime);
|
||||
Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime);
|
||||
int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
|
||||
nsecs_t* nextWakeupTime, bool* outConflictingPointerActions,
|
||||
const MotionSample** outSplitBatchAfterSample);
|
||||
Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
|
||||
bool* outConflictingPointerActions);
|
||||
|
||||
void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
|
||||
int32_t targetFlags, BitSet32 pointerIds);
|
||||
void addMonitoringTargetsLocked();
|
||||
int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets);
|
||||
void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets);
|
||||
|
||||
void pokeUserActivityLocked(const EventEntry* eventEntry);
|
||||
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
|
||||
const InjectionState* injectionState);
|
||||
bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
|
||||
int32_t x, int32_t y) const;
|
||||
bool isWindowFinishedWithPreviousInputLocked(const sp<InputWindowHandle>& windowHandle);
|
||||
bool isWindowReadyForMoreInputLocked(nsecs_t currentTime,
|
||||
const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry);
|
||||
String8 getApplicationWindowLabelLocked(const sp<InputApplicationHandle>& applicationHandle,
|
||||
const sp<InputWindowHandle>& windowHandle);
|
||||
|
||||
@ -1069,19 +1030,19 @@ private:
|
||||
// with the mutex held makes it easier to ensure that connection invariants are maintained.
|
||||
// If needed, the methods post commands to run later once the critical bits are done.
|
||||
void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
|
||||
EventEntry* eventEntry, const InputTarget* inputTarget,
|
||||
bool resumeWithAppendedMotionSample);
|
||||
EventEntry* eventEntry, const InputTarget* inputTarget);
|
||||
void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
|
||||
EventEntry* eventEntry, const InputTarget* inputTarget);
|
||||
void enqueueDispatchEntryLocked(const sp<Connection>& connection,
|
||||
EventEntry* eventEntry, const InputTarget* inputTarget,
|
||||
bool resumeWithAppendedMotionSample, int32_t dispatchMode);
|
||||
EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode);
|
||||
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
|
||||
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
|
||||
bool handled);
|
||||
void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
|
||||
uint32_t seq, bool handled);
|
||||
void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
|
||||
bool notify);
|
||||
void drainOutboundQueueLocked(Connection* connection);
|
||||
static int handleReceiveCallback(int receiveFd, int events, void* data);
|
||||
void drainDispatchQueueLocked(Queue<DispatchEntry>* queue);
|
||||
void releaseDispatchEntryLocked(DispatchEntry* dispatchEntry);
|
||||
static int handleReceiveCallback(int fd, int events, void* data);
|
||||
|
||||
void synthesizeCancelationEventsForAllConnectionsLocked(
|
||||
const CancelationOptions& options);
|
||||
@ -1109,16 +1070,14 @@ private:
|
||||
void deactivateConnectionLocked(Connection* connection);
|
||||
|
||||
// Interesting events that we might like to log or tell the framework about.
|
||||
void onDispatchCycleStartedLocked(
|
||||
nsecs_t currentTime, const sp<Connection>& connection);
|
||||
void onDispatchCycleFinishedLocked(
|
||||
nsecs_t currentTime, const sp<Connection>& connection, bool handled);
|
||||
nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled);
|
||||
void onDispatchCycleBrokenLocked(
|
||||
nsecs_t currentTime, const sp<Connection>& connection);
|
||||
void onANRLocked(
|
||||
nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
|
||||
const sp<InputWindowHandle>& windowHandle,
|
||||
nsecs_t eventTime, nsecs_t waitStartTime);
|
||||
nsecs_t eventTime, nsecs_t waitStartTime, const char* reason);
|
||||
|
||||
// Outbound policy interactions.
|
||||
void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
|
||||
@ -1136,6 +1095,9 @@ private:
|
||||
// Statistics gathering.
|
||||
void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
|
||||
int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
|
||||
void traceInboundQueueLengthLocked();
|
||||
void traceOutboundQueueLengthLocked(const sp<Connection>& connection);
|
||||
void traceWaitQueueLengthLocked(const sp<Connection>& connection);
|
||||
};
|
||||
|
||||
/* Enqueues and dispatches input events, endlessly. */
|
||||
|
@ -69,12 +69,12 @@ void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
|
||||
NotifyMotionArgs::NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source,
|
||||
uint32_t policyFlags,
|
||||
int32_t action, int32_t flags, int32_t metaState, int32_t buttonState,
|
||||
int32_t edgeFlags, uint32_t pointerCount,
|
||||
int32_t edgeFlags, int32_t displayId, uint32_t pointerCount,
|
||||
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
|
||||
float xPrecision, float yPrecision, nsecs_t downTime) :
|
||||
eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags),
|
||||
action(action), flags(flags), metaState(metaState), buttonState(buttonState),
|
||||
edgeFlags(edgeFlags), pointerCount(pointerCount),
|
||||
edgeFlags(edgeFlags), displayId(displayId), pointerCount(pointerCount),
|
||||
xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime) {
|
||||
for (uint32_t i = 0; i < pointerCount; i++) {
|
||||
this->pointerProperties[i].copyFrom(pointerProperties[i]);
|
||||
@ -87,7 +87,8 @@ NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) :
|
||||
policyFlags(other.policyFlags),
|
||||
action(other.action), flags(other.flags),
|
||||
metaState(other.metaState), buttonState(other.buttonState),
|
||||
edgeFlags(other.edgeFlags), pointerCount(other.pointerCount),
|
||||
edgeFlags(other.edgeFlags), displayId(other.displayId),
|
||||
pointerCount(other.pointerCount),
|
||||
xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime) {
|
||||
for (uint32_t i = 0; i < pointerCount; i++) {
|
||||
pointerProperties[i].copyFrom(other.pointerProperties[i]);
|
||||
@ -103,14 +104,14 @@ void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const
|
||||
// --- NotifySwitchArgs ---
|
||||
|
||||
NotifySwitchArgs::NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags,
|
||||
int32_t switchCode, int32_t switchValue) :
|
||||
uint32_t switchValues, uint32_t switchMask) :
|
||||
eventTime(eventTime), policyFlags(policyFlags),
|
||||
switchCode(switchCode), switchValue(switchValue) {
|
||||
switchValues(switchValues), switchMask(switchMask) {
|
||||
}
|
||||
|
||||
NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) :
|
||||
eventTime(other.eventTime), policyFlags(other.policyFlags),
|
||||
switchCode(other.switchCode), switchValue(other.switchValue) {
|
||||
switchValues(other.switchValues), switchMask(other.switchMask) {
|
||||
}
|
||||
|
||||
void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const {
|
||||
|
@ -88,6 +88,7 @@ struct NotifyMotionArgs : public NotifyArgs {
|
||||
int32_t metaState;
|
||||
int32_t buttonState;
|
||||
int32_t edgeFlags;
|
||||
int32_t displayId;
|
||||
uint32_t pointerCount;
|
||||
PointerProperties pointerProperties[MAX_POINTERS];
|
||||
PointerCoords pointerCoords[MAX_POINTERS];
|
||||
@ -99,7 +100,7 @@ struct NotifyMotionArgs : public NotifyArgs {
|
||||
|
||||
NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
|
||||
int32_t action, int32_t flags, int32_t metaState, int32_t buttonState,
|
||||
int32_t edgeFlags, uint32_t pointerCount,
|
||||
int32_t edgeFlags, int32_t displayId, uint32_t pointerCount,
|
||||
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
|
||||
float xPrecision, float yPrecision, nsecs_t downTime);
|
||||
|
||||
@ -115,13 +116,13 @@ struct NotifyMotionArgs : public NotifyArgs {
|
||||
struct NotifySwitchArgs : public NotifyArgs {
|
||||
nsecs_t eventTime;
|
||||
uint32_t policyFlags;
|
||||
int32_t switchCode;
|
||||
int32_t switchValue;
|
||||
uint32_t switchValues;
|
||||
uint32_t switchMask;
|
||||
|
||||
inline NotifySwitchArgs() { }
|
||||
|
||||
NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags,
|
||||
int32_t switchCode, int32_t switchValue);
|
||||
uint32_t switchValues, uint32_t switchMask);
|
||||
|
||||
NotifySwitchArgs(const NotifySwitchArgs& other);
|
||||
|
||||
|
93
widget/gonk/libui/InputManager.cpp
Normal file
93
widget/gonk/libui/InputManager.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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 "InputManager"
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include "InputManager.h"
|
||||
|
||||
#include "cutils_log.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
InputManager::InputManager(
|
||||
const sp<EventHubInterface>& eventHub,
|
||||
const sp<InputReaderPolicyInterface>& readerPolicy,
|
||||
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
|
||||
mDispatcher = new InputDispatcher(dispatcherPolicy);
|
||||
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
|
||||
initialize();
|
||||
}
|
||||
|
||||
InputManager::InputManager(
|
||||
const sp<InputReaderInterface>& reader,
|
||||
const sp<InputDispatcherInterface>& dispatcher) :
|
||||
mReader(reader),
|
||||
mDispatcher(dispatcher) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
InputManager::~InputManager() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void InputManager::initialize() {
|
||||
mReaderThread = new InputReaderThread(mReader);
|
||||
mDispatcherThread = new InputDispatcherThread(mDispatcher);
|
||||
}
|
||||
|
||||
status_t InputManager::start() {
|
||||
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
|
||||
if (result) {
|
||||
ALOGE("Could not start InputDispatcher thread due to error %d.", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
|
||||
if (result) {
|
||||
ALOGE("Could not start InputReader thread due to error %d.", result);
|
||||
|
||||
mDispatcherThread->requestExit();
|
||||
return result;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t InputManager::stop() {
|
||||
status_t result = mReaderThread->requestExitAndWait();
|
||||
if (result) {
|
||||
ALOGW("Could not stop InputReader thread due to error %d.", result);
|
||||
}
|
||||
|
||||
result = mDispatcherThread->requestExitAndWait();
|
||||
if (result) {
|
||||
ALOGW("Could not stop InputDispatcher thread due to error %d.", result);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
sp<InputReaderInterface> InputManager::getReader() {
|
||||
return mReader;
|
||||
}
|
||||
|
||||
sp<InputDispatcherInterface> InputManager::getDispatcher() {
|
||||
return mDispatcher;
|
||||
}
|
||||
|
||||
} // namespace android
|
109
widget/gonk/libui/InputManager.h
Normal file
109
widget/gonk/libui/InputManager.h
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _UI_INPUT_MANAGER_H
|
||||
#define _UI_INPUT_MANAGER_H
|
||||
|
||||
/**
|
||||
* Native input manager.
|
||||
*/
|
||||
|
||||
#include "EventHub.h"
|
||||
#include "InputReader.h"
|
||||
#include "InputDispatcher.h"
|
||||
|
||||
#include "Input.h"
|
||||
#include "InputTransport.h"
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* The input manager is the core of the system event processing.
|
||||
*
|
||||
* The input manager uses two threads.
|
||||
*
|
||||
* 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
|
||||
* applies policy, and posts messages to a queue managed by the DispatcherThread.
|
||||
* 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
|
||||
* queue and asynchronously dispatches them to applications.
|
||||
*
|
||||
* By design, the InputReaderThread class and InputDispatcherThread class do not share any
|
||||
* internal state. Moreover, all communication is done one way from the InputReaderThread
|
||||
* into the InputDispatcherThread and never the reverse. Both classes may interact with the
|
||||
* InputDispatchPolicy, however.
|
||||
*
|
||||
* The InputManager class never makes any calls into Java itself. Instead, the
|
||||
* InputDispatchPolicy is responsible for performing all external interactions with the
|
||||
* system, including calling DVM services.
|
||||
*/
|
||||
class InputManagerInterface : public virtual RefBase {
|
||||
protected:
|
||||
InputManagerInterface() { }
|
||||
virtual ~InputManagerInterface() { }
|
||||
|
||||
public:
|
||||
/* Starts the input manager threads. */
|
||||
virtual status_t start() = 0;
|
||||
|
||||
/* Stops the input manager threads and waits for them to exit. */
|
||||
virtual status_t stop() = 0;
|
||||
|
||||
/* Gets the input reader. */
|
||||
virtual sp<InputReaderInterface> getReader() = 0;
|
||||
|
||||
/* Gets the input dispatcher. */
|
||||
virtual sp<InputDispatcherInterface> getDispatcher() = 0;
|
||||
};
|
||||
|
||||
class InputManager : public InputManagerInterface {
|
||||
protected:
|
||||
virtual ~InputManager();
|
||||
|
||||
public:
|
||||
InputManager(
|
||||
const sp<EventHubInterface>& eventHub,
|
||||
const sp<InputReaderPolicyInterface>& readerPolicy,
|
||||
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
|
||||
|
||||
// (used for testing purposes)
|
||||
InputManager(
|
||||
const sp<InputReaderInterface>& reader,
|
||||
const sp<InputDispatcherInterface>& dispatcher);
|
||||
|
||||
virtual status_t start();
|
||||
virtual status_t stop();
|
||||
|
||||
virtual sp<InputReaderInterface> getReader();
|
||||
virtual sp<InputDispatcherInterface> getDispatcher();
|
||||
|
||||
private:
|
||||
sp<InputReaderInterface> mReader;
|
||||
sp<InputReaderThread> mReaderThread;
|
||||
|
||||
sp<InputDispatcherInterface> mDispatcher;
|
||||
sp<InputDispatcherThread> mDispatcherThread;
|
||||
|
||||
void initialize();
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UI_INPUT_MANAGER_H
|
File diff suppressed because it is too large
Load Diff
@ -22,22 +22,94 @@
|
||||
#include "InputListener.h"
|
||||
|
||||
#include "Input.h"
|
||||
#include "DisplayInfo.h"
|
||||
#include "VelocityControl.h"
|
||||
#include "VelocityTracker.h"
|
||||
#include <utils/KeyedVector.h>
|
||||
#include <utils/threads.h>
|
||||
#include "Timers.h"
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include "String8.h"
|
||||
#include "BitSet.h"
|
||||
#include <utils/String8.h>
|
||||
#include <utils/BitSet.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Maximum supported size of a vibration pattern.
|
||||
// Must be at least 2.
|
||||
#define MAX_VIBRATE_PATTERN_SIZE 100
|
||||
|
||||
// Maximum allowable delay value in a vibration pattern before
|
||||
// which the delay will be truncated.
|
||||
#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
|
||||
|
||||
namespace android {
|
||||
|
||||
class InputDevice;
|
||||
class InputMapper;
|
||||
|
||||
/*
|
||||
* Describes how coordinates are mapped on a physical display.
|
||||
* See com.android.server.display.DisplayViewport.
|
||||
*/
|
||||
struct DisplayViewport {
|
||||
int32_t displayId; // -1 if invalid
|
||||
int32_t orientation;
|
||||
int32_t logicalLeft;
|
||||
int32_t logicalTop;
|
||||
int32_t logicalRight;
|
||||
int32_t logicalBottom;
|
||||
int32_t physicalLeft;
|
||||
int32_t physicalTop;
|
||||
int32_t physicalRight;
|
||||
int32_t physicalBottom;
|
||||
int32_t deviceWidth;
|
||||
int32_t deviceHeight;
|
||||
|
||||
DisplayViewport() :
|
||||
displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
|
||||
logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
|
||||
physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
|
||||
deviceWidth(0), deviceHeight(0) {
|
||||
}
|
||||
|
||||
bool operator==(const DisplayViewport& other) const {
|
||||
return displayId == other.displayId
|
||||
&& orientation == other.orientation
|
||||
&& logicalLeft == other.logicalLeft
|
||||
&& logicalTop == other.logicalTop
|
||||
&& logicalRight == other.logicalRight
|
||||
&& logicalBottom == other.logicalBottom
|
||||
&& physicalLeft == other.physicalLeft
|
||||
&& physicalTop == other.physicalTop
|
||||
&& physicalRight == other.physicalRight
|
||||
&& physicalBottom == other.physicalBottom
|
||||
&& deviceWidth == other.deviceWidth
|
||||
&& deviceHeight == other.deviceHeight;
|
||||
}
|
||||
|
||||
bool operator!=(const DisplayViewport& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
inline bool isValid() const {
|
||||
return displayId >= 0;
|
||||
}
|
||||
|
||||
void setNonDisplayViewport(int32_t width, int32_t height) {
|
||||
displayId = ADISPLAY_ID_NONE;
|
||||
orientation = DISPLAY_ORIENTATION_0;
|
||||
logicalLeft = 0;
|
||||
logicalTop = 0;
|
||||
logicalRight = width;
|
||||
logicalBottom = height;
|
||||
physicalLeft = 0;
|
||||
physicalTop = 0;
|
||||
physicalRight = width;
|
||||
physicalBottom = height;
|
||||
deviceWidth = width;
|
||||
deviceHeight = height;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Input reader configuration.
|
||||
@ -59,6 +131,12 @@ struct InputReaderConfiguration {
|
||||
// The visible touches option changed.
|
||||
CHANGE_SHOW_TOUCHES = 1 << 3,
|
||||
|
||||
// The keyboard layouts must be reloaded.
|
||||
CHANGE_KEYBOARD_LAYOUTS = 1 << 4,
|
||||
|
||||
// The device name alias supplied by the may have changed for some devices.
|
||||
CHANGE_DEVICE_ALIAS = 1 << 5,
|
||||
|
||||
// All devices must be reopened.
|
||||
CHANGE_MUST_REOPEN = 1 << 31,
|
||||
};
|
||||
@ -164,25 +242,12 @@ struct InputReaderConfiguration {
|
||||
pointerGestureZoomSpeedRatio(0.3f),
|
||||
showTouches(false) { }
|
||||
|
||||
bool getDisplayInfo(int32_t displayId, bool external,
|
||||
int32_t* width, int32_t* height, int32_t* orientation) const;
|
||||
|
||||
void setDisplayInfo(int32_t displayId, bool external,
|
||||
int32_t width, int32_t height, int32_t orientation);
|
||||
bool getDisplayInfo(bool external, DisplayViewport* outViewport) const;
|
||||
void setDisplayInfo(bool external, const DisplayViewport& viewport);
|
||||
|
||||
private:
|
||||
struct DisplayInfo {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
int32_t orientation;
|
||||
|
||||
DisplayInfo() :
|
||||
width(-1), height(-1), orientation(DISPLAY_ORIENTATION_0) {
|
||||
}
|
||||
};
|
||||
|
||||
DisplayInfo mInternalDisplay;
|
||||
DisplayInfo mExternalDisplay;
|
||||
DisplayViewport mInternalDisplay;
|
||||
DisplayViewport mExternalDisplay;
|
||||
};
|
||||
|
||||
|
||||
@ -209,6 +274,17 @@ public:
|
||||
|
||||
/* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */
|
||||
virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0;
|
||||
|
||||
/* Notifies the input reader policy that some input devices have changed
|
||||
* and provides information about all current input devices.
|
||||
*/
|
||||
virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) = 0;
|
||||
|
||||
/* Gets the keyboard layout for a particular input device. */
|
||||
virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) = 0;
|
||||
|
||||
/* Gets a user-supplied alias for a particular input device, or an empty string if none. */
|
||||
virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
|
||||
};
|
||||
|
||||
|
||||
@ -234,22 +310,11 @@ public:
|
||||
*/
|
||||
virtual void loopOnce() = 0;
|
||||
|
||||
/* Gets the current input device configuration.
|
||||
/* Gets information about all input devices.
|
||||
*
|
||||
* This method may be called on any thread (usually by the input manager).
|
||||
*/
|
||||
virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0;
|
||||
|
||||
/* Gets information about the specified input device.
|
||||
* Returns OK if the device information was obtained or NAME_NOT_FOUND if there
|
||||
* was no such device.
|
||||
*
|
||||
* This method may be called on any thread (usually by the input manager).
|
||||
*/
|
||||
virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0;
|
||||
|
||||
/* Gets the list of all registered device ids. */
|
||||
virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0;
|
||||
virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices) = 0;
|
||||
|
||||
/* Query current input state. */
|
||||
virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
|
||||
@ -267,6 +332,11 @@ public:
|
||||
* The changes flag is a bitfield that indicates what has changed and whether
|
||||
* the input devices must all be reopened. */
|
||||
virtual void requestRefreshConfiguration(uint32_t changes) = 0;
|
||||
|
||||
/* Controls the vibrator of a particular input device. */
|
||||
virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
|
||||
ssize_t repeat, int32_t token) = 0;
|
||||
virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
|
||||
};
|
||||
|
||||
|
||||
@ -288,6 +358,7 @@ public:
|
||||
virtual void fadePointer() = 0;
|
||||
|
||||
virtual void requestTimeoutAtTime(nsecs_t when) = 0;
|
||||
virtual int32_t bumpGeneration() = 0;
|
||||
|
||||
virtual InputReaderPolicyInterface* getPolicy() = 0;
|
||||
virtual InputListenerInterface* getListener() = 0;
|
||||
@ -318,10 +389,7 @@ public:
|
||||
|
||||
virtual void loopOnce();
|
||||
|
||||
virtual void getInputConfiguration(InputConfiguration* outConfiguration);
|
||||
|
||||
virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo);
|
||||
virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds);
|
||||
virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices);
|
||||
|
||||
virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
|
||||
int32_t scanCode);
|
||||
@ -335,10 +403,14 @@ public:
|
||||
|
||||
virtual void requestRefreshConfiguration(uint32_t changes);
|
||||
|
||||
virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
|
||||
ssize_t repeat, int32_t token);
|
||||
virtual void cancelVibrate(int32_t deviceId, int32_t token);
|
||||
|
||||
protected:
|
||||
// These members are protected so they can be instrumented by test cases.
|
||||
virtual InputDevice* createDeviceLocked(int32_t deviceId,
|
||||
const String8& name, uint32_t classes);
|
||||
const InputDeviceIdentifier& identifier, uint32_t classes);
|
||||
|
||||
class ContextImpl : public InputReaderContext {
|
||||
InputReader* mReader;
|
||||
@ -353,6 +425,7 @@ protected:
|
||||
InputDevice* device, int32_t keyCode, int32_t scanCode);
|
||||
virtual void fadePointer();
|
||||
virtual void requestTimeoutAtTime(nsecs_t when);
|
||||
virtual int32_t bumpGeneration();
|
||||
virtual InputReaderPolicyInterface* getPolicy();
|
||||
virtual InputListenerInterface* getListener();
|
||||
virtual EventHubInterface* getEventHub();
|
||||
@ -363,6 +436,8 @@ protected:
|
||||
private:
|
||||
Mutex mLock;
|
||||
|
||||
Condition mReaderIsAliveCondition;
|
||||
|
||||
sp<EventHubInterface> mEventHub;
|
||||
sp<InputReaderPolicyInterface> mPolicy;
|
||||
sp<QueuedInputListener> mQueuedListener;
|
||||
@ -391,8 +466,10 @@ private:
|
||||
|
||||
void fadePointerLocked();
|
||||
|
||||
InputConfiguration mInputConfiguration;
|
||||
void updateInputConfigurationLocked();
|
||||
int32_t mGeneration;
|
||||
int32_t bumpGenerationLocked();
|
||||
|
||||
void getInputDevicesLocked(Vector<InputDeviceInfo>& outInputDevices);
|
||||
|
||||
nsecs_t mDisableVirtualKeysTimeout;
|
||||
void disableVirtualKeysUntilLocked(nsecs_t time);
|
||||
@ -431,12 +508,14 @@ private:
|
||||
/* Represents the state of a single input device. */
|
||||
class InputDevice {
|
||||
public:
|
||||
InputDevice(InputReaderContext* context, int32_t id, const String8& name, uint32_t classes);
|
||||
InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
|
||||
const InputDeviceIdentifier& identifier, uint32_t classes);
|
||||
~InputDevice();
|
||||
|
||||
inline InputReaderContext* getContext() { return mContext; }
|
||||
inline int32_t getId() { return mId; }
|
||||
inline const String8& getName() { return mName; }
|
||||
inline int32_t getGeneration() { return mGeneration; }
|
||||
inline const String8& getName() { return mIdentifier.name; }
|
||||
inline uint32_t getClasses() { return mClasses; }
|
||||
inline uint32_t getSources() { return mSources; }
|
||||
|
||||
@ -458,11 +537,15 @@ public:
|
||||
int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
|
||||
bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
|
||||
const int32_t* keyCodes, uint8_t* outFlags);
|
||||
void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
|
||||
void cancelVibrate(int32_t token);
|
||||
|
||||
int32_t getMetaState();
|
||||
|
||||
void fadePointer();
|
||||
|
||||
void bumpGeneration();
|
||||
|
||||
void notifyReset(nsecs_t when);
|
||||
|
||||
inline const PropertyMap& getConfiguration() { return mConfiguration; }
|
||||
@ -472,6 +555,12 @@ public:
|
||||
return getEventHub()->hasScanCode(mId, code);
|
||||
}
|
||||
|
||||
bool hasAbsoluteAxis(int32_t code) {
|
||||
RawAbsoluteAxisInfo info;
|
||||
getEventHub()->getAbsoluteAxisInfo(mId, code, &info);
|
||||
return info.valid;
|
||||
}
|
||||
|
||||
bool isKeyPressed(int32_t code) {
|
||||
return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
|
||||
}
|
||||
@ -485,7 +574,9 @@ public:
|
||||
private:
|
||||
InputReaderContext* mContext;
|
||||
int32_t mId;
|
||||
String8 mName;
|
||||
int32_t mGeneration;
|
||||
InputDeviceIdentifier mIdentifier;
|
||||
String8 mAlias;
|
||||
uint32_t mClasses;
|
||||
|
||||
Vector<InputMapper*> mMappers;
|
||||
@ -591,9 +682,11 @@ public:
|
||||
int32_t getToolType() const;
|
||||
bool isToolActive() const;
|
||||
bool isHovering() const;
|
||||
bool hasStylus() const;
|
||||
|
||||
private:
|
||||
bool mHaveBtnTouch;
|
||||
bool mHaveStylus;
|
||||
|
||||
bool mBtnTouch;
|
||||
bool mBtnStylus;
|
||||
@ -699,6 +792,10 @@ struct CookedPointerData {
|
||||
void clear();
|
||||
void copyFrom(const CookedPointerData& other);
|
||||
|
||||
inline const PointerCoords& pointerCoordsForId(uint32_t id) const {
|
||||
return pointerCoords[idToIndex[id]];
|
||||
}
|
||||
|
||||
inline bool isHovering(uint32_t pointerIndex) {
|
||||
return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id);
|
||||
}
|
||||
@ -781,10 +878,11 @@ public:
|
||||
MultiTouchMotionAccumulator();
|
||||
~MultiTouchMotionAccumulator();
|
||||
|
||||
void configure(size_t slotCount, bool usingSlotsProtocol);
|
||||
void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol);
|
||||
void reset(InputDevice* device);
|
||||
void process(const RawEvent* rawEvent);
|
||||
void finishSync();
|
||||
bool hasStylus() const;
|
||||
|
||||
inline size_t getSlotCount() const { return mSlotCount; }
|
||||
inline const Slot* getSlot(size_t index) const { return &mSlots[index]; }
|
||||
@ -794,6 +892,7 @@ private:
|
||||
Slot* mSlots;
|
||||
size_t mSlotCount;
|
||||
bool mUsingSlotsProtocol;
|
||||
bool mHaveStylus;
|
||||
|
||||
void clearSlots(int32_t initialSlot);
|
||||
};
|
||||
@ -837,6 +936,9 @@ public:
|
||||
virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
|
||||
virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
|
||||
const int32_t* keyCodes, uint8_t* outFlags);
|
||||
virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
|
||||
int32_t token);
|
||||
virtual void cancelVibrate(int32_t token);
|
||||
|
||||
virtual int32_t getMetaState();
|
||||
|
||||
@ -847,6 +949,7 @@ protected:
|
||||
InputReaderContext* mContext;
|
||||
|
||||
status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
|
||||
void bumpGeneration();
|
||||
|
||||
static void dumpRawAbsoluteAxisInfo(String8& dump,
|
||||
const RawAbsoluteAxisInfo& axis, const char* name);
|
||||
@ -864,7 +967,40 @@ public:
|
||||
virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
|
||||
|
||||
private:
|
||||
void processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue);
|
||||
uint32_t mUpdatedSwitchValues;
|
||||
uint32_t mUpdatedSwitchMask;
|
||||
|
||||
void processSwitch(int32_t switchCode, int32_t switchValue);
|
||||
void sync(nsecs_t when);
|
||||
};
|
||||
|
||||
|
||||
class VibratorInputMapper : public InputMapper {
|
||||
public:
|
||||
VibratorInputMapper(InputDevice* device);
|
||||
virtual ~VibratorInputMapper();
|
||||
|
||||
virtual uint32_t getSources();
|
||||
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
|
||||
virtual void process(const RawEvent* rawEvent);
|
||||
|
||||
virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
|
||||
int32_t token);
|
||||
virtual void cancelVibrate(int32_t token);
|
||||
virtual void timeoutExpired(nsecs_t when);
|
||||
virtual void dump(String8& dump);
|
||||
|
||||
private:
|
||||
bool mVibrating;
|
||||
nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
|
||||
size_t mPatternSize;
|
||||
ssize_t mRepeat;
|
||||
int32_t mToken;
|
||||
ssize_t mIndex;
|
||||
nsecs_t mNextStepTime;
|
||||
|
||||
void nextStep();
|
||||
void stopVibrating();
|
||||
};
|
||||
|
||||
|
||||
@ -902,6 +1038,8 @@ private:
|
||||
int32_t mMetaState;
|
||||
nsecs_t mDownTime; // time of most recent key down
|
||||
|
||||
int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none
|
||||
|
||||
struct LedState {
|
||||
bool avail; // led is available
|
||||
bool on; // we think the led is currently on
|
||||
@ -912,7 +1050,7 @@ private:
|
||||
|
||||
// Immutable configuration parameters.
|
||||
struct Parameters {
|
||||
int32_t associatedDisplayId;
|
||||
bool hasAssociatedDisplay;
|
||||
bool orientationAware;
|
||||
} mParameters;
|
||||
|
||||
@ -962,7 +1100,7 @@ private:
|
||||
};
|
||||
|
||||
Mode mode;
|
||||
int32_t associatedDisplayId;
|
||||
bool hasAssociatedDisplay;
|
||||
bool orientationAware;
|
||||
} mParameters;
|
||||
|
||||
@ -1047,6 +1185,7 @@ protected:
|
||||
DEVICE_MODE_DISABLED, // input is disabled
|
||||
DEVICE_MODE_DIRECT, // direct mapping (touchscreen)
|
||||
DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad)
|
||||
DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
|
||||
DEVICE_MODE_POINTER, // pointer mapping (pointer)
|
||||
};
|
||||
DeviceMode mDeviceMode;
|
||||
@ -1059,11 +1198,12 @@ protected:
|
||||
enum DeviceType {
|
||||
DEVICE_TYPE_TOUCH_SCREEN,
|
||||
DEVICE_TYPE_TOUCH_PAD,
|
||||
DEVICE_TYPE_TOUCH_NAVIGATION,
|
||||
DEVICE_TYPE_POINTER,
|
||||
};
|
||||
|
||||
DeviceType deviceType;
|
||||
int32_t associatedDisplayId;
|
||||
bool hasAssociatedDisplay;
|
||||
bool associatedDisplayIsExternal;
|
||||
bool orientationAware;
|
||||
|
||||
@ -1082,6 +1222,7 @@ protected:
|
||||
SIZE_CALIBRATION_NONE,
|
||||
SIZE_CALIBRATION_GEOMETRIC,
|
||||
SIZE_CALIBRATION_DIAMETER,
|
||||
SIZE_CALIBRATION_BOX,
|
||||
SIZE_CALIBRATION_AREA,
|
||||
};
|
||||
|
||||
@ -1127,6 +1268,14 @@ protected:
|
||||
bool haveDistanceScale;
|
||||
float distanceScale;
|
||||
|
||||
enum CoverageCalibration {
|
||||
COVERAGE_CALIBRATION_DEFAULT,
|
||||
COVERAGE_CALIBRATION_NONE,
|
||||
COVERAGE_CALIBRATION_BOX,
|
||||
};
|
||||
|
||||
CoverageCalibration coverageCalibration;
|
||||
|
||||
inline void applySizeScaleAndBias(float* outSize) const {
|
||||
if (haveSizeScale) {
|
||||
*outSize *= sizeScale;
|
||||
@ -1186,24 +1335,35 @@ protected:
|
||||
virtual void parseCalibration();
|
||||
virtual void resolveCalibration();
|
||||
virtual void dumpCalibration(String8& dump);
|
||||
virtual bool hasStylus() const = 0;
|
||||
|
||||
virtual void syncTouch(nsecs_t when, bool* outHavePointerIds) = 0;
|
||||
|
||||
private:
|
||||
// The surface orientation and width and height set by configureSurface().
|
||||
int32_t mSurfaceOrientation;
|
||||
// The current viewport.
|
||||
// The components of the viewport are specified in the display's rotated orientation.
|
||||
DisplayViewport mViewport;
|
||||
|
||||
// The surface orientation, width and height set by configureSurface().
|
||||
// The width and height are derived from the viewport but are specified
|
||||
// in the natural orientation.
|
||||
// The surface origin specifies how the surface coordinates should be translated
|
||||
// to align with the logical display coordinate space.
|
||||
// The orientation may be different from the viewport orientation as it specifies
|
||||
// the rotation of the surface coordinates required to produce the viewport's
|
||||
// requested orientation, so it will depend on whether the device is orientation aware.
|
||||
int32_t mSurfaceWidth;
|
||||
int32_t mSurfaceHeight;
|
||||
|
||||
// The associated display orientation and width and height set by configureSurface().
|
||||
int32_t mAssociatedDisplayOrientation;
|
||||
int32_t mAssociatedDisplayWidth;
|
||||
int32_t mAssociatedDisplayHeight;
|
||||
int32_t mSurfaceLeft;
|
||||
int32_t mSurfaceTop;
|
||||
int32_t mSurfaceOrientation;
|
||||
|
||||
// Translation and scaling factors, orientation-independent.
|
||||
float mXTranslate;
|
||||
float mXScale;
|
||||
float mXPrecision;
|
||||
|
||||
float mYTranslate;
|
||||
float mYScale;
|
||||
float mYPrecision;
|
||||
|
||||
@ -1213,7 +1373,6 @@ private:
|
||||
|
||||
float mSizeScale;
|
||||
|
||||
float mOrientationCenter;
|
||||
float mOrientationScale;
|
||||
|
||||
float mDistanceScale;
|
||||
@ -1265,8 +1424,6 @@ private:
|
||||
} mOrientedRanges;
|
||||
|
||||
// Oriented dimensions and precision.
|
||||
float mOrientedSurfaceWidth;
|
||||
float mOrientedSurfaceHeight;
|
||||
float mOrientedXPrecision;
|
||||
float mOrientedYPrecision;
|
||||
|
||||
@ -1534,6 +1691,7 @@ public:
|
||||
protected:
|
||||
virtual void syncTouch(nsecs_t when, bool* outHavePointerIds);
|
||||
virtual void configureRawPointerAxes();
|
||||
virtual bool hasStylus() const;
|
||||
|
||||
private:
|
||||
SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
|
||||
@ -1551,6 +1709,7 @@ public:
|
||||
protected:
|
||||
virtual void syncTouch(nsecs_t when, bool* outHavePointerIds);
|
||||
virtual void configureRawPointerAxes();
|
||||
virtual bool hasStylus() const;
|
||||
|
||||
private:
|
||||
MultiTouchMotionAccumulator mMultiTouchMotionAccumulator;
|
||||
@ -1585,10 +1744,11 @@ private:
|
||||
float highScale; // scale factor from raw to normalized values of high split
|
||||
float highOffset; // offset to add after scaling for normalization of high split
|
||||
|
||||
float min; // normalized inclusive minimum
|
||||
float max; // normalized inclusive maximum
|
||||
float flat; // normalized flat region size
|
||||
float fuzz; // normalized error tolerance
|
||||
float min; // normalized inclusive minimum
|
||||
float max; // normalized inclusive maximum
|
||||
float flat; // normalized flat region size
|
||||
float fuzz; // normalized error tolerance
|
||||
float resolution; // normalized resolution in units/mm
|
||||
|
||||
float filter; // filter out small variations of this size
|
||||
float currentValue; // current value
|
||||
@ -1599,7 +1759,7 @@ private:
|
||||
void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
|
||||
bool explicitlyMapped, float scale, float offset,
|
||||
float highScale, float highOffset,
|
||||
float min, float max, float flat, float fuzz) {
|
||||
float min, float max, float flat, float fuzz, float resolution) {
|
||||
this->rawAxisInfo = rawAxisInfo;
|
||||
this->axisInfo = axisInfo;
|
||||
this->explicitlyMapped = explicitlyMapped;
|
||||
@ -1611,6 +1771,7 @@ private:
|
||||
this->max = max;
|
||||
this->flat = flat;
|
||||
this->fuzz = fuzz;
|
||||
this->resolution = resolution;
|
||||
this->filter = 0;
|
||||
resetValue();
|
||||
}
|
||||
@ -1638,6 +1799,11 @@ private:
|
||||
float newValue, float currentValue, float thresholdValue);
|
||||
|
||||
static bool isCenteredAxis(int32_t axis);
|
||||
static int32_t getCompatAxis(int32_t axis);
|
||||
|
||||
static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info);
|
||||
static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
|
||||
float value);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,114 +14,50 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _UI_INPUT_TRANSPORT_H
|
||||
#define _UI_INPUT_TRANSPORT_H
|
||||
#ifndef _ANDROIDFW_INPUT_TRANSPORT_H
|
||||
#define _ANDROIDFW_INPUT_TRANSPORT_H
|
||||
|
||||
/**
|
||||
* Native input transport.
|
||||
*
|
||||
* Uses anonymous shared memory as a whiteboard for sending input events from an
|
||||
* InputPublisher to an InputConsumer and ensuring appropriate synchronization.
|
||||
* One interesting feature is that published events can be updated in place as long as they
|
||||
* have not yet been consumed.
|
||||
* The InputChannel provides a mechanism for exchanging InputMessage structures across processes.
|
||||
*
|
||||
* The InputPublisher and InputConsumer only take care of transferring event data
|
||||
* over an InputChannel and sending synchronization signals. The InputDispatcher and InputQueue
|
||||
* build on these abstractions to add multiplexing and queueing.
|
||||
* The InputPublisher and InputConsumer each handle one end-point of an input channel.
|
||||
* The InputPublisher is used by the input dispatcher to send events to the application.
|
||||
* The InputConsumer is used by the application to receive events from the input dispatcher.
|
||||
*/
|
||||
|
||||
#include <semaphore.h>
|
||||
#include "Input.h"
|
||||
#include <utils/Errors.h>
|
||||
#include "Timers.h"
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include "String8.h"
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/BitSet.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* An input channel consists of a shared memory buffer and a pair of pipes
|
||||
* used to send input messages from an InputPublisher to an InputConsumer
|
||||
* across processes. Each channel has a descriptive name for debugging purposes.
|
||||
*
|
||||
* Each endpoint has its own InputChannel object that specifies its own file descriptors.
|
||||
*
|
||||
* The input channel is closed when all references to it are released.
|
||||
*/
|
||||
class InputChannel : public RefBase {
|
||||
protected:
|
||||
virtual ~InputChannel();
|
||||
|
||||
public:
|
||||
InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
|
||||
int32_t sendPipeFd);
|
||||
|
||||
/* Creates a pair of input channels and their underlying shared memory buffers
|
||||
* and pipes.
|
||||
*
|
||||
* Returns OK on success.
|
||||
*/
|
||||
static status_t openInputChannelPair(const String8& name,
|
||||
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
|
||||
|
||||
inline String8 getName() const { return mName; }
|
||||
inline int32_t getAshmemFd() const { return mAshmemFd; }
|
||||
inline int32_t getReceivePipeFd() const { return mReceivePipeFd; }
|
||||
inline int32_t getSendPipeFd() const { return mSendPipeFd; }
|
||||
|
||||
/* Sends a signal to the other endpoint.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t sendSignal(char signal);
|
||||
|
||||
/* Receives a signal send by the other endpoint.
|
||||
* (Should only call this after poll() indicates that the receivePipeFd has available input.)
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns WOULD_BLOCK if there is no signal present.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t receiveSignal(char* outSignal);
|
||||
|
||||
private:
|
||||
String8 mName;
|
||||
int32_t mAshmemFd;
|
||||
int32_t mReceivePipeFd;
|
||||
int32_t mSendPipeFd;
|
||||
};
|
||||
|
||||
/*
|
||||
* Private intermediate representation of input events as messages written into an
|
||||
* ashmem buffer.
|
||||
* Intermediate representation used to send input events and related signals.
|
||||
*/
|
||||
struct InputMessage {
|
||||
/* Semaphore count is set to 1 when the message is published.
|
||||
* It becomes 0 transiently while the publisher updates the message.
|
||||
* It becomes 0 permanently when the consumer consumes the message.
|
||||
*/
|
||||
sem_t semaphore;
|
||||
|
||||
/* Initialized to false by the publisher.
|
||||
* Set to true by the consumer when it consumes the message.
|
||||
*/
|
||||
bool consumed;
|
||||
|
||||
int32_t type;
|
||||
|
||||
struct SampleData {
|
||||
nsecs_t eventTime;
|
||||
PointerCoords coords[0]; // variable length
|
||||
enum {
|
||||
TYPE_KEY = 1,
|
||||
TYPE_MOTION = 2,
|
||||
TYPE_FINISHED = 3,
|
||||
};
|
||||
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
struct Header {
|
||||
uint32_t type;
|
||||
uint32_t padding; // 8 byte alignment for the body that follows
|
||||
} header;
|
||||
|
||||
union {
|
||||
struct {
|
||||
union Body {
|
||||
struct Key {
|
||||
uint32_t seq;
|
||||
nsecs_t eventTime;
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
int32_t action;
|
||||
int32_t flags;
|
||||
int32_t keyCode;
|
||||
@ -129,10 +65,17 @@ struct InputMessage {
|
||||
int32_t metaState;
|
||||
int32_t repeatCount;
|
||||
nsecs_t downTime;
|
||||
nsecs_t eventTime;
|
||||
|
||||
inline size_t size() const {
|
||||
return sizeof(Key);
|
||||
}
|
||||
} key;
|
||||
|
||||
struct {
|
||||
struct Motion {
|
||||
uint32_t seq;
|
||||
nsecs_t eventTime;
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
int32_t action;
|
||||
int32_t flags;
|
||||
int32_t metaState;
|
||||
@ -144,28 +87,97 @@ struct InputMessage {
|
||||
float xPrecision;
|
||||
float yPrecision;
|
||||
size_t pointerCount;
|
||||
PointerProperties pointerProperties[MAX_POINTERS];
|
||||
size_t sampleCount;
|
||||
SampleData sampleData[0]; // variable length
|
||||
struct Pointer {
|
||||
PointerProperties properties;
|
||||
PointerCoords coords;
|
||||
} pointers[MAX_POINTERS];
|
||||
|
||||
int32_t getActionId() const {
|
||||
uint32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
|
||||
>> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
|
||||
return pointers[index].properties.id;
|
||||
}
|
||||
|
||||
inline size_t size() const {
|
||||
return sizeof(Motion) - sizeof(Pointer) * MAX_POINTERS
|
||||
+ sizeof(Pointer) * pointerCount;
|
||||
}
|
||||
} motion;
|
||||
};
|
||||
|
||||
/* Gets the number of bytes to add to step to the next SampleData object in a motion
|
||||
* event message for a given number of pointers.
|
||||
*/
|
||||
static inline size_t sampleDataStride(size_t pointerCount) {
|
||||
return sizeof(InputMessage::SampleData) + pointerCount * sizeof(PointerCoords);
|
||||
}
|
||||
struct Finished {
|
||||
uint32_t seq;
|
||||
bool handled;
|
||||
|
||||
/* Adds the SampleData stride to the given pointer. */
|
||||
static inline SampleData* sampleDataPtrIncrement(SampleData* ptr, size_t stride) {
|
||||
return reinterpret_cast<InputMessage::SampleData*>(reinterpret_cast<char*>(ptr) + stride);
|
||||
}
|
||||
inline size_t size() const {
|
||||
return sizeof(Finished);
|
||||
}
|
||||
} finished;
|
||||
} body;
|
||||
|
||||
bool isValid(size_t actualSize) const;
|
||||
size_t size() const;
|
||||
};
|
||||
|
||||
/*
|
||||
* Publishes input events to an anonymous shared memory buffer.
|
||||
* Uses atomic operations to coordinate shared access with a single concurrent consumer.
|
||||
* An input channel consists of a local unix domain socket used to send and receive
|
||||
* input messages across processes. Each channel has a descriptive name for debugging purposes.
|
||||
*
|
||||
* Each endpoint has its own InputChannel object that specifies its file descriptor.
|
||||
*
|
||||
* The input channel is closed when all references to it are released.
|
||||
*/
|
||||
class InputChannel : public RefBase {
|
||||
protected:
|
||||
virtual ~InputChannel();
|
||||
|
||||
public:
|
||||
InputChannel(const String8& name, int fd);
|
||||
|
||||
/* Creates a pair of input channels.
|
||||
*
|
||||
* Returns OK on success.
|
||||
*/
|
||||
static status_t openInputChannelPair(const String8& name,
|
||||
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
|
||||
|
||||
inline String8 getName() const { return mName; }
|
||||
inline int getFd() const { return mFd; }
|
||||
|
||||
/* Sends a message to the other endpoint.
|
||||
*
|
||||
* If the channel is full then the message is guaranteed not to have been sent at all.
|
||||
* Try again after the consumer has sent a finished signal indicating that it has
|
||||
* consumed some of the pending messages from the channel.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns WOULD_BLOCK if the channel is full.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t sendMessage(const InputMessage* msg);
|
||||
|
||||
/* Receives a message sent by the other endpoint.
|
||||
*
|
||||
* If there is no message present, try again after poll() indicates that the fd
|
||||
* is readable.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns WOULD_BLOCK if there is no message present.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t receiveMessage(InputMessage* msg);
|
||||
|
||||
/* Returns a new object that has a duplicate of this channel's fd. */
|
||||
sp<InputChannel> dup() const;
|
||||
|
||||
private:
|
||||
String8 mName;
|
||||
int mFd;
|
||||
};
|
||||
|
||||
/*
|
||||
* Publishes input events to an input channel.
|
||||
*/
|
||||
class InputPublisher {
|
||||
public:
|
||||
@ -178,26 +190,16 @@ public:
|
||||
/* Gets the underlying input channel. */
|
||||
inline sp<InputChannel> getChannel() { return mChannel; }
|
||||
|
||||
/* Prepares the publisher for use. Must be called before it is used.
|
||||
* Returns OK on success.
|
||||
*
|
||||
* This method implicitly calls reset(). */
|
||||
status_t initialize();
|
||||
|
||||
/* Resets the publisher to its initial state and unpins its ashmem buffer.
|
||||
* Returns OK on success.
|
||||
*
|
||||
* Should be called after an event has been consumed to release resources used by the
|
||||
* publisher until the next event is ready to be published.
|
||||
*/
|
||||
status_t reset();
|
||||
|
||||
/* Publishes a key event to the ashmem buffer.
|
||||
/* Publishes a key event to the input channel.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns INVALID_OPERATION if the publisher has not been reset.
|
||||
* Returns WOULD_BLOCK if the channel is full.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Returns BAD_VALUE if seq is 0.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t publishKeyEvent(
|
||||
uint32_t seq,
|
||||
int32_t deviceId,
|
||||
int32_t source,
|
||||
int32_t action,
|
||||
@ -209,13 +211,16 @@ public:
|
||||
nsecs_t downTime,
|
||||
nsecs_t eventTime);
|
||||
|
||||
/* Publishes a motion event to the ashmem buffer.
|
||||
/* Publishes a motion event to the input channel.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns INVALID_OPERATION if the publisher has not been reset.
|
||||
* Returns BAD_VALUE if pointerCount is less than 1 or greater than MAX_POINTERS.
|
||||
* Returns WOULD_BLOCK if the channel is full.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t publishMotionEvent(
|
||||
uint32_t seq,
|
||||
int32_t deviceId,
|
||||
int32_t source,
|
||||
int32_t action,
|
||||
@ -233,55 +238,25 @@ public:
|
||||
const PointerProperties* pointerProperties,
|
||||
const PointerCoords* pointerCoords);
|
||||
|
||||
/* Appends a motion sample to a motion event unless already consumed.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns INVALID_OPERATION if the current event is not a AMOTION_EVENT_ACTION_MOVE event.
|
||||
* Returns FAILED_TRANSACTION if the current event has already been consumed.
|
||||
* Returns NO_MEMORY if the buffer is full and no additional samples can be added.
|
||||
*/
|
||||
status_t appendMotionSample(
|
||||
nsecs_t eventTime,
|
||||
const PointerCoords* pointerCoords);
|
||||
|
||||
/* Sends a dispatch signal to the consumer to inform it that a new message is available.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t sendDispatchSignal();
|
||||
|
||||
/* Receives the finished signal from the consumer in reply to the original dispatch signal.
|
||||
* Returns whether the consumer handled the message.
|
||||
* If a signal was received, returns the message sequence number,
|
||||
* and whether the consumer handled the message.
|
||||
*
|
||||
* The returned sequence number is never 0 unless the operation failed.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns WOULD_BLOCK if there is no signal present.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t receiveFinishedSignal(bool* outHandled);
|
||||
status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
|
||||
|
||||
private:
|
||||
sp<InputChannel> mChannel;
|
||||
|
||||
size_t mAshmemSize;
|
||||
InputMessage* mSharedMessage;
|
||||
bool mPinned;
|
||||
bool mSemaphoreInitialized;
|
||||
bool mWasDispatched;
|
||||
|
||||
size_t mMotionEventPointerCount;
|
||||
InputMessage::SampleData* mMotionEventSampleDataTail;
|
||||
size_t mMotionEventSampleDataStride;
|
||||
|
||||
status_t publishInputEvent(
|
||||
int32_t type,
|
||||
int32_t deviceId,
|
||||
int32_t source);
|
||||
};
|
||||
|
||||
/*
|
||||
* Consumes input events from an anonymous shared memory buffer.
|
||||
* Uses atomic operations to coordinate shared access with a single concurrent publisher.
|
||||
* Consumes input events from an input channel.
|
||||
*/
|
||||
class InputConsumer {
|
||||
public:
|
||||
@ -294,45 +269,175 @@ public:
|
||||
/* Gets the underlying input channel. */
|
||||
inline sp<InputChannel> getChannel() { return mChannel; }
|
||||
|
||||
/* Prepares the consumer for use. Must be called before it is used. */
|
||||
status_t initialize();
|
||||
|
||||
/* Consumes the input event in the buffer and copies its contents into
|
||||
/* Consumes an input event from the input channel and copies its contents into
|
||||
* an InputEvent object created using the specified factory.
|
||||
* This operation will block if the publisher is updating the event.
|
||||
*
|
||||
* Tries to combine a series of move events into larger batches whenever possible.
|
||||
*
|
||||
* If consumeBatches is false, then defers consuming pending batched events if it
|
||||
* is possible for additional samples to be added to them later. Call hasPendingBatch()
|
||||
* to determine whether a pending batch is available to be consumed.
|
||||
*
|
||||
* If consumeBatches is true, then events are still batched but they are consumed
|
||||
* immediately as soon as the input channel is exhausted.
|
||||
*
|
||||
* The frameTime parameter specifies the time when the current display frame started
|
||||
* rendering in the CLOCK_MONOTONIC time base, or -1 if unknown.
|
||||
*
|
||||
* The returned sequence number is never 0 unless the operation failed.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns INVALID_OPERATION if there is no currently published event.
|
||||
* Returns WOULD_BLOCK if there is no event present.
|
||||
* Returns DEAD_OBJECT if the channel's peer has been closed.
|
||||
* Returns NO_MEMORY if the event could not be created.
|
||||
*/
|
||||
status_t consume(InputEventFactoryInterface* factory, InputEvent** outEvent);
|
||||
|
||||
/* Sends a finished signal to the publisher to inform it that the current message is
|
||||
* finished processing and specifies whether the message was handled by the consumer.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t sendFinishedSignal(bool handled);
|
||||
|
||||
/* Receives the dispatched signal from the publisher.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns WOULD_BLOCK if there is no signal present.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t receiveDispatchSignal();
|
||||
status_t consume(InputEventFactoryInterface* factory, bool consumeBatches,
|
||||
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
|
||||
|
||||
/* Sends a finished signal to the publisher to inform it that the message
|
||||
* with the specified sequence number has finished being process and whether
|
||||
* the message was handled by the consumer.
|
||||
*
|
||||
* Returns OK on success.
|
||||
* Returns BAD_VALUE if seq is 0.
|
||||
* Other errors probably indicate that the channel is broken.
|
||||
*/
|
||||
status_t sendFinishedSignal(uint32_t seq, bool handled);
|
||||
|
||||
/* Returns true if there is a deferred event waiting.
|
||||
*
|
||||
* Should be called after calling consume() to determine whether the consumer
|
||||
* has a deferred event to be processed. Deferred events are somewhat special in
|
||||
* that they have already been removed from the input channel. If the input channel
|
||||
* becomes empty, the client may need to do extra work to ensure that it processes
|
||||
* the deferred event despite the fact that the input channel's file descriptor
|
||||
* is not readable.
|
||||
*
|
||||
* One option is simply to call consume() in a loop until it returns WOULD_BLOCK.
|
||||
* This guarantees that all deferred events will be processed.
|
||||
*
|
||||
* Alternately, the caller can call hasDeferredEvent() to determine whether there is
|
||||
* a deferred event waiting and then ensure that its event loop wakes up at least
|
||||
* one more time to consume the deferred event.
|
||||
*/
|
||||
bool hasDeferredEvent() const;
|
||||
|
||||
/* Returns true if there is a pending batch.
|
||||
*
|
||||
* Should be called after calling consume() with consumeBatches == false to determine
|
||||
* whether consume() should be called again later on with consumeBatches == true.
|
||||
*/
|
||||
bool hasPendingBatch() const;
|
||||
|
||||
private:
|
||||
// True if touch resampling is enabled.
|
||||
const bool mResampleTouch;
|
||||
|
||||
// The input channel.
|
||||
sp<InputChannel> mChannel;
|
||||
|
||||
size_t mAshmemSize;
|
||||
InputMessage* mSharedMessage;
|
||||
// The current input message.
|
||||
InputMessage mMsg;
|
||||
|
||||
void populateKeyEvent(KeyEvent* keyEvent) const;
|
||||
void populateMotionEvent(MotionEvent* motionEvent) const;
|
||||
// True if mMsg contains a valid input message that was deferred from the previous
|
||||
// call to consume and that still needs to be handled.
|
||||
bool mMsgDeferred;
|
||||
|
||||
// Batched motion events per device and source.
|
||||
struct Batch {
|
||||
Vector<InputMessage> samples;
|
||||
};
|
||||
Vector<Batch> mBatches;
|
||||
|
||||
// Touch state per device and source, only for sources of class pointer.
|
||||
struct History {
|
||||
nsecs_t eventTime;
|
||||
BitSet32 idBits;
|
||||
int32_t idToIndex[MAX_POINTER_ID + 1];
|
||||
PointerCoords pointers[MAX_POINTERS];
|
||||
|
||||
void initializeFrom(const InputMessage* msg) {
|
||||
eventTime = msg->body.motion.eventTime;
|
||||
idBits.clear();
|
||||
for (size_t i = 0; i < msg->body.motion.pointerCount; i++) {
|
||||
uint32_t id = msg->body.motion.pointers[i].properties.id;
|
||||
idBits.markBit(id);
|
||||
idToIndex[id] = i;
|
||||
pointers[i].copyFrom(msg->body.motion.pointers[i].coords);
|
||||
}
|
||||
}
|
||||
|
||||
const PointerCoords& getPointerById(uint32_t id) const {
|
||||
return pointers[idToIndex[id]];
|
||||
}
|
||||
};
|
||||
struct TouchState {
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
size_t historyCurrent;
|
||||
size_t historySize;
|
||||
History history[2];
|
||||
History lastResample;
|
||||
|
||||
void initialize(int32_t deviceId, int32_t source) {
|
||||
this->deviceId = deviceId;
|
||||
this->source = source;
|
||||
historyCurrent = 0;
|
||||
historySize = 0;
|
||||
lastResample.eventTime = 0;
|
||||
lastResample.idBits.clear();
|
||||
}
|
||||
|
||||
void addHistory(const InputMessage* msg) {
|
||||
historyCurrent ^= 1;
|
||||
if (historySize < 2) {
|
||||
historySize += 1;
|
||||
}
|
||||
history[historyCurrent].initializeFrom(msg);
|
||||
}
|
||||
|
||||
const History* getHistory(size_t index) const {
|
||||
return &history[(historyCurrent + index) & 1];
|
||||
}
|
||||
};
|
||||
Vector<TouchState> mTouchStates;
|
||||
|
||||
// Chain of batched sequence numbers. When multiple input messages are combined into
|
||||
// a batch, we append a record here that associates the last sequence number in the
|
||||
// batch with the previous one. When the finished signal is sent, we traverse the
|
||||
// chain to individually finish all input messages that were part of the batch.
|
||||
struct SeqChain {
|
||||
uint32_t seq; // sequence number of batched input message
|
||||
uint32_t chain; // sequence number of previous batched input message
|
||||
};
|
||||
Vector<SeqChain> mSeqChains;
|
||||
|
||||
status_t consumeBatch(InputEventFactoryInterface* factory,
|
||||
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
|
||||
status_t consumeSamples(InputEventFactoryInterface* factory,
|
||||
Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent);
|
||||
|
||||
void updateTouchState(InputMessage* msg);
|
||||
void rewriteMessage(const TouchState& state, InputMessage* msg);
|
||||
void resampleTouchState(nsecs_t frameTime, MotionEvent* event,
|
||||
const InputMessage *next);
|
||||
|
||||
ssize_t findBatch(int32_t deviceId, int32_t source) const;
|
||||
ssize_t findTouchState(int32_t deviceId, int32_t source) const;
|
||||
|
||||
status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
|
||||
|
||||
static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
|
||||
static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
|
||||
static void addSample(MotionEvent* event, const InputMessage* msg);
|
||||
static bool canAddSample(const Batch& batch, const InputMessage* msg);
|
||||
static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
|
||||
static bool shouldResampleTool(int32_t toolType);
|
||||
|
||||
static bool isTouchResamplingEnabled();
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UI_INPUT_TRANSPORT_H
|
||||
#endif // _ANDROIDFW_INPUT_TRANSPORT_H
|
||||
|
@ -25,11 +25,7 @@ namespace android {
|
||||
// --- InputWindowInfo ---
|
||||
|
||||
bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
return touchableRegion.contains(x, y);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
|
||||
|
@ -20,10 +20,10 @@
|
||||
#include "Input.h"
|
||||
#include "InputTransport.h"
|
||||
#include <utils/RefBase.h>
|
||||
#include "Timers.h"
|
||||
#include "String8.h"
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
#include "SkRegion.h"
|
||||
#include <SkRegion.h>
|
||||
|
||||
#include "InputApplication.h"
|
||||
|
||||
@ -110,6 +110,8 @@ struct InputWindowInfo {
|
||||
|
||||
enum {
|
||||
INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
|
||||
INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
|
||||
INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
|
||||
};
|
||||
|
||||
sp<InputChannel> inputChannel;
|
||||
@ -122,9 +124,7 @@ struct InputWindowInfo {
|
||||
int32_t frameRight;
|
||||
int32_t frameBottom;
|
||||
float scaleFactor;
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
SkRegion touchableRegion;
|
||||
#endif
|
||||
bool visible;
|
||||
bool canReceiveKeys;
|
||||
bool hasFocus;
|
||||
@ -134,6 +134,7 @@ struct InputWindowInfo {
|
||||
int32_t ownerPid;
|
||||
int32_t ownerUid;
|
||||
int32_t inputFeatures;
|
||||
int32_t displayId;
|
||||
|
||||
bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
|
||||
bool frameContainsPoint(int32_t x, int32_t y) const;
|
||||
|
@ -15,16 +15,21 @@
|
||||
*/
|
||||
|
||||
#define LOG_TAG "KeyCharacterMap"
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "utils_Log.h"
|
||||
#include "android_keycodes.h"
|
||||
#include "Keyboard.h"
|
||||
#include "KeyCharacterMap.h"
|
||||
|
||||
#if HAVE_ANDROID_OS
|
||||
#include <binder/Parcel.h>
|
||||
#endif
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include "Tokenizer.h"
|
||||
#include "Timers.h"
|
||||
#include <utils/Timers.h>
|
||||
|
||||
// Enables debug output for the parser.
|
||||
#define DEBUG_PARSER 0
|
||||
@ -78,10 +83,20 @@ static String8 toString(const char16_t* chars, size_t numChars) {
|
||||
|
||||
// --- KeyCharacterMap ---
|
||||
|
||||
sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap();
|
||||
|
||||
KeyCharacterMap::KeyCharacterMap() :
|
||||
mType(KEYBOARD_TYPE_UNKNOWN) {
|
||||
}
|
||||
|
||||
KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) :
|
||||
RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode),
|
||||
mKeysByUsageCode(other.mKeysByUsageCode) {
|
||||
for (size_t i = 0; i < other.mKeys.size(); i++) {
|
||||
mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
|
||||
}
|
||||
}
|
||||
|
||||
KeyCharacterMap::~KeyCharacterMap() {
|
||||
for (size_t i = 0; i < mKeys.size(); i++) {
|
||||
Key* key = mKeys.editValueAt(i);
|
||||
@ -89,41 +104,100 @@ KeyCharacterMap::~KeyCharacterMap() {
|
||||
}
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::load(const String8& filename, KeyCharacterMap** outMap) {
|
||||
*outMap = NULL;
|
||||
status_t KeyCharacterMap::load(const String8& filename,
|
||||
Format format, sp<KeyCharacterMap>* outMap) {
|
||||
outMap->clear();
|
||||
|
||||
Tokenizer* tokenizer;
|
||||
status_t status = Tokenizer::open(filename, &tokenizer);
|
||||
if (status) {
|
||||
ALOGE("Error %d opening key character map file %s.", status, filename.string());
|
||||
} else {
|
||||
KeyCharacterMap* map = new KeyCharacterMap();
|
||||
if (!map) {
|
||||
ALOGE("Error allocating key character map.");
|
||||
status = NO_MEMORY;
|
||||
} else {
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
#endif
|
||||
Parser parser(map, tokenizer);
|
||||
status = parser.parse();
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
|
||||
ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
|
||||
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
|
||||
elapsedTime / 1000000.0);
|
||||
#endif
|
||||
if (status) {
|
||||
delete map;
|
||||
} else {
|
||||
*outMap = map;
|
||||
}
|
||||
}
|
||||
status = load(tokenizer, format, outMap);
|
||||
delete tokenizer;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents,
|
||||
Format format, sp<KeyCharacterMap>* outMap) {
|
||||
outMap->clear();
|
||||
|
||||
Tokenizer* tokenizer;
|
||||
status_t status = Tokenizer::fromContents(filename, contents, &tokenizer);
|
||||
if (status) {
|
||||
ALOGE("Error %d opening key character map.", status);
|
||||
} else {
|
||||
status = load(tokenizer, format, outMap);
|
||||
delete tokenizer;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::load(Tokenizer* tokenizer,
|
||||
Format format, sp<KeyCharacterMap>* outMap) {
|
||||
status_t status = OK;
|
||||
sp<KeyCharacterMap> map = new KeyCharacterMap();
|
||||
if (!map.get()) {
|
||||
ALOGE("Error allocating key character map.");
|
||||
status = NO_MEMORY;
|
||||
} else {
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
#endif
|
||||
Parser parser(map.get(), tokenizer, format);
|
||||
status = parser.parse();
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
|
||||
ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
|
||||
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
|
||||
elapsedTime / 1000000.0);
|
||||
#endif
|
||||
if (!status) {
|
||||
*outMap = map;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
|
||||
const sp<KeyCharacterMap>& overlay) {
|
||||
if (overlay == NULL) {
|
||||
return base;
|
||||
}
|
||||
if (base == NULL) {
|
||||
return overlay;
|
||||
}
|
||||
|
||||
sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get());
|
||||
for (size_t i = 0; i < overlay->mKeys.size(); i++) {
|
||||
int32_t keyCode = overlay->mKeys.keyAt(i);
|
||||
Key* key = overlay->mKeys.valueAt(i);
|
||||
ssize_t oldIndex = map->mKeys.indexOfKey(keyCode);
|
||||
if (oldIndex >= 0) {
|
||||
delete map->mKeys.valueAt(oldIndex);
|
||||
map->mKeys.editValueAt(oldIndex) = new Key(*key);
|
||||
} else {
|
||||
map->mKeys.add(keyCode, new Key(*key));
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) {
|
||||
map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i),
|
||||
overlay->mKeysByScanCode.valueAt(i));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) {
|
||||
map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i),
|
||||
overlay->mKeysByUsageCode.valueAt(i));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
sp<KeyCharacterMap> KeyCharacterMap::empty() {
|
||||
return sEmpty;
|
||||
}
|
||||
|
||||
int32_t KeyCharacterMap::getKeyboardType() const {
|
||||
return mType;
|
||||
}
|
||||
@ -252,6 +326,37 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t
|
||||
return true;
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const {
|
||||
if (usageCode) {
|
||||
ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
|
||||
if (index >= 0) {
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
|
||||
scanCode, usageCode, *outKeyCode);
|
||||
#endif
|
||||
*outKeyCode = mKeysByUsageCode.valueAt(index);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
if (scanCode) {
|
||||
ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
|
||||
if (index >= 0) {
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
|
||||
scanCode, usageCode, *outKeyCode);
|
||||
#endif
|
||||
*outKeyCode = mKeysByScanCode.valueAt(index);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode);
|
||||
#endif
|
||||
*outKeyCode = AKEYCODE_UNKNOWN;
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
|
||||
ssize_t index = mKeys.indexOfKey(keyCode);
|
||||
if (index >= 0) {
|
||||
@ -267,7 +372,7 @@ bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState,
|
||||
if (getKey(keyCode, &key)) {
|
||||
const Behavior* behavior = key->firstBehavior;
|
||||
while (behavior) {
|
||||
if ((behavior->metaState & metaState) == behavior->metaState) {
|
||||
if (matchesMetaState(metaState, behavior->metaState)) {
|
||||
*outKey = key;
|
||||
*outBehavior = behavior;
|
||||
return true;
|
||||
@ -278,6 +383,37 @@ bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KeyCharacterMap::matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState) {
|
||||
// Behavior must have at least the set of meta states specified.
|
||||
// And if the key event has CTRL, ALT or META then the behavior must exactly
|
||||
// match those, taking into account that a behavior can specify that it handles
|
||||
// one, both or either of a left/right modifier pair.
|
||||
if ((eventMetaState & behaviorMetaState) == behaviorMetaState) {
|
||||
const int32_t EXACT_META_STATES =
|
||||
AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON
|
||||
| AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON
|
||||
| AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON;
|
||||
int32_t unmatchedMetaState = eventMetaState & ~behaviorMetaState & EXACT_META_STATES;
|
||||
if (behaviorMetaState & AMETA_CTRL_ON) {
|
||||
unmatchedMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
|
||||
} else if (behaviorMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
|
||||
unmatchedMetaState &= ~AMETA_CTRL_ON;
|
||||
}
|
||||
if (behaviorMetaState & AMETA_ALT_ON) {
|
||||
unmatchedMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
|
||||
} else if (behaviorMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
|
||||
unmatchedMetaState &= ~AMETA_ALT_ON;
|
||||
}
|
||||
if (behaviorMetaState & AMETA_META_ON) {
|
||||
unmatchedMetaState &= ~(AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
|
||||
} else if (behaviorMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
|
||||
unmatchedMetaState &= ~AMETA_META_ON;
|
||||
}
|
||||
return !unmatchedMetaState;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const {
|
||||
if (!ch) {
|
||||
return false;
|
||||
@ -419,6 +555,79 @@ void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents,
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_ANDROID_OS
|
||||
sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
|
||||
sp<KeyCharacterMap> map = new KeyCharacterMap();
|
||||
map->mType = parcel->readInt32();
|
||||
size_t numKeys = parcel->readInt32();
|
||||
if (parcel->errorCheck()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < numKeys; i++) {
|
||||
int32_t keyCode = parcel->readInt32();
|
||||
char16_t label = parcel->readInt32();
|
||||
char16_t number = parcel->readInt32();
|
||||
if (parcel->errorCheck()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Key* key = new Key();
|
||||
key->label = label;
|
||||
key->number = number;
|
||||
map->mKeys.add(keyCode, key);
|
||||
|
||||
Behavior* lastBehavior = NULL;
|
||||
while (parcel->readInt32()) {
|
||||
int32_t metaState = parcel->readInt32();
|
||||
char16_t character = parcel->readInt32();
|
||||
int32_t fallbackKeyCode = parcel->readInt32();
|
||||
if (parcel->errorCheck()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Behavior* behavior = new Behavior();
|
||||
behavior->metaState = metaState;
|
||||
behavior->character = character;
|
||||
behavior->fallbackKeyCode = fallbackKeyCode;
|
||||
if (lastBehavior) {
|
||||
lastBehavior->next = behavior;
|
||||
} else {
|
||||
key->firstBehavior = behavior;
|
||||
}
|
||||
lastBehavior = behavior;
|
||||
}
|
||||
|
||||
if (parcel->errorCheck()) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
void KeyCharacterMap::writeToParcel(Parcel* parcel) const {
|
||||
parcel->writeInt32(mType);
|
||||
|
||||
size_t numKeys = mKeys.size();
|
||||
parcel->writeInt32(numKeys);
|
||||
for (size_t i = 0; i < numKeys; i++) {
|
||||
int32_t keyCode = mKeys.keyAt(i);
|
||||
const Key* key = mKeys.valueAt(i);
|
||||
parcel->writeInt32(keyCode);
|
||||
parcel->writeInt32(key->label);
|
||||
parcel->writeInt32(key->number);
|
||||
for (const Behavior* behavior = key->firstBehavior; behavior != NULL;
|
||||
behavior = behavior->next) {
|
||||
parcel->writeInt32(1);
|
||||
parcel->writeInt32(behavior->metaState);
|
||||
parcel->writeInt32(behavior->character);
|
||||
parcel->writeInt32(behavior->fallbackKeyCode);
|
||||
}
|
||||
parcel->writeInt32(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// --- KeyCharacterMap::Key ---
|
||||
|
||||
@ -426,6 +635,11 @@ KeyCharacterMap::Key::Key() :
|
||||
label(0), number(0), firstBehavior(NULL) {
|
||||
}
|
||||
|
||||
KeyCharacterMap::Key::Key(const Key& other) :
|
||||
label(other.label), number(other.number),
|
||||
firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) {
|
||||
}
|
||||
|
||||
KeyCharacterMap::Key::~Key() {
|
||||
Behavior* behavior = firstBehavior;
|
||||
while (behavior) {
|
||||
@ -442,11 +656,17 @@ KeyCharacterMap::Behavior::Behavior() :
|
||||
next(NULL), metaState(0), character(0), fallbackKeyCode(0) {
|
||||
}
|
||||
|
||||
KeyCharacterMap::Behavior::Behavior(const Behavior& other) :
|
||||
next(other.next ? new Behavior(*other.next) : NULL),
|
||||
metaState(other.metaState), character(other.character),
|
||||
fallbackKeyCode(other.fallbackKeyCode) {
|
||||
}
|
||||
|
||||
|
||||
// --- KeyCharacterMap::Parser ---
|
||||
|
||||
KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) :
|
||||
mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) {
|
||||
KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) :
|
||||
mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) {
|
||||
}
|
||||
|
||||
KeyCharacterMap::Parser::~Parser() {
|
||||
@ -469,6 +689,10 @@ status_t KeyCharacterMap::Parser::parse() {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
status_t status = parseType();
|
||||
if (status) return status;
|
||||
} else if (keywordToken == "map") {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
status_t status = parseMap();
|
||||
if (status) return status;
|
||||
} else if (keywordToken == "key") {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
status_t status = parseKey();
|
||||
@ -489,8 +713,8 @@ status_t KeyCharacterMap::Parser::parse() {
|
||||
}
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
if (!mTokenizer->isEol()) {
|
||||
ALOGE("%s: Expected end of line, got '%s'.",
|
||||
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
|
||||
ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
|
||||
mTokenizer->getLocation().string(),
|
||||
mTokenizer->peekRemainderOfLine().string());
|
||||
return BAD_VALUE;
|
||||
@ -507,11 +731,26 @@ status_t KeyCharacterMap::Parser::parse() {
|
||||
}
|
||||
|
||||
if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
|
||||
ALOGE("%s: Missing required keyboard 'type' declaration.",
|
||||
ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.",
|
||||
mTokenizer->getLocation().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if (mFormat == FORMAT_BASE) {
|
||||
if (mMap->mType == KEYBOARD_TYPE_OVERLAY) {
|
||||
ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
|
||||
mTokenizer->getLocation().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
} else if (mFormat == FORMAT_OVERLAY) {
|
||||
if (mMap->mType != KEYBOARD_TYPE_OVERLAY) {
|
||||
ALOGE("%s: Overlay keyboard layout missing required keyboard "
|
||||
"'type OVERLAY' declaration.",
|
||||
mTokenizer->getLocation().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@ -534,6 +773,8 @@ status_t KeyCharacterMap::Parser::parseType() {
|
||||
type = KEYBOARD_TYPE_FULL;
|
||||
} else if (typeToken == "SPECIAL_FUNCTION") {
|
||||
type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
|
||||
} else if (typeToken == "OVERLAY") {
|
||||
type = KEYBOARD_TYPE_OVERLAY;
|
||||
} else {
|
||||
ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
|
||||
typeToken.string());
|
||||
@ -547,6 +788,58 @@ status_t KeyCharacterMap::Parser::parseType() {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::Parser::parseMap() {
|
||||
String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
|
||||
if (keywordToken == "key") {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
return parseMapKey();
|
||||
}
|
||||
ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().string(),
|
||||
keywordToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::Parser::parseMapKey() {
|
||||
String8 codeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
bool mapUsage = false;
|
||||
if (codeToken == "usage") {
|
||||
mapUsage = true;
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
codeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
}
|
||||
|
||||
char* end;
|
||||
int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
|
||||
if (*end) {
|
||||
ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
|
||||
mapUsage ? "usage" : "scan code", codeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
KeyedVector<int32_t, int32_t>& map =
|
||||
mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
|
||||
if (map.indexOfKey(code) >= 0) {
|
||||
ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
|
||||
mapUsage ? "usage" : "scan code", codeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
|
||||
if (!keyCode) {
|
||||
ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
|
||||
keyCodeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
#if DEBUG_PARSER
|
||||
ALOGD("Parsed map key %s: code=%d, keyCode=%d.",
|
||||
mapUsage ? "usage" : "scan code", code, keyCode);
|
||||
#endif
|
||||
map.add(code, keyCode);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::Parser::parseKey() {
|
||||
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
|
||||
@ -579,10 +872,11 @@ status_t KeyCharacterMap::Parser::parseKey() {
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::Parser::parseKeyProperty() {
|
||||
Key* key = mMap->mKeys.valueFor(mKeyCode);
|
||||
String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
|
||||
if (token == "}") {
|
||||
mState = STATE_TOP;
|
||||
return NO_ERROR;
|
||||
return finishKey(key);
|
||||
}
|
||||
|
||||
Vector<Property> properties;
|
||||
@ -679,10 +973,9 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() {
|
||||
}
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
} while (!mTokenizer->isEol());
|
||||
} while (!mTokenizer->isEol() && mTokenizer->peekChar() != '#');
|
||||
|
||||
// Add the behavior.
|
||||
Key* key = mMap->mKeys.valueFor(mKeyCode);
|
||||
for (size_t i = 0; i < properties.size(); i++) {
|
||||
const Property& property = properties.itemAt(i);
|
||||
switch (property.property) {
|
||||
@ -731,6 +1024,28 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::Parser::finishKey(Key* key) {
|
||||
// Fill in default number property.
|
||||
if (!key->number) {
|
||||
char16_t digit = 0;
|
||||
char16_t symbol = 0;
|
||||
for (Behavior* b = key->firstBehavior; b; b = b->next) {
|
||||
char16_t ch = b->character;
|
||||
if (ch) {
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
digit = ch;
|
||||
} else if (ch == '(' || ch == ')' || ch == '#' || ch == '*'
|
||||
|| ch == '-' || ch == '+' || ch == ',' || ch == '.'
|
||||
|| ch == '\'' || ch == ':' || ch == ';' || ch == '/') {
|
||||
symbol = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
key->number = digit ? digit : symbol;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) {
|
||||
if (token == "base") {
|
||||
*outMetaState = 0;
|
||||
|
@ -14,17 +14,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _UI_KEY_CHARACTER_MAP_H
|
||||
#define _UI_KEY_CHARACTER_MAP_H
|
||||
#ifndef _ANDROIDFW_KEY_CHARACTER_MAP_H
|
||||
#define _ANDROIDFW_KEY_CHARACTER_MAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if HAVE_ANDROID_OS
|
||||
#include <binder/IBinder.h>
|
||||
#endif
|
||||
|
||||
#include "Input.h"
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include "Tokenizer.h"
|
||||
#include "String8.h"
|
||||
#include "Unicode.h"
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Unicode.h>
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
@ -32,8 +37,10 @@ namespace android {
|
||||
* Describes a mapping from Android key codes to characters.
|
||||
* Also specifies other functions of the keyboard such as the keyboard type
|
||||
* and key modifier semantics.
|
||||
*
|
||||
* This object is immutable after it has been loaded.
|
||||
*/
|
||||
class KeyCharacterMap {
|
||||
class KeyCharacterMap : public RefBase {
|
||||
public:
|
||||
enum KeyboardType {
|
||||
KEYBOARD_TYPE_UNKNOWN = 0,
|
||||
@ -42,6 +49,17 @@ public:
|
||||
KEYBOARD_TYPE_ALPHA = 3,
|
||||
KEYBOARD_TYPE_FULL = 4,
|
||||
KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
|
||||
KEYBOARD_TYPE_OVERLAY = 6,
|
||||
};
|
||||
|
||||
enum Format {
|
||||
// Base keyboard layout, may contain device-specific options, such as "type" declaration.
|
||||
FORMAT_BASE = 0,
|
||||
// Overlay keyboard layout, more restrictive, may be published by applications,
|
||||
// cannot override device-specific options.
|
||||
FORMAT_OVERLAY = 1,
|
||||
// Either base or overlay layout ok.
|
||||
FORMAT_ANY = 2,
|
||||
};
|
||||
|
||||
// Substitute key code and meta state for fallback action.
|
||||
@ -50,9 +68,19 @@ public:
|
||||
int32_t metaState;
|
||||
};
|
||||
|
||||
~KeyCharacterMap();
|
||||
/* Loads a key character map from a file. */
|
||||
static status_t load(const String8& filename, Format format, sp<KeyCharacterMap>* outMap);
|
||||
|
||||
static status_t load(const String8& filename, KeyCharacterMap** outMap);
|
||||
/* Loads a key character map from its string contents. */
|
||||
static status_t loadContents(const String8& filename,
|
||||
const char* contents, Format format, sp<KeyCharacterMap>* outMap);
|
||||
|
||||
/* Combines a base key character map and an overlay. */
|
||||
static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base,
|
||||
const sp<KeyCharacterMap>& overlay);
|
||||
|
||||
/* Returns an empty key character map. */
|
||||
static sp<KeyCharacterMap> empty();
|
||||
|
||||
/* Gets the keyboard type. */
|
||||
int32_t getKeyboardType() const;
|
||||
@ -92,9 +120,25 @@ public:
|
||||
bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
|
||||
Vector<KeyEvent>& outEvents) const;
|
||||
|
||||
/* Maps a scan code and usage code to a key code, in case this key map overrides
|
||||
* the mapping in some way. */
|
||||
status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const;
|
||||
|
||||
#if HAVE_ANDROID_OS
|
||||
/* Reads a key map from a parcel. */
|
||||
static sp<KeyCharacterMap> readFromParcel(Parcel* parcel);
|
||||
|
||||
/* Writes a key map to a parcel. */
|
||||
void writeToParcel(Parcel* parcel) const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual ~KeyCharacterMap();
|
||||
|
||||
private:
|
||||
struct Behavior {
|
||||
Behavior();
|
||||
Behavior(const Behavior& other);
|
||||
|
||||
/* The next behavior in the list, or NULL if none. */
|
||||
Behavior* next;
|
||||
@ -111,6 +155,7 @@ private:
|
||||
|
||||
struct Key {
|
||||
Key();
|
||||
Key(const Key& other);
|
||||
~Key();
|
||||
|
||||
/* The single character label printed on the key, or 0 if none. */
|
||||
@ -146,33 +191,46 @@ private:
|
||||
|
||||
KeyCharacterMap* mMap;
|
||||
Tokenizer* mTokenizer;
|
||||
Format mFormat;
|
||||
State mState;
|
||||
int32_t mKeyCode;
|
||||
|
||||
public:
|
||||
Parser(KeyCharacterMap* map, Tokenizer* tokenizer);
|
||||
Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format);
|
||||
~Parser();
|
||||
status_t parse();
|
||||
|
||||
private:
|
||||
status_t parseType();
|
||||
status_t parseMap();
|
||||
status_t parseMapKey();
|
||||
status_t parseKey();
|
||||
status_t parseKeyProperty();
|
||||
status_t finishKey(Key* key);
|
||||
status_t parseModifier(const String8& token, int32_t* outMetaState);
|
||||
status_t parseCharacterLiteral(char16_t* outCharacter);
|
||||
};
|
||||
|
||||
static sp<KeyCharacterMap> sEmpty;
|
||||
|
||||
KeyedVector<int32_t, Key*> mKeys;
|
||||
int mType;
|
||||
|
||||
KeyedVector<int32_t, int32_t> mKeysByScanCode;
|
||||
KeyedVector<int32_t, int32_t> mKeysByUsageCode;
|
||||
|
||||
KeyCharacterMap();
|
||||
KeyCharacterMap(const KeyCharacterMap& other);
|
||||
|
||||
bool getKey(int32_t keyCode, const Key** outKey) const;
|
||||
bool getKeyBehavior(int32_t keyCode, int32_t metaState,
|
||||
const Key** outKey, const Behavior** outBehavior) const;
|
||||
static bool matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState);
|
||||
|
||||
bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
|
||||
|
||||
static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap);
|
||||
|
||||
static void addKey(Vector<KeyEvent>& outEvents,
|
||||
int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time);
|
||||
static void addMetaKeys(Vector<KeyEvent>& outEvents,
|
||||
@ -196,4 +254,4 @@ private:
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UI_KEY_CHARACTER_MAP_H
|
||||
#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H
|
||||
|
@ -15,15 +15,15 @@
|
||||
*/
|
||||
|
||||
#define LOG_TAG "KeyLayoutMap"
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "utils_Log.h"
|
||||
#include "android_keycodes.h"
|
||||
#include "Keyboard.h"
|
||||
#include "KeyLayoutMap.h"
|
||||
#include <utils/Errors.h>
|
||||
#include "Tokenizer.h"
|
||||
#include "Timers.h"
|
||||
#include <utils/Timers.h>
|
||||
|
||||
// Enables debug output for the parser.
|
||||
#define DEBUG_PARSER 0
|
||||
@ -47,23 +47,23 @@ KeyLayoutMap::KeyLayoutMap() {
|
||||
KeyLayoutMap::~KeyLayoutMap() {
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::load(const String8& filename, KeyLayoutMap** outMap) {
|
||||
*outMap = NULL;
|
||||
status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) {
|
||||
outMap->clear();
|
||||
|
||||
Tokenizer* tokenizer;
|
||||
status_t status = Tokenizer::open(filename, &tokenizer);
|
||||
if (status) {
|
||||
ALOGE("Error %d opening key layout map file %s.", status, filename.string());
|
||||
} else {
|
||||
KeyLayoutMap* map = new KeyLayoutMap();
|
||||
if (!map) {
|
||||
sp<KeyLayoutMap> map = new KeyLayoutMap();
|
||||
if (!map.get()) {
|
||||
ALOGE("Error allocating key layout map.");
|
||||
status = NO_MEMORY;
|
||||
} else {
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
#endif
|
||||
Parser parser(map, tokenizer);
|
||||
Parser parser(map.get(), tokenizer);
|
||||
status = parser.parse();
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
|
||||
@ -71,9 +71,7 @@ status_t KeyLayoutMap::load(const String8& filename, KeyLayoutMap** outMap) {
|
||||
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
|
||||
elapsedTime / 1000000.0);
|
||||
#endif
|
||||
if (status) {
|
||||
delete map;
|
||||
} else {
|
||||
if (!status) {
|
||||
*outMap = map;
|
||||
}
|
||||
}
|
||||
@ -82,32 +80,49 @@ status_t KeyLayoutMap::load(const String8& filename, KeyLayoutMap** outMap) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const {
|
||||
ssize_t index = mKeys.indexOfKey(scanCode);
|
||||
if (index < 0) {
|
||||
status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode,
|
||||
int32_t* outKeyCode, uint32_t* outFlags) const {
|
||||
const Key* key = getKey(scanCode, usageCode);
|
||||
if (!key) {
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("mapKey: scanCode=%d ~ Failed.", scanCode);
|
||||
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode);
|
||||
#endif
|
||||
*keyCode = AKEYCODE_UNKNOWN;
|
||||
*flags = 0;
|
||||
*outKeyCode = AKEYCODE_UNKNOWN;
|
||||
*outFlags = 0;
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
const Key& k = mKeys.valueAt(index);
|
||||
*keyCode = k.keyCode;
|
||||
*flags = k.flags;
|
||||
*outKeyCode = key->keyCode;
|
||||
*outFlags = key->flags;
|
||||
|
||||
#if DEBUG_MAPPING
|
||||
ALOGD("mapKey: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags);
|
||||
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.",
|
||||
scanCode, usageCode, *outKeyCode, *outFlags);
|
||||
#endif
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const {
|
||||
if (usageCode) {
|
||||
ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
|
||||
if (index >= 0) {
|
||||
return &mKeysByUsageCode.valueAt(index);
|
||||
}
|
||||
}
|
||||
if (scanCode) {
|
||||
ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
|
||||
if (index >= 0) {
|
||||
return &mKeysByScanCode.valueAt(index);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
|
||||
const size_t N = mKeys.size();
|
||||
const size_t N = mKeysByScanCode.size();
|
||||
for (size_t i=0; i<N; i++) {
|
||||
if (mKeys.valueAt(i).keyCode == keyCode) {
|
||||
outScanCodes->add(mKeys.keyAt(i));
|
||||
if (mKeysByScanCode.valueAt(i).keyCode == keyCode) {
|
||||
outScanCodes->add(mKeysByScanCode.keyAt(i));
|
||||
}
|
||||
}
|
||||
return NO_ERROR;
|
||||
@ -170,8 +185,8 @@ status_t KeyLayoutMap::Parser::parse() {
|
||||
}
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
if (!mTokenizer->isEol()) {
|
||||
ALOGE("%s: Expected end of line, got '%s'.",
|
||||
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
|
||||
ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
|
||||
mTokenizer->getLocation().string(),
|
||||
mTokenizer->peekRemainderOfLine().string());
|
||||
return BAD_VALUE;
|
||||
@ -184,17 +199,26 @@ status_t KeyLayoutMap::Parser::parse() {
|
||||
}
|
||||
|
||||
status_t KeyLayoutMap::Parser::parseKey() {
|
||||
String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
String8 codeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
bool mapUsage = false;
|
||||
if (codeToken == "usage") {
|
||||
mapUsage = true;
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
codeToken = mTokenizer->nextToken(WHITESPACE);
|
||||
}
|
||||
|
||||
char* end;
|
||||
int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
|
||||
int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
|
||||
if (*end) {
|
||||
ALOGE("%s: Expected key scan code number, got '%s'.", mTokenizer->getLocation().string(),
|
||||
scanCodeToken.string());
|
||||
ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
|
||||
mapUsage ? "usage" : "scan code", codeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
if (mMap->mKeys.indexOfKey(scanCode) >= 0) {
|
||||
ALOGE("%s: Duplicate entry for key scan code '%s'.", mTokenizer->getLocation().string(),
|
||||
scanCodeToken.string());
|
||||
KeyedVector<int32_t, Key>& map =
|
||||
mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
|
||||
if (map.indexOfKey(code) >= 0) {
|
||||
ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
|
||||
mapUsage ? "usage" : "scan code", codeToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
@ -210,7 +234,7 @@ status_t KeyLayoutMap::Parser::parseKey() {
|
||||
uint32_t flags = 0;
|
||||
for (;;) {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
if (mTokenizer->isEol()) break;
|
||||
if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
|
||||
|
||||
String8 flagToken = mTokenizer->nextToken(WHITESPACE);
|
||||
uint32_t flag = getKeyFlagByLabel(flagToken.string());
|
||||
@ -228,12 +252,13 @@ status_t KeyLayoutMap::Parser::parseKey() {
|
||||
}
|
||||
|
||||
#if DEBUG_PARSER
|
||||
ALOGD("Parsed key: scanCode=%d, keyCode=%d, flags=0x%08x.", scanCode, keyCode, flags);
|
||||
ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.",
|
||||
mapUsage ? "usage" : "scan code", code, keyCode, flags);
|
||||
#endif
|
||||
Key key;
|
||||
key.keyCode = keyCode;
|
||||
key.flags = flags;
|
||||
mMap->mKeys.add(scanCode, key);
|
||||
map.add(code, key);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@ -307,7 +332,7 @@ status_t KeyLayoutMap::Parser::parseAxis() {
|
||||
|
||||
for (;;) {
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
if (mTokenizer->isEol()) {
|
||||
if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') {
|
||||
break;
|
||||
}
|
||||
String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
|
||||
|
@ -14,13 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _UI_KEY_LAYOUT_MAP_H
|
||||
#define _UI_KEY_LAYOUT_MAP_H
|
||||
#ifndef _ANDROIDFW_KEY_LAYOUT_MAP_H
|
||||
#define _ANDROIDFW_KEY_LAYOUT_MAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include "Tokenizer.h"
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
@ -56,29 +57,36 @@ struct AxisInfo {
|
||||
|
||||
/**
|
||||
* Describes a mapping from keyboard scan codes and joystick axes to Android key codes and axes.
|
||||
*
|
||||
* This object is immutable after it has been loaded.
|
||||
*/
|
||||
class KeyLayoutMap {
|
||||
class KeyLayoutMap : public RefBase {
|
||||
public:
|
||||
~KeyLayoutMap();
|
||||
static status_t load(const String8& filename, sp<KeyLayoutMap>* outMap);
|
||||
|
||||
static status_t load(const String8& filename, KeyLayoutMap** outMap);
|
||||
|
||||
status_t mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const;
|
||||
status_t mapKey(int32_t scanCode, int32_t usageCode,
|
||||
int32_t* outKeyCode, uint32_t* outFlags) const;
|
||||
status_t findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const;
|
||||
|
||||
status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const;
|
||||
|
||||
protected:
|
||||
virtual ~KeyLayoutMap();
|
||||
|
||||
private:
|
||||
struct Key {
|
||||
int32_t keyCode;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
KeyedVector<int32_t, Key> mKeys;
|
||||
KeyedVector<int32_t, Key> mKeysByScanCode;
|
||||
KeyedVector<int32_t, Key> mKeysByUsageCode;
|
||||
KeyedVector<int32_t, AxisInfo> mAxes;
|
||||
|
||||
KeyLayoutMap();
|
||||
|
||||
const Key* getKey(int32_t scanCode, int32_t usageCode) const;
|
||||
|
||||
class Parser {
|
||||
KeyLayoutMap* mMap;
|
||||
Tokenizer* mTokenizer;
|
||||
@ -96,4 +104,4 @@ private:
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UI_KEY_LAYOUT_MAP_H
|
||||
#endif // _ANDROIDFW_KEY_LAYOUT_MAP_H
|
||||
|
@ -15,16 +15,17 @@
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Keyboard"
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "utils_Log.h"
|
||||
#include "Keyboard.h"
|
||||
#include "KeycodeLabels.h"
|
||||
#include "KeyLayoutMap.h"
|
||||
#include "KeyCharacterMap.h"
|
||||
#include "InputDevice.h"
|
||||
#include <utils/Errors.h>
|
||||
#include <cutils/properties.h>
|
||||
|
||||
@ -32,13 +33,10 @@ namespace android {
|
||||
|
||||
// --- KeyMap ---
|
||||
|
||||
KeyMap::KeyMap() :
|
||||
keyLayoutMap(NULL), keyCharacterMap(NULL) {
|
||||
KeyMap::KeyMap() {
|
||||
}
|
||||
|
||||
KeyMap::~KeyMap() {
|
||||
delete keyLayoutMap;
|
||||
delete keyCharacterMap;
|
||||
}
|
||||
|
||||
status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
|
||||
@ -114,14 +112,12 @@ status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
KeyLayoutMap* map;
|
||||
status_t status = KeyLayoutMap::load(path, &map);
|
||||
status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
keyLayoutFile.setTo(path);
|
||||
keyLayoutMap = map;
|
||||
return OK;
|
||||
}
|
||||
|
||||
@ -133,14 +129,13 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
KeyCharacterMap* map;
|
||||
status_t status = KeyCharacterMap::load(path, &map);
|
||||
status_t status = KeyCharacterMap::load(path,
|
||||
KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
keyCharacterMapFile.setTo(path);
|
||||
keyCharacterMap = map;
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -14,13 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _UI_KEYBOARD_H
|
||||
#define _UI_KEYBOARD_H
|
||||
#ifndef _ANDROIDFW_KEYBOARD_H
|
||||
#define _ANDROIDFW_KEYBOARD_H
|
||||
|
||||
#include "Input.h"
|
||||
#include "InputDevice.h"
|
||||
#include <utils/Errors.h>
|
||||
#include "String8.h"
|
||||
#include "PropertyMap.h"
|
||||
#include <utils/String8.h>
|
||||
#include <utils/PropertyMap.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
@ -42,10 +43,10 @@ class KeyCharacterMap;
|
||||
class KeyMap {
|
||||
public:
|
||||
String8 keyLayoutFile;
|
||||
KeyLayoutMap* keyLayoutMap;
|
||||
sp<KeyLayoutMap> keyLayoutMap;
|
||||
|
||||
String8 keyCharacterMapFile;
|
||||
KeyCharacterMap* keyCharacterMap;
|
||||
sp<KeyCharacterMap> keyCharacterMap;
|
||||
|
||||
KeyMap();
|
||||
~KeyMap();
|
||||
@ -116,4 +117,4 @@ extern bool isMetaKey(int32_t keyCode);
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UI_KEYBOARD_H
|
||||
#endif // _ANDROIDFW_KEYBOARD_H
|
||||
|
@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _UI_KEYCODE_LABELS_H
|
||||
#define _UI_KEYCODE_LABELS_H
|
||||
#ifndef _ANDROIDFW_KEYCODE_LABELS_H
|
||||
#define _ANDROIDFW_KEYCODE_LABELS_H
|
||||
|
||||
#include "android_keycodes.h"
|
||||
|
||||
@ -235,6 +235,17 @@ static const KeycodeLabel KEYCODES[] = {
|
||||
{ "CALENDAR", 208 },
|
||||
{ "MUSIC", 209 },
|
||||
{ "CALCULATOR", 210 },
|
||||
{ "ZENKAKU_HANKAKU", 211 },
|
||||
{ "EISU", 212 },
|
||||
{ "MUHENKAN", 213 },
|
||||
{ "HENKAN", 214 },
|
||||
{ "KATAKANA_HIRAGANA", 215 },
|
||||
{ "YEN", 216 },
|
||||
{ "RO", 217 },
|
||||
{ "KANA", 218 },
|
||||
{ "ASSIST", 219 },
|
||||
{ "BRIGHTNESS_DOWN", 220 },
|
||||
{ "BRIGHTNESS_UP", 221 },
|
||||
|
||||
// NOTE: If you add a new keycode here you must also add it to several other files.
|
||||
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
|
||||
@ -307,4 +318,4 @@ static const KeycodeLabel AXES[] = {
|
||||
{ NULL, -1 }
|
||||
};
|
||||
|
||||
#endif // _UI_KEYCODE_LABELS_H
|
||||
#endif // _ANDROIDFW_KEYCODE_LABELS_H
|
||||
|
@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#include "PixelFormat.h"
|
||||
#include <pixelflinger/format.h>
|
||||
#include <hardware/hardware.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
static const int COMPONENT_YUV = 0xFF;
|
||||
|
||||
size_t PixelFormatInfo::getScanlineSize(unsigned int width) const
|
||||
{
|
||||
size_t size;
|
||||
if (components == COMPONENT_YUV) {
|
||||
// YCbCr formats are different.
|
||||
size = (width * bitsPerPixel)>>3;
|
||||
} else {
|
||||
size = width * bytesPerPixel;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
ssize_t bytesPerPixel(PixelFormat format)
|
||||
{
|
||||
PixelFormatInfo info;
|
||||
status_t err = getPixelFormatInfo(format, &info);
|
||||
return (err < 0) ? err : info.bytesPerPixel;
|
||||
}
|
||||
|
||||
ssize_t bitsPerPixel(PixelFormat format)
|
||||
{
|
||||
PixelFormatInfo info;
|
||||
status_t err = getPixelFormatInfo(format, &info);
|
||||
return (err < 0) ? err : info.bitsPerPixel;
|
||||
}
|
||||
|
||||
status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info)
|
||||
{
|
||||
if (format < 0)
|
||||
return BAD_VALUE;
|
||||
|
||||
if (info->version != sizeof(PixelFormatInfo))
|
||||
return INVALID_OPERATION;
|
||||
|
||||
// YUV format from the HAL are handled here
|
||||
switch (format) {
|
||||
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
|
||||
case HAL_PIXEL_FORMAT_YCbCr_422_I:
|
||||
info->bitsPerPixel = 16;
|
||||
goto done;
|
||||
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
||||
case HAL_PIXEL_FORMAT_YV12:
|
||||
info->bitsPerPixel = 12;
|
||||
done:
|
||||
info->format = format;
|
||||
info->components = COMPONENT_YUV;
|
||||
info->bytesPerPixel = 1;
|
||||
info->h_alpha = 0;
|
||||
info->l_alpha = 0;
|
||||
info->h_red = info->h_green = info->h_blue = 8;
|
||||
info->l_red = info->l_green = info->l_blue = 0;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
size_t numEntries;
|
||||
const GGLFormat *i = gglGetPixelFormatTable(&numEntries) + format;
|
||||
bool valid = uint32_t(format) < numEntries;
|
||||
if (!valid) {
|
||||
return BAD_INDEX;
|
||||
}
|
||||
|
||||
#define COMPONENT(name) \
|
||||
case GGL_##name: info->components = PixelFormatInfo::name; break;
|
||||
|
||||
switch (i->components) {
|
||||
COMPONENT(ALPHA)
|
||||
COMPONENT(RGB)
|
||||
COMPONENT(RGBA)
|
||||
COMPONENT(LUMINANCE)
|
||||
COMPONENT(LUMINANCE_ALPHA)
|
||||
default:
|
||||
return BAD_INDEX;
|
||||
}
|
||||
|
||||
#undef COMPONENT
|
||||
|
||||
info->format = format;
|
||||
info->bytesPerPixel = i->size;
|
||||
info->bitsPerPixel = i->bitsPerPixel;
|
||||
info->h_alpha = i->ah;
|
||||
info->l_alpha = i->al;
|
||||
info->h_red = i->rh;
|
||||
info->l_red = i->rl;
|
||||
info->h_green = i->gh;
|
||||
info->l_green = i->gl;
|
||||
info->h_blue = i->bh;
|
||||
info->l_blue = i->bl;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
#endif // HAVE_ANDROID_OS
|
||||
|
||||
}; // namespace android
|
||||
|
@ -1,139 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
//
|
||||
|
||||
// Pixel formats used across the system.
|
||||
// These formats might not all be supported by all renderers, for instance
|
||||
// skia or SurfaceFlinger are not required to support all of these formats
|
||||
// (either as source or destination)
|
||||
|
||||
// XXX: we should consolidate these formats and skia's
|
||||
|
||||
#ifndef UI_PIXELFORMAT_H
|
||||
#define UI_PIXELFORMAT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <pixelflinger/format.h>
|
||||
#include <hardware/hardware.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
enum {
|
||||
//
|
||||
// these constants need to match those
|
||||
// in graphics/PixelFormat.java & pixelflinger/format.h
|
||||
//
|
||||
PIXEL_FORMAT_UNKNOWN = 0,
|
||||
PIXEL_FORMAT_NONE = 0,
|
||||
|
||||
// logical pixel formats used by the SurfaceFlinger -----------------------
|
||||
PIXEL_FORMAT_CUSTOM = -4,
|
||||
// Custom pixel-format described by a PixelFormatInfo structure
|
||||
|
||||
PIXEL_FORMAT_TRANSLUCENT = -3,
|
||||
// System chooses a format that supports translucency (many alpha bits)
|
||||
|
||||
PIXEL_FORMAT_TRANSPARENT = -2,
|
||||
// System chooses a format that supports transparency
|
||||
// (at least 1 alpha bit)
|
||||
|
||||
PIXEL_FORMAT_OPAQUE = -1,
|
||||
// System chooses an opaque format (no alpha bits required)
|
||||
|
||||
// real pixel formats supported for rendering -----------------------------
|
||||
|
||||
PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA
|
||||
PIXEL_FORMAT_RGBX_8888 = HAL_PIXEL_FORMAT_RGBX_8888, // 4x8-bit RGB0
|
||||
PIXEL_FORMAT_RGB_888 = HAL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB
|
||||
PIXEL_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565, // 16-bit RGB
|
||||
PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA
|
||||
PIXEL_FORMAT_RGBA_5551 = HAL_PIXEL_FORMAT_RGBA_5551, // 16-bit ARGB
|
||||
PIXEL_FORMAT_RGBA_4444 = HAL_PIXEL_FORMAT_RGBA_4444, // 16-bit ARGB
|
||||
PIXEL_FORMAT_A_8 = GGL_PIXEL_FORMAT_A_8, // 8-bit A
|
||||
PIXEL_FORMAT_L_8 = GGL_PIXEL_FORMAT_L_8, // 8-bit L (R=G=B=L)
|
||||
PIXEL_FORMAT_LA_88 = GGL_PIXEL_FORMAT_LA_88, // 16-bit LA
|
||||
PIXEL_FORMAT_RGB_332 = GGL_PIXEL_FORMAT_RGB_332, // 8-bit RGB
|
||||
|
||||
// New formats can be added if they're also defined in
|
||||
// pixelflinger/format.h
|
||||
};
|
||||
|
||||
typedef int32_t PixelFormat;
|
||||
|
||||
struct PixelFormatInfo
|
||||
{
|
||||
enum {
|
||||
INDEX_ALPHA = 0,
|
||||
INDEX_RED = 1,
|
||||
INDEX_GREEN = 2,
|
||||
INDEX_BLUE = 3
|
||||
};
|
||||
|
||||
enum { // components
|
||||
ALPHA = 1,
|
||||
RGB = 2,
|
||||
RGBA = 3,
|
||||
LUMINANCE = 4,
|
||||
LUMINANCE_ALPHA = 5,
|
||||
OTHER = 0xFF
|
||||
};
|
||||
|
||||
struct szinfo {
|
||||
uint8_t h;
|
||||
uint8_t l;
|
||||
};
|
||||
|
||||
inline PixelFormatInfo() : version(sizeof(PixelFormatInfo)) { }
|
||||
size_t getScanlineSize(unsigned int width) const;
|
||||
size_t getSize(size_t ci) const {
|
||||
return (ci <= 3) ? (cinfo[ci].h - cinfo[ci].l) : 0;
|
||||
}
|
||||
size_t version;
|
||||
PixelFormat format;
|
||||
size_t bytesPerPixel;
|
||||
size_t bitsPerPixel;
|
||||
union {
|
||||
szinfo cinfo[4];
|
||||
struct {
|
||||
uint8_t h_alpha;
|
||||
uint8_t l_alpha;
|
||||
uint8_t h_red;
|
||||
uint8_t l_red;
|
||||
uint8_t h_green;
|
||||
uint8_t l_green;
|
||||
uint8_t h_blue;
|
||||
uint8_t l_blue;
|
||||
};
|
||||
};
|
||||
uint8_t components;
|
||||
uint8_t reserved0[3];
|
||||
uint32_t reserved1;
|
||||
};
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
// Consider caching the results of these functions are they're not
|
||||
// guaranteed to be fast.
|
||||
ssize_t bytesPerPixel(PixelFormat format);
|
||||
ssize_t bitsPerPixel(PixelFormat format);
|
||||
status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info);
|
||||
#endif
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif // UI_PIXELFORMAT_H
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include "PointerController.h"
|
||||
|
||||
#include <cutils/log.h>
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <SkBitmap.h>
|
||||
#include <SkCanvas.h>
|
||||
@ -54,9 +54,7 @@ static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
|
||||
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
|
||||
const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
|
||||
mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
mHandler = new WeakMessageHandler(this);
|
||||
#endif
|
||||
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
@ -84,9 +82,7 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>&
|
||||
}
|
||||
|
||||
PointerController::~PointerController() {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
mLooper->removeMessages(mHandler);
|
||||
#endif
|
||||
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
@ -311,9 +307,17 @@ void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::setDisplaySize(int32_t width, int32_t height) {
|
||||
void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
// Adjust to use the display's unrotated coordinate frame.
|
||||
if (orientation == DISPLAY_ORIENTATION_90
|
||||
|| orientation == DISPLAY_ORIENTATION_270) {
|
||||
int32_t temp = height;
|
||||
height = width;
|
||||
width = temp;
|
||||
}
|
||||
|
||||
if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
|
||||
mLocked.displayWidth = width;
|
||||
mLocked.displayHeight = height;
|
||||
@ -328,12 +332,7 @@ void PointerController::setDisplaySize(int32_t width, int32_t height) {
|
||||
}
|
||||
|
||||
fadeOutAndReleaseAllSpotsLocked();
|
||||
updatePointerLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::setDisplayOrientation(int32_t orientation) {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
if (mLocked.displayOrientation != orientation) {
|
||||
// Apply offsets to convert from the pixel top-left corner position to the pixel center.
|
||||
@ -384,9 +383,9 @@ void PointerController::setDisplayOrientation(int32_t orientation) {
|
||||
mLocked.pointerX = x - 0.5f;
|
||||
mLocked.pointerY = y - 0.5f;
|
||||
mLocked.displayOrientation = orientation;
|
||||
|
||||
updatePointerLocked();
|
||||
}
|
||||
|
||||
updatePointerLocked();
|
||||
}
|
||||
|
||||
void PointerController::setPointerIcon(const SpriteIcon& icon) {
|
||||
@ -398,7 +397,6 @@ void PointerController::setPointerIcon(const SpriteIcon& icon) {
|
||||
updatePointerLocked();
|
||||
}
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
void PointerController::handleMessage(const Message& message) {
|
||||
switch (message.what) {
|
||||
case MSG_ANIMATE:
|
||||
@ -409,7 +407,6 @@ void PointerController::handleMessage(const Message& message) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void PointerController::doAnimate() {
|
||||
AutoMutex _l(mLock);
|
||||
@ -467,28 +464,20 @@ void PointerController::startAnimationLocked() {
|
||||
if (!mLocked.animationPending) {
|
||||
mLocked.animationPending = true;
|
||||
mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
mLooper->sendMessageDelayed(ANIMATION_FRAME_INTERVAL, mHandler, Message(MSG_ANIMATE));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void PointerController::resetInactivityTimeoutLocked() {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
|
||||
#endif
|
||||
|
||||
nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT
|
||||
? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PointerController::removeInactivityTimeoutLocked() {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PointerController::updatePointerLocked() {
|
||||
|
@ -19,11 +19,12 @@
|
||||
|
||||
#include "SpriteController.h"
|
||||
|
||||
#include "DisplayInfo.h"
|
||||
#include <ui/DisplayInfo.h>
|
||||
#include "Input.h"
|
||||
#include <utils/BitSet.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/Looper.h>
|
||||
#include "String8.h"
|
||||
#include <utils/String8.h>
|
||||
|
||||
#include <SkBitmap.h>
|
||||
|
||||
@ -141,11 +142,7 @@ public:
|
||||
*
|
||||
* Handles pointer acceleration and animation.
|
||||
*/
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
class PointerController : public PointerControllerInterface, public MessageHandler {
|
||||
#else
|
||||
class PointerController : public PointerControllerInterface {
|
||||
#endif
|
||||
protected:
|
||||
virtual ~PointerController();
|
||||
|
||||
@ -173,8 +170,7 @@ public:
|
||||
const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
|
||||
virtual void clearSpots();
|
||||
|
||||
void setDisplaySize(int32_t width, int32_t height);
|
||||
void setDisplayOrientation(int32_t orientation);
|
||||
void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
|
||||
void setPointerIcon(const SpriteIcon& icon);
|
||||
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
|
||||
|
||||
@ -211,9 +207,7 @@ private:
|
||||
sp<PointerControllerPolicyInterface> mPolicy;
|
||||
sp<Looper> mLooper;
|
||||
sp<SpriteController> mSpriteController;
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
sp<WeakMessageHandler> mHandler;
|
||||
#endif
|
||||
|
||||
PointerResources mResources;
|
||||
|
||||
@ -247,9 +241,7 @@ private:
|
||||
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
|
||||
void setPositionLocked(float x, float y);
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
void handleMessage(const Message& message);
|
||||
#endif
|
||||
void doAnimate();
|
||||
void doInactivityTimeout();
|
||||
|
||||
|
@ -14,20 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _UI_POWER_MANAGER_H
|
||||
#define _UI_POWER_MANAGER_H
|
||||
#ifndef _ANDROIDFW_POWER_MANAGER_H
|
||||
#define _ANDROIDFW_POWER_MANAGER_H
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
enum {
|
||||
POWER_MANAGER_OTHER_EVENT = 0,
|
||||
POWER_MANAGER_BUTTON_EVENT = 1,
|
||||
POWER_MANAGER_TOUCH_EVENT = 2,
|
||||
USER_ACTIVITY_EVENT_OTHER = 0,
|
||||
USER_ACTIVITY_EVENT_BUTTON = 1,
|
||||
USER_ACTIVITY_EVENT_TOUCH = 2,
|
||||
|
||||
POWER_MANAGER_LAST_EVENT = POWER_MANAGER_TOUCH_EVENT, // Last valid event code.
|
||||
USER_ACTIVITY_EVENT_LAST = USER_ACTIVITY_EVENT_TOUCH, // Last valid event code.
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UI_POWER_MANAGER_H
|
||||
#endif // _ANDROIDFW_POWER_MANAGER_H
|
||||
|
@ -1,218 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 "PropertyMap"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utils_Log.h"
|
||||
#include "PropertyMap.h"
|
||||
|
||||
// Enables debug output for the parser.
|
||||
#define DEBUG_PARSER 0
|
||||
|
||||
// Enables debug output for parser performance.
|
||||
#define DEBUG_PARSER_PERFORMANCE 0
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
static const char* WHITESPACE = " \t\r";
|
||||
static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r=";
|
||||
|
||||
|
||||
// --- PropertyMap ---
|
||||
|
||||
PropertyMap::PropertyMap() {
|
||||
}
|
||||
|
||||
PropertyMap::~PropertyMap() {
|
||||
}
|
||||
|
||||
void PropertyMap::clear() {
|
||||
mProperties.clear();
|
||||
}
|
||||
|
||||
void PropertyMap::addProperty(const String8& key, const String8& value) {
|
||||
mProperties.add(key, value);
|
||||
}
|
||||
|
||||
bool PropertyMap::hasProperty(const String8& key) const {
|
||||
return mProperties.indexOfKey(key) >= 0;
|
||||
}
|
||||
|
||||
bool PropertyMap::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 PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
|
||||
int32_t intValue;
|
||||
if (!tryGetProperty(key, intValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outValue = intValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyMap::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') {
|
||||
ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.",
|
||||
key.string(), stringValue.string());
|
||||
return false;
|
||||
}
|
||||
outValue = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertyMap::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') {
|
||||
ALOGW("Property key '%s' has invalid value '%s'. Expected a float.",
|
||||
key.string(), stringValue.string());
|
||||
return false;
|
||||
}
|
||||
outValue = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PropertyMap::addAll(const PropertyMap* map) {
|
||||
for (size_t i = 0; i < map->mProperties.size(); i++) {
|
||||
mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
|
||||
*outMap = NULL;
|
||||
|
||||
Tokenizer* tokenizer;
|
||||
status_t status = Tokenizer::open(filename, &tokenizer);
|
||||
if (status) {
|
||||
ALOGE("Error %d opening property file %s.", status, filename.string());
|
||||
} else {
|
||||
PropertyMap* map = new PropertyMap();
|
||||
if (!map) {
|
||||
ALOGE("Error allocating property map.");
|
||||
status = NO_MEMORY;
|
||||
} else {
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
#endif
|
||||
Parser parser(map, tokenizer);
|
||||
status = parser.parse();
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
|
||||
ALOGD("Parsed property file '%s' %d lines in %0.3fms.",
|
||||
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
|
||||
elapsedTime / 1000000.0);
|
||||
#endif
|
||||
if (status) {
|
||||
delete map;
|
||||
} else {
|
||||
*outMap = map;
|
||||
}
|
||||
}
|
||||
delete tokenizer;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
// --- PropertyMap::Parser ---
|
||||
|
||||
PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) :
|
||||
mMap(map), mTokenizer(tokenizer) {
|
||||
}
|
||||
|
||||
PropertyMap::Parser::~Parser() {
|
||||
}
|
||||
|
||||
status_t PropertyMap::Parser::parse() {
|
||||
while (!mTokenizer->isEof()) {
|
||||
#if DEBUG_PARSER
|
||||
ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
|
||||
mTokenizer->peekRemainderOfLine().string());
|
||||
#endif
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
|
||||
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
|
||||
String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
|
||||
if (keyToken.isEmpty()) {
|
||||
ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
|
||||
if (mTokenizer->nextChar() != '=') {
|
||||
ALOGE("%s: Expected '=' between property key and value.",
|
||||
mTokenizer->getLocation().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
|
||||
String8 valueToken = mTokenizer->nextToken(WHITESPACE);
|
||||
if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
|
||||
ALOGE("%s: Found reserved character '\\' or '\"' in property value.",
|
||||
mTokenizer->getLocation().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mTokenizer->skipDelimiters(WHITESPACE);
|
||||
if (!mTokenizer->isEol()) {
|
||||
ALOGE("%s: Expected end of line, got '%s'.",
|
||||
mTokenizer->getLocation().string(),
|
||||
mTokenizer->peekRemainderOfLine().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if (mMap->hasProperty(keyToken)) {
|
||||
ALOGE("%s: Duplicate property value for key '%s'.",
|
||||
mTokenizer->getLocation().string(), keyToken.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mMap->addProperty(keyToken, valueToken);
|
||||
}
|
||||
|
||||
mTokenizer->nextLine();
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
} // namespace android
|
@ -1,106 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _UTILS_PROPERTY_MAP_H
|
||||
#define _UTILS_PROPERTY_MAP_H
|
||||
|
||||
#include <utils/KeyedVector.h>
|
||||
#include "String8.h"
|
||||
#include <utils/Errors.h>
|
||||
#include "Tokenizer.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Provides a mechanism for passing around string-based property key / value pairs
|
||||
* and loading them from property files.
|
||||
*
|
||||
* The property files have the following simple structure:
|
||||
*
|
||||
* # Comment
|
||||
* key = value
|
||||
*
|
||||
* Keys and values are any sequence of printable ASCII characters.
|
||||
* The '=' separates the key from the value.
|
||||
* The key and value may not contain whitespace.
|
||||
*
|
||||
* The '\' character is reserved for escape sequences and is not currently supported.
|
||||
* The '"" character is reserved for quoting and is not currently supported.
|
||||
* Files that contain the '\' or '"' character will fail to parse.
|
||||
*
|
||||
* The file must not contain duplicate keys.
|
||||
*
|
||||
* TODO Support escape sequences and quoted values when needed.
|
||||
*/
|
||||
class PropertyMap {
|
||||
public:
|
||||
/* Creates an empty property map. */
|
||||
PropertyMap();
|
||||
~PropertyMap();
|
||||
|
||||
/* Clears the property map. */
|
||||
void clear();
|
||||
|
||||
/* Adds a property.
|
||||
* Replaces the property with the same key if it is already present.
|
||||
*/
|
||||
void addProperty(const String8& key, const String8& value);
|
||||
|
||||
/* Returns true if the property map contains the specified key. */
|
||||
bool hasProperty(const String8& key) const;
|
||||
|
||||
/* Gets the value of a property and parses it.
|
||||
* Returns true and sets outValue if the key was found and its value was parsed successfully.
|
||||
* Otherwise returns false and does not modify outValue. (Also logs a warning.)
|
||||
*/
|
||||
bool tryGetProperty(const String8& key, String8& outValue) const;
|
||||
bool tryGetProperty(const String8& key, bool& outValue) const;
|
||||
bool tryGetProperty(const String8& key, int32_t& outValue) const;
|
||||
bool tryGetProperty(const String8& key, float& outValue) const;
|
||||
|
||||
/* Adds all values from the specified property map. */
|
||||
void addAll(const PropertyMap* map);
|
||||
|
||||
/* Gets the underlying property map. */
|
||||
inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; }
|
||||
|
||||
/* Loads a property map from a file. */
|
||||
static status_t load(const String8& filename, PropertyMap** outMap);
|
||||
|
||||
private:
|
||||
class Parser {
|
||||
PropertyMap* mMap;
|
||||
Tokenizer* mTokenizer;
|
||||
|
||||
public:
|
||||
Parser(PropertyMap* map, Tokenizer* tokenizer);
|
||||
~Parser();
|
||||
status_t parse();
|
||||
|
||||
private:
|
||||
status_t parseType();
|
||||
status_t parseKey();
|
||||
status_t parseKeyProperty();
|
||||
status_t parseModifier(const String8& token, int32_t* outMetaState);
|
||||
status_t parseCharacterLiteral(char16_t* outCharacter);
|
||||
};
|
||||
|
||||
KeyedVector<String8, String8> mProperties;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UTILS_PROPERTY_MAP_H
|
@ -21,13 +21,17 @@
|
||||
#include "SpriteController.h"
|
||||
|
||||
#include "cutils_log.h"
|
||||
#include "String8.h"
|
||||
#include <utils/String8.h>
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
#include <gui/Surface.h>
|
||||
#endif
|
||||
|
||||
#include <SkBitmap.h>
|
||||
#include <SkCanvas.h>
|
||||
#include <SkColor.h>
|
||||
#include <SkPaint.h>
|
||||
#include <SkXfermode.h>
|
||||
#include <android/native_window.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
@ -214,33 +218,32 @@ void SpriteController::doUpdateSprites() {
|
||||
if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn
|
||||
&& update.state.wantSurfaceVisible()) {
|
||||
sp<Surface> surface = update.state.surfaceControl->getSurface();
|
||||
Surface::SurfaceInfo surfaceInfo;
|
||||
status_t status = surface->lock(&surfaceInfo);
|
||||
ANativeWindow_Buffer outBuffer;
|
||||
status_t status = surface->lock(&outBuffer, NULL);
|
||||
if (status) {
|
||||
ALOGE("Error %d locking sprite surface before drawing.", status);
|
||||
} else {
|
||||
SkBitmap surfaceBitmap;
|
||||
ssize_t bpr = surfaceInfo.s * bytesPerPixel(surfaceInfo.format);
|
||||
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
|
||||
surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config,
|
||||
surfaceInfo.w, surfaceInfo.h, bpr);
|
||||
surfaceBitmap.setPixels(surfaceInfo.bits);
|
||||
outBuffer.width, outBuffer.height, bpr);
|
||||
surfaceBitmap.setPixels(outBuffer.bits);
|
||||
|
||||
SkCanvas surfaceCanvas;
|
||||
surfaceCanvas.setBitmapDevice(surfaceBitmap);
|
||||
SkCanvas surfaceCanvas(surfaceBitmap);
|
||||
|
||||
SkPaint paint;
|
||||
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
|
||||
surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
|
||||
|
||||
if (surfaceInfo.w > uint32_t(update.state.icon.bitmap.width())) {
|
||||
if (outBuffer.width > uint32_t(update.state.icon.bitmap.width())) {
|
||||
paint.setColor(0); // transparent fill color
|
||||
surfaceCanvas.drawRectCoords(update.state.icon.bitmap.width(), 0,
|
||||
surfaceInfo.w, update.state.icon.bitmap.height(), paint);
|
||||
outBuffer.width, update.state.icon.bitmap.height(), paint);
|
||||
}
|
||||
if (surfaceInfo.h > uint32_t(update.state.icon.bitmap.height())) {
|
||||
if (outBuffer.height > uint32_t(update.state.icon.bitmap.height())) {
|
||||
paint.setColor(0); // transparent fill color
|
||||
surfaceCanvas.drawRectCoords(0, update.state.icon.bitmap.height(),
|
||||
surfaceInfo.w, surfaceInfo.h, paint);
|
||||
outBuffer.width, outBuffer.height, paint);
|
||||
}
|
||||
|
||||
status = surface->unlockAndPost();
|
||||
@ -317,7 +320,7 @@ void SpriteController::doUpdateSprites() {
|
||||
}
|
||||
|
||||
if (becomingVisible) {
|
||||
status = update.state.surfaceControl->show(surfaceLayer);
|
||||
status = update.state.surfaceControl->show();
|
||||
if (status) {
|
||||
ALOGE("Error %d showing sprite surface.", status);
|
||||
} else {
|
||||
@ -398,9 +401,9 @@ sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height
|
||||
ensureSurfaceComposerClient();
|
||||
|
||||
sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface(
|
||||
String8("Sprite"), 0, width, height, PIXEL_FORMAT_RGBA_8888);
|
||||
if (surfaceControl == NULL || !surfaceControl->isValid()
|
||||
|| !surfaceControl->getSurface()->isValid()) {
|
||||
String8("Sprite"), width, height, PIXEL_FORMAT_RGBA_8888,
|
||||
ISurfaceComposerClient::eHidden);
|
||||
if (surfaceControl == NULL || !surfaceControl->isValid()) {
|
||||
ALOGE("Error creating sprite surface.");
|
||||
return NULL;
|
||||
}
|
||||
|
@ -21,12 +21,10 @@
|
||||
#include <utils/Looper.h>
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
#include <surfaceflinger/Surface.h>
|
||||
#include <surfaceflinger/SurfaceComposerClient.h>
|
||||
#include <surfaceflinger/ISurfaceComposer.h>
|
||||
#include <gui/SurfaceComposerClient.h>
|
||||
#endif
|
||||
|
||||
#include <SkBitmap.h>
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
|
||||
@ -151,11 +149,7 @@ public:
|
||||
*
|
||||
* Clients are responsible for animating sprites by periodically updating their properties.
|
||||
*/
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
class SpriteController : public MessageHandler {
|
||||
#else
|
||||
class SpriteController : public virtual RefBase {
|
||||
#endif
|
||||
protected:
|
||||
virtual ~SpriteController();
|
||||
|
||||
|
@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.
|
||||
*/
|
||||
|
||||
// All static variables go here, to control initialization and
|
||||
// destruction order in the library.
|
||||
|
||||
#include "Static.h"
|
||||
|
||||
#include <utils/BufferedTextOutput.h>
|
||||
#include "utils_Log.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
class LibUtilsFirstStatics
|
||||
{
|
||||
public:
|
||||
LibUtilsFirstStatics()
|
||||
{
|
||||
initialize_string8();
|
||||
initialize_string16();
|
||||
}
|
||||
|
||||
~LibUtilsFirstStatics()
|
||||
{
|
||||
terminate_string16();
|
||||
terminate_string8();
|
||||
}
|
||||
};
|
||||
|
||||
static LibUtilsFirstStatics gFirstStatics;
|
||||
int gDarwinCantLoadAllObjects = 1;
|
||||
|
||||
// ------------ Text output streams
|
||||
#if 0
|
||||
Vector<int32_t> gTextBuffers;
|
||||
|
||||
class LogTextOutput : public BufferedTextOutput
|
||||
{
|
||||
public:
|
||||
LogTextOutput() : BufferedTextOutput(MULTITHREADED) { }
|
||||
virtual ~LogTextOutput() { };
|
||||
|
||||
protected:
|
||||
virtual status_t writeLines(const struct iovec& vec, size_t N)
|
||||
{
|
||||
//android_writevLog(&vec, N); <-- this is now a no-op
|
||||
if (N != 1) ALOGI("WARNING: writeLines N=%d\n", N);
|
||||
ALOGI("%.*s", vec.iov_len, (const char*) vec.iov_base);
|
||||
return NO_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
class FdTextOutput : public BufferedTextOutput
|
||||
{
|
||||
public:
|
||||
FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { }
|
||||
virtual ~FdTextOutput() { };
|
||||
|
||||
protected:
|
||||
virtual status_t writeLines(const struct iovec& vec, size_t N)
|
||||
{
|
||||
writev(mFD, &vec, N);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
private:
|
||||
int mFD;
|
||||
};
|
||||
|
||||
static LogTextOutput gLogTextOutput;
|
||||
static FdTextOutput gStdoutTextOutput(STDOUT_FILENO);
|
||||
static FdTextOutput gStderrTextOutput(STDERR_FILENO);
|
||||
|
||||
TextOutput& alog(gLogTextOutput);
|
||||
TextOutput& aout(gStdoutTextOutput);
|
||||
TextOutput& aerr(gStderrTextOutput);
|
||||
#endif
|
||||
} // namespace android
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.
|
||||
*/
|
||||
|
||||
// All static variables go here, to control initialization and
|
||||
// destruction order in the library.
|
||||
|
||||
#include <utils/threads.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
|
||||
namespace android {
|
||||
// For TextStream.cpp
|
||||
//extern Vector<int32_t> gTextBuffers;
|
||||
|
||||
// For String8.cpp
|
||||
extern void initialize_string8();
|
||||
extern void terminate_string8();
|
||||
|
||||
// For String16.cpp
|
||||
extern void initialize_string16();
|
||||
extern void terminate_string16();
|
||||
|
||||
} // namespace android
|
@ -1,418 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
#include "String16.h"
|
||||
|
||||
#include <utils/Debug.h>
|
||||
#include "utils_Log.h"
|
||||
#include "Unicode.h"
|
||||
#include "String8.h"
|
||||
#include <utils/TextOutput.h>
|
||||
#include <utils/threads.h>
|
||||
|
||||
#include "Static.h"
|
||||
|
||||
#include <memory.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
static SharedBuffer* gEmptyStringBuf = NULL;
|
||||
static char16_t* gEmptyString = NULL;
|
||||
|
||||
static inline char16_t* getEmptyString()
|
||||
{
|
||||
gEmptyStringBuf->acquire();
|
||||
return gEmptyString;
|
||||
}
|
||||
|
||||
void initialize_string16()
|
||||
{
|
||||
SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
*str = 0;
|
||||
gEmptyStringBuf = buf;
|
||||
gEmptyString = str;
|
||||
}
|
||||
|
||||
void terminate_string16()
|
||||
{
|
||||
SharedBuffer::bufferFromData(gEmptyString)->release();
|
||||
gEmptyStringBuf = NULL;
|
||||
gEmptyString = NULL;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static char16_t* allocFromUTF8(const char* u8str, size_t u8len)
|
||||
{
|
||||
if (u8len == 0) return getEmptyString();
|
||||
|
||||
const uint8_t* u8cur = (const uint8_t*) u8str;
|
||||
|
||||
const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len);
|
||||
if (u16len < 0) {
|
||||
return getEmptyString();
|
||||
}
|
||||
|
||||
const uint8_t* const u8end = u8cur + u8len;
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1));
|
||||
if (buf) {
|
||||
u8cur = (const uint8_t*) u8str;
|
||||
char16_t* u16str = (char16_t*)buf->data();
|
||||
|
||||
utf8_to_utf16(u8cur, u8len, u16str);
|
||||
|
||||
//printf("Created UTF-16 string from UTF-8 \"%s\":", in);
|
||||
//printHexData(1, str, buf->size(), 16, 1);
|
||||
//printf("\n");
|
||||
|
||||
return u16str;
|
||||
}
|
||||
|
||||
return getEmptyString();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
String16::String16()
|
||||
: mString(getEmptyString())
|
||||
{
|
||||
}
|
||||
|
||||
String16::String16(const String16& o)
|
||||
: mString(o.mString)
|
||||
{
|
||||
SharedBuffer::bufferFromData(mString)->acquire();
|
||||
}
|
||||
|
||||
String16::String16(const String16& o, size_t len, size_t begin)
|
||||
: mString(getEmptyString())
|
||||
{
|
||||
setTo(o, len, begin);
|
||||
}
|
||||
|
||||
String16::String16(const char16_t* o)
|
||||
{
|
||||
size_t len = strlen16(o);
|
||||
SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
|
||||
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
strcpy16(str, o);
|
||||
mString = str;
|
||||
return;
|
||||
}
|
||||
|
||||
mString = getEmptyString();
|
||||
}
|
||||
|
||||
String16::String16(const char16_t* o, size_t len)
|
||||
{
|
||||
SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
|
||||
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
memcpy(str, o, len*sizeof(char16_t));
|
||||
str[len] = 0;
|
||||
mString = str;
|
||||
return;
|
||||
}
|
||||
|
||||
mString = getEmptyString();
|
||||
}
|
||||
|
||||
String16::String16(const String8& o)
|
||||
: mString(allocFromUTF8(o.string(), o.size()))
|
||||
{
|
||||
}
|
||||
|
||||
String16::String16(const char* o)
|
||||
: mString(allocFromUTF8(o, strlen(o)))
|
||||
{
|
||||
}
|
||||
|
||||
String16::String16(const char* o, size_t len)
|
||||
: mString(allocFromUTF8(o, len))
|
||||
{
|
||||
}
|
||||
|
||||
String16::~String16()
|
||||
{
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
}
|
||||
|
||||
void String16::setTo(const String16& other)
|
||||
{
|
||||
SharedBuffer::bufferFromData(other.mString)->acquire();
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
mString = other.mString;
|
||||
}
|
||||
|
||||
status_t String16::setTo(const String16& other, size_t len, size_t begin)
|
||||
{
|
||||
const size_t N = other.size();
|
||||
if (begin >= N) {
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
mString = getEmptyString();
|
||||
return NO_ERROR;
|
||||
}
|
||||
if ((begin+len) > N) len = N-begin;
|
||||
if (begin == 0 && len == N) {
|
||||
setTo(other);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
if (&other == this) {
|
||||
LOG_ALWAYS_FATAL("Not implemented");
|
||||
}
|
||||
|
||||
return setTo(other.string()+begin, len);
|
||||
}
|
||||
|
||||
status_t String16::setTo(const char16_t* other)
|
||||
{
|
||||
return setTo(other, strlen16(other));
|
||||
}
|
||||
|
||||
status_t String16::setTo(const char16_t* other, size_t len)
|
||||
{
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize((len+1)*sizeof(char16_t));
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
memmove(str, other, len*sizeof(char16_t));
|
||||
str[len] = 0;
|
||||
mString = str;
|
||||
return NO_ERROR;
|
||||
}
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
status_t String16::append(const String16& other)
|
||||
{
|
||||
const size_t myLen = size();
|
||||
const size_t otherLen = other.size();
|
||||
if (myLen == 0) {
|
||||
setTo(other);
|
||||
return NO_ERROR;
|
||||
} else if (otherLen == 0) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize((myLen+otherLen+1)*sizeof(char16_t));
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
|
||||
mString = str;
|
||||
return NO_ERROR;
|
||||
}
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
status_t String16::append(const char16_t* chrs, size_t otherLen)
|
||||
{
|
||||
const size_t myLen = size();
|
||||
if (myLen == 0) {
|
||||
setTo(chrs, otherLen);
|
||||
return NO_ERROR;
|
||||
} else if (otherLen == 0) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize((myLen+otherLen+1)*sizeof(char16_t));
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
|
||||
str[myLen+otherLen] = 0;
|
||||
mString = str;
|
||||
return NO_ERROR;
|
||||
}
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
status_t String16::insert(size_t pos, const char16_t* chrs)
|
||||
{
|
||||
return insert(pos, chrs, strlen16(chrs));
|
||||
}
|
||||
|
||||
status_t String16::insert(size_t pos, const char16_t* chrs, size_t len)
|
||||
{
|
||||
const size_t myLen = size();
|
||||
if (myLen == 0) {
|
||||
return setTo(chrs, len);
|
||||
return NO_ERROR;
|
||||
} else if (len == 0) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
if (pos > myLen) pos = myLen;
|
||||
|
||||
#if 0
|
||||
printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n",
|
||||
String8(*this).string(), pos,
|
||||
len, myLen, String8(chrs, len).string());
|
||||
#endif
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize((myLen+len+1)*sizeof(char16_t));
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
if (pos < myLen) {
|
||||
memmove(str+pos+len, str+pos, (myLen-pos)*sizeof(char16_t));
|
||||
}
|
||||
memcpy(str+pos, chrs, len*sizeof(char16_t));
|
||||
str[myLen+len] = 0;
|
||||
mString = str;
|
||||
#if 0
|
||||
printf("Result (%d chrs): %s\n", size(), String8(*this).string());
|
||||
#endif
|
||||
return NO_ERROR;
|
||||
}
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
ssize_t String16::findFirst(char16_t c) const
|
||||
{
|
||||
const char16_t* str = string();
|
||||
const char16_t* p = str;
|
||||
const char16_t* e = p + size();
|
||||
while (p < e) {
|
||||
if (*p == c) {
|
||||
return p-str;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t String16::findLast(char16_t c) const
|
||||
{
|
||||
const char16_t* str = string();
|
||||
const char16_t* p = str;
|
||||
const char16_t* e = p + size();
|
||||
while (p < e) {
|
||||
e--;
|
||||
if (*e == c) {
|
||||
return e-str;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool String16::startsWith(const String16& prefix) const
|
||||
{
|
||||
const size_t ps = prefix.size();
|
||||
if (ps > size()) return false;
|
||||
return strzcmp16(mString, ps, prefix.string(), ps) == 0;
|
||||
}
|
||||
|
||||
bool String16::startsWith(const char16_t* prefix) const
|
||||
{
|
||||
const size_t ps = strlen16(prefix);
|
||||
if (ps > size()) return false;
|
||||
return strncmp16(mString, prefix, ps) == 0;
|
||||
}
|
||||
|
||||
status_t String16::makeLower()
|
||||
{
|
||||
const size_t N = size();
|
||||
const char16_t* str = string();
|
||||
char16_t* edit = NULL;
|
||||
for (size_t i=0; i<N; i++) {
|
||||
const char16_t v = str[i];
|
||||
if (v >= 'A' && v <= 'Z') {
|
||||
if (!edit) {
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
|
||||
if (!buf) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
edit = (char16_t*)buf->data();
|
||||
mString = str = edit;
|
||||
}
|
||||
edit[i] = tolower((char)v);
|
||||
}
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
|
||||
{
|
||||
const size_t N = size();
|
||||
const char16_t* str = string();
|
||||
char16_t* edit = NULL;
|
||||
for (size_t i=0; i<N; i++) {
|
||||
if (str[i] == replaceThis) {
|
||||
if (!edit) {
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
|
||||
if (!buf) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
edit = (char16_t*)buf->data();
|
||||
mString = str = edit;
|
||||
}
|
||||
edit[i] = withThis;
|
||||
}
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t String16::remove(size_t len, size_t begin)
|
||||
{
|
||||
const size_t N = size();
|
||||
if (begin >= N) {
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
mString = getEmptyString();
|
||||
return NO_ERROR;
|
||||
}
|
||||
if ((begin+len) > N) len = N-begin;
|
||||
if (begin == 0 && len == N) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
if (begin > 0) {
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize((N+1)*sizeof(char16_t));
|
||||
if (!buf) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
memmove(str, str+begin, (N-begin+1)*sizeof(char16_t));
|
||||
mString = str;
|
||||
}
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize((len+1)*sizeof(char16_t));
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
str[len] = 0;
|
||||
mString = str;
|
||||
return NO_ERROR;
|
||||
}
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
TextOutput& operator<<(TextOutput& to, const String16& val)
|
||||
{
|
||||
to << String8(val).string();
|
||||
return to;
|
||||
}
|
||||
|
||||
}; // namespace android
|
@ -1,238 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_STRING16_H
|
||||
#define ANDROID_STRING16_H
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/SharedBuffer.h>
|
||||
#include "Unicode.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
extern "C" {
|
||||
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace android {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class String8;
|
||||
class TextOutput;
|
||||
|
||||
//! This is a string holding UTF-16 characters.
|
||||
class String16
|
||||
{
|
||||
public:
|
||||
String16();
|
||||
String16(const String16& o);
|
||||
String16(const String16& o,
|
||||
size_t len,
|
||||
size_t begin=0);
|
||||
explicit String16(const char16_t* o);
|
||||
explicit String16(const char16_t* o, size_t len);
|
||||
explicit String16(const String8& o);
|
||||
explicit String16(const char* o);
|
||||
explicit String16(const char* o, size_t len);
|
||||
|
||||
~String16();
|
||||
|
||||
inline const char16_t* string() const;
|
||||
inline size_t size() const;
|
||||
|
||||
inline const SharedBuffer* sharedBuffer() const;
|
||||
|
||||
void setTo(const String16& other);
|
||||
status_t setTo(const char16_t* other);
|
||||
status_t setTo(const char16_t* other, size_t len);
|
||||
status_t setTo(const String16& other,
|
||||
size_t len,
|
||||
size_t begin=0);
|
||||
|
||||
status_t append(const String16& other);
|
||||
status_t append(const char16_t* other, size_t len);
|
||||
|
||||
inline String16& operator=(const String16& other);
|
||||
|
||||
inline String16& operator+=(const String16& other);
|
||||
inline String16 operator+(const String16& other) const;
|
||||
|
||||
status_t insert(size_t pos, const char16_t* chrs);
|
||||
status_t insert(size_t pos,
|
||||
const char16_t* chrs, size_t len);
|
||||
|
||||
ssize_t findFirst(char16_t c) const;
|
||||
ssize_t findLast(char16_t c) const;
|
||||
|
||||
bool startsWith(const String16& prefix) const;
|
||||
bool startsWith(const char16_t* prefix) const;
|
||||
|
||||
status_t makeLower();
|
||||
|
||||
status_t replaceAll(char16_t replaceThis,
|
||||
char16_t withThis);
|
||||
|
||||
status_t remove(size_t len, size_t begin=0);
|
||||
|
||||
inline int compare(const String16& other) const;
|
||||
|
||||
inline bool operator<(const String16& other) const;
|
||||
inline bool operator<=(const String16& other) const;
|
||||
inline bool operator==(const String16& other) const;
|
||||
inline bool operator!=(const String16& other) const;
|
||||
inline bool operator>=(const String16& other) const;
|
||||
inline bool operator>(const String16& other) const;
|
||||
|
||||
inline bool operator<(const char16_t* other) const;
|
||||
inline bool operator<=(const char16_t* other) const;
|
||||
inline bool operator==(const char16_t* other) const;
|
||||
inline bool operator!=(const char16_t* other) const;
|
||||
inline bool operator>=(const char16_t* other) const;
|
||||
inline bool operator>(const char16_t* other) const;
|
||||
|
||||
inline operator const char16_t*() const;
|
||||
|
||||
private:
|
||||
const char16_t* mString;
|
||||
};
|
||||
|
||||
TextOutput& operator<<(TextOutput& to, const String16& val);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// No user servicable parts below.
|
||||
|
||||
inline int compare_type(const String16& lhs, const String16& rhs)
|
||||
{
|
||||
return lhs.compare(rhs);
|
||||
}
|
||||
|
||||
inline int strictly_order_type(const String16& lhs, const String16& rhs)
|
||||
{
|
||||
return compare_type(lhs, rhs) < 0;
|
||||
}
|
||||
|
||||
inline const char16_t* String16::string() const
|
||||
{
|
||||
return mString;
|
||||
}
|
||||
|
||||
inline size_t String16::size() const
|
||||
{
|
||||
return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
|
||||
}
|
||||
|
||||
inline const SharedBuffer* String16::sharedBuffer() const
|
||||
{
|
||||
return SharedBuffer::bufferFromData(mString);
|
||||
}
|
||||
|
||||
inline String16& String16::operator=(const String16& other)
|
||||
{
|
||||
setTo(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline String16& String16::operator+=(const String16& other)
|
||||
{
|
||||
append(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline String16 String16::operator+(const String16& other) const
|
||||
{
|
||||
String16 tmp(*this);
|
||||
tmp += other;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline int String16::compare(const String16& other) const
|
||||
{
|
||||
return strzcmp16(mString, size(), other.mString, other.size());
|
||||
}
|
||||
|
||||
inline bool String16::operator<(const String16& other) const
|
||||
{
|
||||
return strzcmp16(mString, size(), other.mString, other.size()) < 0;
|
||||
}
|
||||
|
||||
inline bool String16::operator<=(const String16& other) const
|
||||
{
|
||||
return strzcmp16(mString, size(), other.mString, other.size()) <= 0;
|
||||
}
|
||||
|
||||
inline bool String16::operator==(const String16& other) const
|
||||
{
|
||||
return strzcmp16(mString, size(), other.mString, other.size()) == 0;
|
||||
}
|
||||
|
||||
inline bool String16::operator!=(const String16& other) const
|
||||
{
|
||||
return strzcmp16(mString, size(), other.mString, other.size()) != 0;
|
||||
}
|
||||
|
||||
inline bool String16::operator>=(const String16& other) const
|
||||
{
|
||||
return strzcmp16(mString, size(), other.mString, other.size()) >= 0;
|
||||
}
|
||||
|
||||
inline bool String16::operator>(const String16& other) const
|
||||
{
|
||||
return strzcmp16(mString, size(), other.mString, other.size()) > 0;
|
||||
}
|
||||
|
||||
inline bool String16::operator<(const char16_t* other) const
|
||||
{
|
||||
return strcmp16(mString, other) < 0;
|
||||
}
|
||||
|
||||
inline bool String16::operator<=(const char16_t* other) const
|
||||
{
|
||||
return strcmp16(mString, other) <= 0;
|
||||
}
|
||||
|
||||
inline bool String16::operator==(const char16_t* other) const
|
||||
{
|
||||
return strcmp16(mString, other) == 0;
|
||||
}
|
||||
|
||||
inline bool String16::operator!=(const char16_t* other) const
|
||||
{
|
||||
return strcmp16(mString, other) != 0;
|
||||
}
|
||||
|
||||
inline bool String16::operator>=(const char16_t* other) const
|
||||
{
|
||||
return strcmp16(mString, other) >= 0;
|
||||
}
|
||||
|
||||
inline bool String16::operator>(const char16_t* other) const
|
||||
{
|
||||
return strcmp16(mString, other) > 0;
|
||||
}
|
||||
|
||||
inline String16::operator const char16_t*() const
|
||||
{
|
||||
return mString;
|
||||
}
|
||||
|
||||
}; // namespace android
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#endif // ANDROID_STRING16_H
|
@ -1,636 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
#include "String8.h"
|
||||
|
||||
#include "utils_Log.h"
|
||||
#include "Unicode.h"
|
||||
#include <utils/SharedBuffer.h>
|
||||
#include "String16.h"
|
||||
#include <utils/TextOutput.h>
|
||||
#include <utils/threads.h>
|
||||
|
||||
#include "Static.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#define OS_PATH_SEPARATOR '/'
|
||||
|
||||
/*
|
||||
* Functions outside android is below the namespace android, since they use
|
||||
* functions and constants in android namespace.
|
||||
*/
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace android {
|
||||
|
||||
// Separator used by resource paths. This is not platform dependent contrary
|
||||
// to OS_PATH_SEPARATOR.
|
||||
#define RES_PATH_SEPARATOR '/'
|
||||
|
||||
static SharedBuffer* gEmptyStringBuf = NULL;
|
||||
static char* gEmptyString = NULL;
|
||||
|
||||
extern int gDarwinCantLoadAllObjects;
|
||||
int gDarwinIsReallyAnnoying;
|
||||
|
||||
static inline char* getEmptyString()
|
||||
{
|
||||
gEmptyStringBuf->acquire();
|
||||
return gEmptyString;
|
||||
}
|
||||
|
||||
void initialize_string8()
|
||||
{
|
||||
// HACK: This dummy dependency forces linking libutils Static.cpp,
|
||||
// which is needed to initialize String8/String16 classes.
|
||||
// These variables are named for Darwin, but are needed elsewhere too,
|
||||
// including static linking on any platform.
|
||||
gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects;
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::alloc(1);
|
||||
char* str = (char*)buf->data();
|
||||
*str = 0;
|
||||
gEmptyStringBuf = buf;
|
||||
gEmptyString = str;
|
||||
}
|
||||
|
||||
void terminate_string8()
|
||||
{
|
||||
SharedBuffer::bufferFromData(gEmptyString)->release();
|
||||
gEmptyStringBuf = NULL;
|
||||
gEmptyString = NULL;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static char* allocFromUTF8(const char* in, size_t len)
|
||||
{
|
||||
if (len > 0) {
|
||||
SharedBuffer* buf = SharedBuffer::alloc(len+1);
|
||||
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
|
||||
if (buf) {
|
||||
char* str = (char*)buf->data();
|
||||
memcpy(str, in, len);
|
||||
str[len] = 0;
|
||||
return str;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return getEmptyString();
|
||||
}
|
||||
|
||||
static char* allocFromUTF16(const char16_t* in, size_t len)
|
||||
{
|
||||
if (len == 0) return getEmptyString();
|
||||
|
||||
const ssize_t bytes = utf16_to_utf8_length(in, len);
|
||||
if (bytes < 0) {
|
||||
return getEmptyString();
|
||||
}
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
|
||||
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
|
||||
if (!buf) {
|
||||
return getEmptyString();
|
||||
}
|
||||
|
||||
char* str = (char*)buf->data();
|
||||
utf16_to_utf8(in, len, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
static char* allocFromUTF32(const char32_t* in, size_t len)
|
||||
{
|
||||
if (len == 0) {
|
||||
return getEmptyString();
|
||||
}
|
||||
|
||||
const ssize_t bytes = utf32_to_utf8_length(in, len);
|
||||
if (bytes < 0) {
|
||||
return getEmptyString();
|
||||
}
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
|
||||
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
|
||||
if (!buf) {
|
||||
return getEmptyString();
|
||||
}
|
||||
|
||||
char* str = (char*) buf->data();
|
||||
utf32_to_utf8(in, len, str);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
String8::String8()
|
||||
: mString(getEmptyString())
|
||||
{
|
||||
}
|
||||
|
||||
String8::String8(const String8& o)
|
||||
: mString(o.mString)
|
||||
{
|
||||
SharedBuffer::bufferFromData(mString)->acquire();
|
||||
}
|
||||
|
||||
String8::String8(const char* o)
|
||||
: mString(allocFromUTF8(o, strlen(o)))
|
||||
{
|
||||
if (mString == NULL) {
|
||||
mString = getEmptyString();
|
||||
}
|
||||
}
|
||||
|
||||
String8::String8(const char* o, size_t len)
|
||||
: mString(allocFromUTF8(o, len))
|
||||
{
|
||||
if (mString == NULL) {
|
||||
mString = getEmptyString();
|
||||
}
|
||||
}
|
||||
|
||||
String8::String8(const String16& o)
|
||||
: mString(allocFromUTF16(o.string(), o.size()))
|
||||
{
|
||||
}
|
||||
|
||||
String8::String8(const char16_t* o)
|
||||
: mString(allocFromUTF16(o, strlen16(o)))
|
||||
{
|
||||
}
|
||||
|
||||
String8::String8(const char16_t* o, size_t len)
|
||||
: mString(allocFromUTF16(o, len))
|
||||
{
|
||||
}
|
||||
|
||||
String8::String8(const char32_t* o)
|
||||
: mString(allocFromUTF32(o, strlen32(o)))
|
||||
{
|
||||
}
|
||||
|
||||
String8::String8(const char32_t* o, size_t len)
|
||||
: mString(allocFromUTF32(o, len))
|
||||
{
|
||||
}
|
||||
|
||||
String8::~String8()
|
||||
{
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
}
|
||||
|
||||
String8 String8::format(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
String8 result(formatV(fmt, args));
|
||||
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
String8 String8::formatV(const char* fmt, va_list args)
|
||||
{
|
||||
String8 result;
|
||||
result.appendFormatV(fmt, args);
|
||||
return result;
|
||||
}
|
||||
|
||||
void String8::clear() {
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
mString = getEmptyString();
|
||||
}
|
||||
|
||||
void String8::setTo(const String8& other)
|
||||
{
|
||||
SharedBuffer::bufferFromData(other.mString)->acquire();
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
mString = other.mString;
|
||||
}
|
||||
|
||||
status_t String8::setTo(const char* other)
|
||||
{
|
||||
const char *newString = allocFromUTF8(other, strlen(other));
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
mString = newString;
|
||||
if (mString) return NO_ERROR;
|
||||
|
||||
mString = getEmptyString();
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
status_t String8::setTo(const char* other, size_t len)
|
||||
{
|
||||
const char *newString = allocFromUTF8(other, len);
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
mString = newString;
|
||||
if (mString) return NO_ERROR;
|
||||
|
||||
mString = getEmptyString();
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
status_t String8::setTo(const char16_t* other, size_t len)
|
||||
{
|
||||
const char *newString = allocFromUTF16(other, len);
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
mString = newString;
|
||||
if (mString) return NO_ERROR;
|
||||
|
||||
mString = getEmptyString();
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
status_t String8::setTo(const char32_t* other, size_t len)
|
||||
{
|
||||
const char *newString = allocFromUTF32(other, len);
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
mString = newString;
|
||||
if (mString) return NO_ERROR;
|
||||
|
||||
mString = getEmptyString();
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
status_t String8::append(const String8& other)
|
||||
{
|
||||
const size_t otherLen = other.bytes();
|
||||
if (bytes() == 0) {
|
||||
setTo(other);
|
||||
return NO_ERROR;
|
||||
} else if (otherLen == 0) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
return real_append(other.string(), otherLen);
|
||||
}
|
||||
|
||||
status_t String8::append(const char* other)
|
||||
{
|
||||
return append(other, strlen(other));
|
||||
}
|
||||
|
||||
status_t String8::append(const char* other, size_t otherLen)
|
||||
{
|
||||
if (bytes() == 0) {
|
||||
return setTo(other, otherLen);
|
||||
} else if (otherLen == 0) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
return real_append(other, otherLen);
|
||||
}
|
||||
|
||||
status_t String8::appendFormat(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
status_t result = appendFormatV(fmt, args);
|
||||
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t String8::appendFormatV(const char* fmt, va_list args)
|
||||
{
|
||||
int result = NO_ERROR;
|
||||
int n = vsnprintf(NULL, 0, fmt, args);
|
||||
if (n != 0) {
|
||||
size_t oldLength = length();
|
||||
char* buf = lockBuffer(oldLength + n);
|
||||
if (buf) {
|
||||
vsnprintf(buf + oldLength, n + 1, fmt, args);
|
||||
} else {
|
||||
result = NO_MEMORY;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t String8::real_append(const char* other, size_t otherLen)
|
||||
{
|
||||
const size_t myLen = bytes();
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize(myLen+otherLen+1);
|
||||
if (buf) {
|
||||
char* str = (char*)buf->data();
|
||||
mString = str;
|
||||
str += myLen;
|
||||
memcpy(str, other, otherLen);
|
||||
str[otherLen] = '\0';
|
||||
return NO_ERROR;
|
||||
}
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
char* String8::lockBuffer(size_t size)
|
||||
{
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize(size+1);
|
||||
if (buf) {
|
||||
char* str = (char*)buf->data();
|
||||
mString = str;
|
||||
return str;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void String8::unlockBuffer()
|
||||
{
|
||||
unlockBuffer(strlen(mString));
|
||||
}
|
||||
|
||||
status_t String8::unlockBuffer(size_t size)
|
||||
{
|
||||
if (size != this->size()) {
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize(size+1);
|
||||
if (! buf) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
char* str = (char*)buf->data();
|
||||
str[size] = 0;
|
||||
mString = str;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
ssize_t String8::find(const char* other, size_t start) const
|
||||
{
|
||||
size_t len = size();
|
||||
if (start >= len) {
|
||||
return -1;
|
||||
}
|
||||
const char* s = mString+start;
|
||||
const char* p = strstr(s, other);
|
||||
return p ? p-mString : -1;
|
||||
}
|
||||
|
||||
void String8::toLower()
|
||||
{
|
||||
toLower(0, size());
|
||||
}
|
||||
|
||||
void String8::toLower(size_t start, size_t length)
|
||||
{
|
||||
const size_t len = size();
|
||||
if (start >= len) {
|
||||
return;
|
||||
}
|
||||
if (start+length > len) {
|
||||
length = len-start;
|
||||
}
|
||||
char* buf = lockBuffer(len);
|
||||
buf += start;
|
||||
while (length > 0) {
|
||||
*buf = tolower(*buf);
|
||||
buf++;
|
||||
length--;
|
||||
}
|
||||
unlockBuffer(len);
|
||||
}
|
||||
|
||||
void String8::toUpper()
|
||||
{
|
||||
toUpper(0, size());
|
||||
}
|
||||
|
||||
void String8::toUpper(size_t start, size_t length)
|
||||
{
|
||||
const size_t len = size();
|
||||
if (start >= len) {
|
||||
return;
|
||||
}
|
||||
if (start+length > len) {
|
||||
length = len-start;
|
||||
}
|
||||
char* buf = lockBuffer(len);
|
||||
buf += start;
|
||||
while (length > 0) {
|
||||
*buf = toupper(*buf);
|
||||
buf++;
|
||||
length--;
|
||||
}
|
||||
unlockBuffer(len);
|
||||
}
|
||||
|
||||
size_t String8::getUtf32Length() const
|
||||
{
|
||||
return utf8_to_utf32_length(mString, length());
|
||||
}
|
||||
|
||||
int32_t String8::getUtf32At(size_t index, size_t *next_index) const
|
||||
{
|
||||
return utf32_from_utf8_at(mString, length(), index, next_index);
|
||||
}
|
||||
|
||||
void String8::getUtf32(char32_t* dst) const
|
||||
{
|
||||
utf8_to_utf32(mString, length(), dst);
|
||||
}
|
||||
|
||||
TextOutput& operator<<(TextOutput& to, const String8& val)
|
||||
{
|
||||
to << val.string();
|
||||
return to;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Path functions
|
||||
|
||||
void String8::setPathName(const char* name)
|
||||
{
|
||||
setPathName(name, strlen(name));
|
||||
}
|
||||
|
||||
void String8::setPathName(const char* name, size_t len)
|
||||
{
|
||||
char* buf = lockBuffer(len);
|
||||
|
||||
memcpy(buf, name, len);
|
||||
|
||||
// remove trailing path separator, if present
|
||||
if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR)
|
||||
len--;
|
||||
|
||||
buf[len] = '\0';
|
||||
|
||||
unlockBuffer(len);
|
||||
}
|
||||
|
||||
String8 String8::getPathLeaf(void) const
|
||||
{
|
||||
const char* cp;
|
||||
const char*const buf = mString;
|
||||
|
||||
cp = strrchr(buf, OS_PATH_SEPARATOR);
|
||||
if (cp == NULL)
|
||||
return String8(*this);
|
||||
else
|
||||
return String8(cp+1);
|
||||
}
|
||||
|
||||
String8 String8::getPathDir(void) const
|
||||
{
|
||||
const char* cp;
|
||||
const char*const str = mString;
|
||||
|
||||
cp = strrchr(str, OS_PATH_SEPARATOR);
|
||||
if (cp == NULL)
|
||||
return String8("");
|
||||
else
|
||||
return String8(str, cp - str);
|
||||
}
|
||||
|
||||
String8 String8::walkPath(String8* outRemains) const
|
||||
{
|
||||
const char* cp;
|
||||
const char*const str = mString;
|
||||
const char* buf = str;
|
||||
|
||||
cp = strchr(buf, OS_PATH_SEPARATOR);
|
||||
if (cp == buf) {
|
||||
// don't include a leading '/'.
|
||||
buf = buf+1;
|
||||
cp = strchr(buf, OS_PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
if (cp == NULL) {
|
||||
String8 res = buf != str ? String8(buf) : *this;
|
||||
if (outRemains) *outRemains = String8("");
|
||||
return res;
|
||||
}
|
||||
|
||||
String8 res(buf, cp-buf);
|
||||
if (outRemains) *outRemains = String8(cp+1);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function for finding the start of an extension in a pathname.
|
||||
*
|
||||
* Returns a pointer inside mString, or NULL if no extension was found.
|
||||
*/
|
||||
char* String8::find_extension(void) const
|
||||
{
|
||||
const char* lastSlash;
|
||||
const char* lastDot;
|
||||
int extLen;
|
||||
const char* const str = mString;
|
||||
|
||||
// only look at the filename
|
||||
lastSlash = strrchr(str, OS_PATH_SEPARATOR);
|
||||
if (lastSlash == NULL)
|
||||
lastSlash = str;
|
||||
else
|
||||
lastSlash++;
|
||||
|
||||
// find the last dot
|
||||
lastDot = strrchr(lastSlash, '.');
|
||||
if (lastDot == NULL)
|
||||
return NULL;
|
||||
|
||||
// looks good, ship it
|
||||
return const_cast<char*>(lastDot);
|
||||
}
|
||||
|
||||
String8 String8::getPathExtension(void) const
|
||||
{
|
||||
char* ext;
|
||||
|
||||
ext = find_extension();
|
||||
if (ext != NULL)
|
||||
return String8(ext);
|
||||
else
|
||||
return String8("");
|
||||
}
|
||||
|
||||
String8 String8::getBasePath(void) const
|
||||
{
|
||||
char* ext;
|
||||
const char* const str = mString;
|
||||
|
||||
ext = find_extension();
|
||||
if (ext == NULL)
|
||||
return String8(*this);
|
||||
else
|
||||
return String8(str, ext - str);
|
||||
}
|
||||
|
||||
String8& String8::appendPath(const char* name)
|
||||
{
|
||||
// TODO: The test below will fail for Win32 paths. Fix later or ignore.
|
||||
if (name[0] != OS_PATH_SEPARATOR) {
|
||||
if (*name == '\0') {
|
||||
// nothing to do
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t len = length();
|
||||
if (len == 0) {
|
||||
// no existing filename, just use the new one
|
||||
setPathName(name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// make room for oldPath + '/' + newPath
|
||||
int newlen = strlen(name);
|
||||
|
||||
char* buf = lockBuffer(len+1+newlen);
|
||||
|
||||
// insert a '/' if needed
|
||||
if (buf[len-1] != OS_PATH_SEPARATOR)
|
||||
buf[len++] = OS_PATH_SEPARATOR;
|
||||
|
||||
memcpy(buf+len, name, newlen+1);
|
||||
len += newlen;
|
||||
|
||||
unlockBuffer(len);
|
||||
|
||||
return *this;
|
||||
} else {
|
||||
setPathName(name);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
String8& String8::convertToResPath()
|
||||
{
|
||||
#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR
|
||||
size_t len = length();
|
||||
if (len > 0) {
|
||||
char * buf = lockBuffer(len);
|
||||
for (char * end = buf + len; buf < end; ++buf) {
|
||||
if (*buf == OS_PATH_SEPARATOR)
|
||||
*buf = RES_PATH_SEPARATOR;
|
||||
}
|
||||
unlockBuffer(len);
|
||||
}
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
}; // namespace android
|
@ -1,383 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_STRING8_H
|
||||
#define ANDROID_STRING8_H
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/SharedBuffer.h>
|
||||
#include "Unicode.h"
|
||||
|
||||
#include <string.h> // for strcmp
|
||||
#include <stdarg.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace android {
|
||||
|
||||
class String16;
|
||||
class TextOutput;
|
||||
|
||||
//! This is a string holding UTF-8 characters. Does not allow the value more
|
||||
// than 0x10FFFF, which is not valid unicode codepoint.
|
||||
class String8
|
||||
{
|
||||
public:
|
||||
String8();
|
||||
String8(const String8& o);
|
||||
explicit String8(const char* o);
|
||||
explicit String8(const char* o, size_t numChars);
|
||||
|
||||
explicit String8(const String16& o);
|
||||
explicit String8(const char16_t* o);
|
||||
explicit String8(const char16_t* o, size_t numChars);
|
||||
explicit String8(const char32_t* o);
|
||||
explicit String8(const char32_t* o, size_t numChars);
|
||||
~String8();
|
||||
|
||||
static inline const String8 empty();
|
||||
|
||||
static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
|
||||
static String8 formatV(const char* fmt, va_list args);
|
||||
|
||||
inline const char* string() const;
|
||||
inline size_t size() const;
|
||||
inline size_t length() const;
|
||||
inline size_t bytes() const;
|
||||
inline bool isEmpty() const;
|
||||
|
||||
inline const SharedBuffer* sharedBuffer() const;
|
||||
|
||||
void clear();
|
||||
|
||||
void setTo(const String8& other);
|
||||
status_t setTo(const char* other);
|
||||
status_t setTo(const char* other, size_t numChars);
|
||||
status_t setTo(const char16_t* other, size_t numChars);
|
||||
status_t setTo(const char32_t* other,
|
||||
size_t length);
|
||||
|
||||
status_t append(const String8& other);
|
||||
status_t append(const char* other);
|
||||
status_t append(const char* other, size_t numChars);
|
||||
|
||||
status_t appendFormat(const char* fmt, ...)
|
||||
__attribute__((format (printf, 2, 3)));
|
||||
status_t appendFormatV(const char* fmt, va_list args);
|
||||
|
||||
// Note that this function takes O(N) time to calculate the value.
|
||||
// No cache value is stored.
|
||||
size_t getUtf32Length() const;
|
||||
int32_t getUtf32At(size_t index,
|
||||
size_t *next_index) const;
|
||||
void getUtf32(char32_t* dst) const;
|
||||
|
||||
inline String8& operator=(const String8& other);
|
||||
inline String8& operator=(const char* other);
|
||||
|
||||
inline String8& operator+=(const String8& other);
|
||||
inline String8 operator+(const String8& other) const;
|
||||
|
||||
inline String8& operator+=(const char* other);
|
||||
inline String8 operator+(const char* other) const;
|
||||
|
||||
inline int compare(const String8& other) const;
|
||||
|
||||
inline bool operator<(const String8& other) const;
|
||||
inline bool operator<=(const String8& other) const;
|
||||
inline bool operator==(const String8& other) const;
|
||||
inline bool operator!=(const String8& other) const;
|
||||
inline bool operator>=(const String8& other) const;
|
||||
inline bool operator>(const String8& other) const;
|
||||
|
||||
inline bool operator<(const char* other) const;
|
||||
inline bool operator<=(const char* other) const;
|
||||
inline bool operator==(const char* other) const;
|
||||
inline bool operator!=(const char* other) const;
|
||||
inline bool operator>=(const char* other) const;
|
||||
inline bool operator>(const char* other) const;
|
||||
|
||||
inline operator const char*() const;
|
||||
|
||||
char* lockBuffer(size_t size);
|
||||
void unlockBuffer();
|
||||
status_t unlockBuffer(size_t size);
|
||||
|
||||
// return the index of the first byte of other in this at or after
|
||||
// start, or -1 if not found
|
||||
ssize_t find(const char* other, size_t start = 0) const;
|
||||
|
||||
void toLower();
|
||||
void toLower(size_t start, size_t numChars);
|
||||
void toUpper();
|
||||
void toUpper(size_t start, size_t numChars);
|
||||
|
||||
/*
|
||||
* These methods operate on the string as if it were a path name.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Set the filename field to a specific value.
|
||||
*
|
||||
* Normalizes the filename, removing a trailing '/' if present.
|
||||
*/
|
||||
void setPathName(const char* name);
|
||||
void setPathName(const char* name, size_t numChars);
|
||||
|
||||
/*
|
||||
* Get just the filename component.
|
||||
*
|
||||
* "/tmp/foo/bar.c" --> "bar.c"
|
||||
*/
|
||||
String8 getPathLeaf(void) const;
|
||||
|
||||
/*
|
||||
* Remove the last (file name) component, leaving just the directory
|
||||
* name.
|
||||
*
|
||||
* "/tmp/foo/bar.c" --> "/tmp/foo"
|
||||
* "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX
|
||||
* "bar.c" --> ""
|
||||
*/
|
||||
String8 getPathDir(void) const;
|
||||
|
||||
/*
|
||||
* Retrieve the front (root dir) component. Optionally also return the
|
||||
* remaining components.
|
||||
*
|
||||
* "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c")
|
||||
* "/tmp" --> "tmp" (remain = "")
|
||||
* "bar.c" --> "bar.c" (remain = "")
|
||||
*/
|
||||
String8 walkPath(String8* outRemains = NULL) const;
|
||||
|
||||
/*
|
||||
* Return the filename extension. This is the last '.' and any number
|
||||
* of characters that follow it. The '.' is included in case we
|
||||
* decide to expand our definition of what constitutes an extension.
|
||||
*
|
||||
* "/tmp/foo/bar.c" --> ".c"
|
||||
* "/tmp" --> ""
|
||||
* "/tmp/foo.bar/baz" --> ""
|
||||
* "foo.jpeg" --> ".jpeg"
|
||||
* "foo." --> ""
|
||||
*/
|
||||
String8 getPathExtension(void) const;
|
||||
|
||||
/*
|
||||
* Return the path without the extension. Rules for what constitutes
|
||||
* an extension are described in the comment for getPathExtension().
|
||||
*
|
||||
* "/tmp/foo/bar.c" --> "/tmp/foo/bar"
|
||||
*/
|
||||
String8 getBasePath(void) const;
|
||||
|
||||
/*
|
||||
* Add a component to the pathname. We guarantee that there is
|
||||
* exactly one path separator between the old path and the new.
|
||||
* If there is no existing name, we just copy the new name in.
|
||||
*
|
||||
* If leaf is a fully qualified path (i.e. starts with '/', it
|
||||
* replaces whatever was there before.
|
||||
*/
|
||||
String8& appendPath(const char* leaf);
|
||||
String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); }
|
||||
|
||||
/*
|
||||
* Like appendPath(), but does not affect this string. Returns a new one instead.
|
||||
*/
|
||||
String8 appendPathCopy(const char* leaf) const
|
||||
{ String8 p(*this); p.appendPath(leaf); return p; }
|
||||
String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); }
|
||||
|
||||
/*
|
||||
* Converts all separators in this string to /, the default path separator.
|
||||
*
|
||||
* If the default OS separator is backslash, this converts all
|
||||
* backslashes to slashes, in-place. Otherwise it does nothing.
|
||||
* Returns self.
|
||||
*/
|
||||
String8& convertToResPath();
|
||||
|
||||
private:
|
||||
status_t real_append(const char* other, size_t numChars);
|
||||
char* find_extension(void) const;
|
||||
|
||||
const char* mString;
|
||||
};
|
||||
|
||||
TextOutput& operator<<(TextOutput& to, const String16& val);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// No user servicable parts below.
|
||||
|
||||
inline int compare_type(const String8& lhs, const String8& rhs)
|
||||
{
|
||||
return lhs.compare(rhs);
|
||||
}
|
||||
|
||||
inline int strictly_order_type(const String8& lhs, const String8& rhs)
|
||||
{
|
||||
return compare_type(lhs, rhs) < 0;
|
||||
}
|
||||
|
||||
inline const String8 String8::empty() {
|
||||
return String8();
|
||||
}
|
||||
|
||||
inline const char* String8::string() const
|
||||
{
|
||||
return mString;
|
||||
}
|
||||
|
||||
inline size_t String8::length() const
|
||||
{
|
||||
return SharedBuffer::sizeFromData(mString)-1;
|
||||
}
|
||||
|
||||
inline size_t String8::size() const
|
||||
{
|
||||
return length();
|
||||
}
|
||||
|
||||
inline bool String8::isEmpty() const
|
||||
{
|
||||
return length() == 0;
|
||||
}
|
||||
|
||||
inline size_t String8::bytes() const
|
||||
{
|
||||
return SharedBuffer::sizeFromData(mString)-1;
|
||||
}
|
||||
|
||||
inline const SharedBuffer* String8::sharedBuffer() const
|
||||
{
|
||||
return SharedBuffer::bufferFromData(mString);
|
||||
}
|
||||
|
||||
inline String8& String8::operator=(const String8& other)
|
||||
{
|
||||
setTo(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline String8& String8::operator=(const char* other)
|
||||
{
|
||||
setTo(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline String8& String8::operator+=(const String8& other)
|
||||
{
|
||||
append(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline String8 String8::operator+(const String8& other) const
|
||||
{
|
||||
String8 tmp(*this);
|
||||
tmp += other;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline String8& String8::operator+=(const char* other)
|
||||
{
|
||||
append(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline String8 String8::operator+(const char* other) const
|
||||
{
|
||||
String8 tmp(*this);
|
||||
tmp += other;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline int String8::compare(const String8& other) const
|
||||
{
|
||||
return strcmp(mString, other.mString);
|
||||
}
|
||||
|
||||
inline bool String8::operator<(const String8& other) const
|
||||
{
|
||||
return strcmp(mString, other.mString) < 0;
|
||||
}
|
||||
|
||||
inline bool String8::operator<=(const String8& other) const
|
||||
{
|
||||
return strcmp(mString, other.mString) <= 0;
|
||||
}
|
||||
|
||||
inline bool String8::operator==(const String8& other) const
|
||||
{
|
||||
return strcmp(mString, other.mString) == 0;
|
||||
}
|
||||
|
||||
inline bool String8::operator!=(const String8& other) const
|
||||
{
|
||||
return strcmp(mString, other.mString) != 0;
|
||||
}
|
||||
|
||||
inline bool String8::operator>=(const String8& other) const
|
||||
{
|
||||
return strcmp(mString, other.mString) >= 0;
|
||||
}
|
||||
|
||||
inline bool String8::operator>(const String8& other) const
|
||||
{
|
||||
return strcmp(mString, other.mString) > 0;
|
||||
}
|
||||
|
||||
inline bool String8::operator<(const char* other) const
|
||||
{
|
||||
return strcmp(mString, other) < 0;
|
||||
}
|
||||
|
||||
inline bool String8::operator<=(const char* other) const
|
||||
{
|
||||
return strcmp(mString, other) <= 0;
|
||||
}
|
||||
|
||||
inline bool String8::operator==(const char* other) const
|
||||
{
|
||||
return strcmp(mString, other) == 0;
|
||||
}
|
||||
|
||||
inline bool String8::operator!=(const char* other) const
|
||||
{
|
||||
return strcmp(mString, other) != 0;
|
||||
}
|
||||
|
||||
inline bool String8::operator>=(const char* other) const
|
||||
{
|
||||
return strcmp(mString, other) >= 0;
|
||||
}
|
||||
|
||||
inline bool String8::operator>(const char* other) const
|
||||
{
|
||||
return strcmp(mString, other) > 0;
|
||||
}
|
||||
|
||||
inline String8::operator const char*() const
|
||||
{
|
||||
return mString;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#endif // ANDROID_STRING8_H
|
@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// Timer functions.
|
||||
//
|
||||
#include "utils_Log.h"
|
||||
#include "Timers.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef HAVE_WIN32_THREADS
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
nsecs_t systemTime(int clock)
|
||||
{
|
||||
#if defined(HAVE_POSIX_CLOCKS)
|
||||
static const clockid_t clocks[] = {
|
||||
CLOCK_REALTIME,
|
||||
CLOCK_MONOTONIC,
|
||||
CLOCK_PROCESS_CPUTIME_ID,
|
||||
CLOCK_THREAD_CPUTIME_ID
|
||||
};
|
||||
struct timespec t;
|
||||
t.tv_sec = t.tv_nsec = 0;
|
||||
clock_gettime(clocks[clock], &t);
|
||||
return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec;
|
||||
#else
|
||||
// we don't support the clocks here.
|
||||
struct timeval t;
|
||||
t.tv_sec = t.tv_usec = 0;
|
||||
gettimeofday(&t, NULL);
|
||||
return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL;
|
||||
#endif
|
||||
}
|
||||
|
||||
int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime)
|
||||
{
|
||||
int timeoutDelayMillis;
|
||||
if (timeoutTime > referenceTime) {
|
||||
uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
|
||||
if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) {
|
||||
timeoutDelayMillis = -1;
|
||||
} else {
|
||||
timeoutDelayMillis = (timeoutDelay + 999999LL) / 1000000LL;
|
||||
}
|
||||
} else {
|
||||
timeoutDelayMillis = 0;
|
||||
}
|
||||
return timeoutDelayMillis;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* DurationTimer
|
||||
* ===========================================================================
|
||||
*/
|
||||
|
||||
using namespace android;
|
||||
|
||||
// Start the timer.
|
||||
void DurationTimer::start(void)
|
||||
{
|
||||
gettimeofday(&mStartWhen, NULL);
|
||||
}
|
||||
|
||||
// Stop the timer.
|
||||
void DurationTimer::stop(void)
|
||||
{
|
||||
gettimeofday(&mStopWhen, NULL);
|
||||
}
|
||||
|
||||
// Get the duration in microseconds.
|
||||
long long DurationTimer::durationUsecs(void) const
|
||||
{
|
||||
return (long) subtractTimevals(&mStopWhen, &mStartWhen);
|
||||
}
|
||||
|
||||
// Subtract two timevals. Returns the difference (ptv1-ptv2) in
|
||||
// microseconds.
|
||||
/*static*/ long long DurationTimer::subtractTimevals(const struct timeval* ptv1,
|
||||
const struct timeval* ptv2)
|
||||
{
|
||||
long long stop = ((long long) ptv1->tv_sec) * 1000000LL +
|
||||
((long long) ptv1->tv_usec);
|
||||
long long start = ((long long) ptv2->tv_sec) * 1000000LL +
|
||||
((long long) ptv2->tv_usec);
|
||||
return stop - start;
|
||||
}
|
||||
|
||||
// Add the specified amount of time to the timeval.
|
||||
/*static*/ void DurationTimer::addToTimeval(struct timeval* ptv, long usec)
|
||||
{
|
||||
if (usec < 0) {
|
||||
ALOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// normalize tv_usec if necessary
|
||||
if (ptv->tv_usec >= 1000000) {
|
||||
ptv->tv_sec += ptv->tv_usec / 1000000;
|
||||
ptv->tv_usec %= 1000000;
|
||||
}
|
||||
|
||||
ptv->tv_usec += usec % 1000000;
|
||||
if (ptv->tv_usec >= 1000000) {
|
||||
ptv->tv_usec -= 1000000;
|
||||
ptv->tv_sec++;
|
||||
}
|
||||
ptv->tv_sec += usec / 1000000;
|
||||
}
|
||||
|
@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// Timer functions.
|
||||
//
|
||||
#ifndef _LIBS_UTILS_TIMERS_H
|
||||
#define _LIBS_UTILS_TIMERS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// C API
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef int64_t nsecs_t; // nano-seconds
|
||||
|
||||
static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs)
|
||||
{
|
||||
return secs*1000000000;
|
||||
}
|
||||
|
||||
static inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs)
|
||||
{
|
||||
return secs*1000000;
|
||||
}
|
||||
|
||||
static inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs)
|
||||
{
|
||||
return secs*1000;
|
||||
}
|
||||
|
||||
static inline nsecs_t nanoseconds_to_seconds(nsecs_t secs)
|
||||
{
|
||||
return secs/1000000000;
|
||||
}
|
||||
|
||||
static inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs)
|
||||
{
|
||||
return secs/1000000;
|
||||
}
|
||||
|
||||
static inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs)
|
||||
{
|
||||
return secs/1000;
|
||||
}
|
||||
|
||||
static inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);}
|
||||
static inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);}
|
||||
static inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);}
|
||||
static inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);}
|
||||
static inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);}
|
||||
static inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);}
|
||||
|
||||
static inline nsecs_t seconds(nsecs_t v) { return s2ns(v); }
|
||||
static inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); }
|
||||
static inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); }
|
||||
|
||||
enum {
|
||||
SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock
|
||||
SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point
|
||||
SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock
|
||||
SYSTEM_TIME_THREAD = 3 // high-resolution per-thread clock
|
||||
};
|
||||
|
||||
// return the system-time according to the specified clock
|
||||
#ifdef __cplusplus
|
||||
nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC);
|
||||
#else
|
||||
nsecs_t systemTime(int clock);
|
||||
#endif // def __cplusplus
|
||||
|
||||
/**
|
||||
* Returns the number of milliseconds to wait between the reference time and the timeout time.
|
||||
* If the timeout is in the past relative to the reference time, returns 0.
|
||||
* If the timeout is more than INT_MAX milliseconds in the future relative to the reference time,
|
||||
* such as when timeoutTime == LLONG_MAX, returns -1 to indicate an infinite timeout delay.
|
||||
* Otherwise, returns the difference between the reference time and timeout time
|
||||
* rounded up to the next millisecond.
|
||||
*/
|
||||
int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// C++ API
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace android {
|
||||
/*
|
||||
* Time the duration of something.
|
||||
*
|
||||
* Includes some timeval manipulation functions.
|
||||
*/
|
||||
class DurationTimer {
|
||||
public:
|
||||
DurationTimer() {}
|
||||
~DurationTimer() {}
|
||||
|
||||
// Start the timer.
|
||||
void start();
|
||||
// Stop the timer.
|
||||
void stop();
|
||||
// Get the duration in microseconds.
|
||||
long long durationUsecs() const;
|
||||
|
||||
// Subtract two timevals. Returns the difference (ptv1-ptv2) in
|
||||
// microseconds.
|
||||
static long long subtractTimevals(const struct timeval* ptv1,
|
||||
const struct timeval* ptv2);
|
||||
|
||||
// Add the specified amount of time to the timeval.
|
||||
static void addToTimeval(struct timeval* ptv, long usec);
|
||||
|
||||
private:
|
||||
struct timeval mStartWhen;
|
||||
struct timeval mStopWhen;
|
||||
};
|
||||
|
||||
}; // android
|
||||
#endif // def __cplusplus
|
||||
|
||||
#endif // _LIBS_UTILS_TIMERS_H
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Tokenizer"
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
@ -22,7 +23,6 @@
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "utils_Log.h"
|
||||
#include "Tokenizer.h"
|
||||
|
||||
// Enables debug output for the tokenizer.
|
||||
@ -35,15 +35,18 @@ static inline bool isDelimiter(char ch, const char* delimiters) {
|
||||
return strchr(delimiters, ch) != NULL;
|
||||
}
|
||||
|
||||
Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length) :
|
||||
Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,
|
||||
bool ownBuffer, size_t length) :
|
||||
mFilename(filename), mFileMap(fileMap),
|
||||
mBuffer(buffer), mLength(length), mCurrent(buffer), mLineNumber(1) {
|
||||
mBuffer(buffer), mOwnBuffer(ownBuffer), mLength(length),
|
||||
mCurrent(buffer), mLineNumber(1) {
|
||||
}
|
||||
|
||||
Tokenizer::~Tokenizer() {
|
||||
if (mFileMap) {
|
||||
mFileMap->release();
|
||||
} else {
|
||||
}
|
||||
if (mOwnBuffer) {
|
||||
delete[] mBuffer;
|
||||
}
|
||||
}
|
||||
@ -65,6 +68,7 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
|
||||
size_t length = size_t(stat.st_size);
|
||||
|
||||
FileMap* fileMap = new FileMap();
|
||||
bool ownBuffer = false;
|
||||
char* buffer;
|
||||
if (fileMap->create(NULL, fd, 0, length, true)) {
|
||||
fileMap->advise(FileMap::SEQUENTIAL);
|
||||
@ -77,6 +81,7 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
|
||||
// The length we obtained from stat is wrong too (it will always be 4096)
|
||||
// so we must trust that read will read the entire file.
|
||||
buffer = new char[length];
|
||||
ownBuffer = true;
|
||||
ssize_t nrd = read(fd, buffer, length);
|
||||
if (nrd < 0) {
|
||||
result = -errno;
|
||||
@ -89,7 +94,7 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
*outTokenizer = new Tokenizer(filename, fileMap, buffer, length);
|
||||
*outTokenizer = new Tokenizer(filename, fileMap, buffer, ownBuffer, length);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
@ -97,6 +102,13 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t Tokenizer::fromContents(const String8& filename,
|
||||
const char* contents, Tokenizer** outTokenizer) {
|
||||
*outTokenizer = new Tokenizer(filename, NULL,
|
||||
const_cast<char*>(contents), false, strlen(contents));
|
||||
return OK;
|
||||
}
|
||||
|
||||
String8 Tokenizer::getLocation() const {
|
||||
String8 result;
|
||||
result.appendFormat("%s:%d", mFilename.string(), mLineNumber);
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include <assert.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/FileMap.h>
|
||||
#include "String8.h"
|
||||
#include <utils/String8.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
@ -28,7 +28,8 @@ namespace android {
|
||||
* A simple tokenizer for loading and parsing ASCII text files line by line.
|
||||
*/
|
||||
class Tokenizer {
|
||||
Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length);
|
||||
Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,
|
||||
bool ownBuffer, size_t length);
|
||||
|
||||
public:
|
||||
~Tokenizer();
|
||||
@ -41,6 +42,15 @@ public:
|
||||
*/
|
||||
static status_t open(const String8& filename, Tokenizer** outTokenizer);
|
||||
|
||||
/**
|
||||
* Prepares to tokenize the contents of a string.
|
||||
*
|
||||
* Returns NO_ERROR and a tokenizer for the string, if successful.
|
||||
* Otherwise returns an error and sets outTokenizer to NULL.
|
||||
*/
|
||||
static status_t fromContents(const String8& filename,
|
||||
const char* contents, Tokenizer** outTokenizer);
|
||||
|
||||
/**
|
||||
* Returns true if at the end of the file.
|
||||
*/
|
||||
@ -111,6 +121,7 @@ private:
|
||||
String8 mFilename;
|
||||
FileMap* mFileMap;
|
||||
char* mBuffer;
|
||||
bool mOwnBuffer;
|
||||
size_t mLength;
|
||||
|
||||
const char* mCurrent;
|
||||
|
64
widget/gonk/libui/Trace.h
Normal file
64
widget/gonk/libui/Trace.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_TRACE_H
|
||||
#define ANDROID_TRACE_H
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/threads.h>
|
||||
#include "cutils_trace.h"
|
||||
|
||||
// See <cutils/trace.h> for more ATRACE_* macros.
|
||||
|
||||
// ATRACE_NAME traces the beginning and end of the current scope. To trace
|
||||
// the correct start and end times this macro should be declared first in the
|
||||
// scope body.
|
||||
#define ATRACE_NAME(name) android::ScopedTrace ___tracer(ATRACE_TAG, name)
|
||||
// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
|
||||
#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
|
||||
|
||||
namespace android {
|
||||
|
||||
class ScopedTrace {
|
||||
public:
|
||||
inline ScopedTrace(uint64_t tag, const char* name)
|
||||
: mTag(tag) {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
atrace_begin(mTag,name);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline ~ScopedTrace() {
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
atrace_end(mTag);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t mTag;
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_TRACE_H
|
@ -1,576 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
#include "Unicode.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef HAVE_WINSOCK
|
||||
# undef nhtol
|
||||
# undef htonl
|
||||
# undef nhtos
|
||||
# undef htons
|
||||
|
||||
# ifdef HAVE_LITTLE_ENDIAN
|
||||
# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) )
|
||||
# define htonl(x) ntohl(x)
|
||||
# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) )
|
||||
# define htons(x) ntohs(x)
|
||||
# else
|
||||
# define ntohl(x) (x)
|
||||
# define htonl(x) (x)
|
||||
# define ntohs(x) (x)
|
||||
# define htons(x) (x)
|
||||
# endif
|
||||
#else
|
||||
# include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
|
||||
static const char32_t kByteMask = 0x000000BF;
|
||||
static const char32_t kByteMark = 0x00000080;
|
||||
|
||||
// Surrogates aren't valid for UTF-32 characters, so define some
|
||||
// constants that will let us screen them out.
|
||||
static const char32_t kUnicodeSurrogateHighStart = 0x0000D800;
|
||||
static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF;
|
||||
static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00;
|
||||
static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF;
|
||||
static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart;
|
||||
static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd;
|
||||
static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF;
|
||||
|
||||
// Mask used to set appropriate bits in first byte of UTF-8 sequence,
|
||||
// indexed by number of bytes in the sequence.
|
||||
// 0xxxxxxx
|
||||
// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000
|
||||
// 110yyyyx 10xxxxxx
|
||||
// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0
|
||||
// 1110yyyy 10yxxxxx 10xxxxxx
|
||||
// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0
|
||||
// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
|
||||
// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0
|
||||
static const char32_t kFirstByteMark[] = {
|
||||
0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// UTF-32
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return number of UTF-8 bytes required for the character. If the character
|
||||
* is invalid, return size of 0.
|
||||
*/
|
||||
static inline size_t utf32_codepoint_utf8_length(char32_t srcChar)
|
||||
{
|
||||
// Figure out how many bytes the result will require.
|
||||
if (srcChar < 0x00000080) {
|
||||
return 1;
|
||||
} else if (srcChar < 0x00000800) {
|
||||
return 2;
|
||||
} else if (srcChar < 0x00010000) {
|
||||
if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) {
|
||||
return 3;
|
||||
} else {
|
||||
// Surrogates are invalid UTF-32 characters.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// Max code point for Unicode is 0x0010FFFF.
|
||||
else if (srcChar <= kUnicodeMaxCodepoint) {
|
||||
return 4;
|
||||
} else {
|
||||
// Invalid UTF-32 character.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Write out the source character to <dstP>.
|
||||
|
||||
static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes)
|
||||
{
|
||||
dstP += bytes;
|
||||
switch (bytes)
|
||||
{ /* note: everything falls through. */
|
||||
case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
|
||||
case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
|
||||
case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
|
||||
case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]);
|
||||
}
|
||||
}
|
||||
|
||||
size_t strlen32(const char32_t *s)
|
||||
{
|
||||
const char32_t *ss = s;
|
||||
while ( *ss )
|
||||
ss++;
|
||||
return ss-s;
|
||||
}
|
||||
|
||||
size_t strnlen32(const char32_t *s, size_t maxlen)
|
||||
{
|
||||
const char32_t *ss = s;
|
||||
while ((maxlen > 0) && *ss) {
|
||||
ss++;
|
||||
maxlen--;
|
||||
}
|
||||
return ss-s;
|
||||
}
|
||||
|
||||
static inline int32_t utf32_at_internal(const char* cur, size_t *num_read)
|
||||
{
|
||||
const char first_char = *cur;
|
||||
if ((first_char & 0x80) == 0) { // ASCII
|
||||
*num_read = 1;
|
||||
return *cur;
|
||||
}
|
||||
cur++;
|
||||
char32_t mask, to_ignore_mask;
|
||||
size_t num_to_read = 0;
|
||||
char32_t utf32 = first_char;
|
||||
for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80;
|
||||
(first_char & mask);
|
||||
num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
|
||||
// 0x3F == 00111111
|
||||
utf32 = (utf32 << 6) + (*cur++ & 0x3F);
|
||||
}
|
||||
to_ignore_mask |= mask;
|
||||
utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1)));
|
||||
|
||||
*num_read = num_to_read;
|
||||
return static_cast<int32_t>(utf32);
|
||||
}
|
||||
|
||||
int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index)
|
||||
{
|
||||
if (index >= src_len) {
|
||||
return -1;
|
||||
}
|
||||
size_t dummy_index;
|
||||
if (next_index == NULL) {
|
||||
next_index = &dummy_index;
|
||||
}
|
||||
size_t num_read;
|
||||
int32_t ret = utf32_at_internal(src + index, &num_read);
|
||||
if (ret >= 0) {
|
||||
*next_index = index + num_read;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len)
|
||||
{
|
||||
if (src == NULL || src_len == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t ret = 0;
|
||||
const char32_t *end = src + src_len;
|
||||
while (src < end) {
|
||||
ret += utf32_codepoint_utf8_length(*src++);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst)
|
||||
{
|
||||
if (src == NULL || src_len == 0 || dst == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char32_t *cur_utf32 = src;
|
||||
const char32_t *end_utf32 = src + src_len;
|
||||
char *cur = dst;
|
||||
while (cur_utf32 < end_utf32) {
|
||||
size_t len = utf32_codepoint_utf8_length(*cur_utf32);
|
||||
utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len);
|
||||
cur += len;
|
||||
}
|
||||
*cur = '\0';
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// UTF-16
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
int strcmp16(const char16_t *s1, const char16_t *s2)
|
||||
{
|
||||
char16_t ch;
|
||||
int d = 0;
|
||||
|
||||
while ( 1 ) {
|
||||
d = (int)(ch = *s1++) - (int)*s2++;
|
||||
if ( d || !ch )
|
||||
break;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
int strncmp16(const char16_t *s1, const char16_t *s2, size_t n)
|
||||
{
|
||||
char16_t ch;
|
||||
int d = 0;
|
||||
|
||||
while ( n-- ) {
|
||||
d = (int)(ch = *s1++) - (int)*s2++;
|
||||
if ( d || !ch )
|
||||
break;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
char16_t *strcpy16(char16_t *dst, const char16_t *src)
|
||||
{
|
||||
char16_t *q = dst;
|
||||
const char16_t *p = src;
|
||||
char16_t ch;
|
||||
|
||||
do {
|
||||
*q++ = ch = *p++;
|
||||
} while ( ch );
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
size_t strlen16(const char16_t *s)
|
||||
{
|
||||
const char16_t *ss = s;
|
||||
while ( *ss )
|
||||
ss++;
|
||||
return ss-s;
|
||||
}
|
||||
|
||||
|
||||
char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n)
|
||||
{
|
||||
char16_t *q = dst;
|
||||
const char16_t *p = src;
|
||||
char ch;
|
||||
|
||||
while (n) {
|
||||
n--;
|
||||
*q++ = ch = *p++;
|
||||
if ( !ch )
|
||||
break;
|
||||
}
|
||||
|
||||
*q = 0;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
size_t strnlen16(const char16_t *s, size_t maxlen)
|
||||
{
|
||||
const char16_t *ss = s;
|
||||
|
||||
/* Important: the maxlen test must precede the reference through ss;
|
||||
since the byte beyond the maximum may segfault */
|
||||
while ((maxlen > 0) && *ss) {
|
||||
ss++;
|
||||
maxlen--;
|
||||
}
|
||||
return ss-s;
|
||||
}
|
||||
|
||||
int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2)
|
||||
{
|
||||
const char16_t* e1 = s1+n1;
|
||||
const char16_t* e2 = s2+n2;
|
||||
|
||||
while (s1 < e1 && s2 < e2) {
|
||||
const int d = (int)*s1++ - (int)*s2++;
|
||||
if (d) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
return n1 < n2
|
||||
? (0 - (int)*s2)
|
||||
: (n1 > n2
|
||||
? ((int)*s1 - 0)
|
||||
: 0);
|
||||
}
|
||||
|
||||
int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2)
|
||||
{
|
||||
const char16_t* e1 = s1H+n1;
|
||||
const char16_t* e2 = s2N+n2;
|
||||
|
||||
while (s1H < e1 && s2N < e2) {
|
||||
const char16_t c2 = ntohs(*s2N);
|
||||
const int d = (int)*s1H++ - (int)c2;
|
||||
s2N++;
|
||||
if (d) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
return n1 < n2
|
||||
? (0 - (int)ntohs(*s2N))
|
||||
: (n1 > n2
|
||||
? ((int)*s1H - 0)
|
||||
: 0);
|
||||
}
|
||||
|
||||
void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst)
|
||||
{
|
||||
if (src == NULL || src_len == 0 || dst == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char16_t* cur_utf16 = src;
|
||||
const char16_t* const end_utf16 = src + src_len;
|
||||
char *cur = dst;
|
||||
while (cur_utf16 < end_utf16) {
|
||||
char32_t utf32;
|
||||
// surrogate pairs
|
||||
if ((*cur_utf16 & 0xFC00) == 0xD800) {
|
||||
utf32 = (*cur_utf16++ - 0xD800) << 10;
|
||||
utf32 |= *cur_utf16++ - 0xDC00;
|
||||
utf32 += 0x10000;
|
||||
} else {
|
||||
utf32 = (char32_t) *cur_utf16++;
|
||||
}
|
||||
const size_t len = utf32_codepoint_utf8_length(utf32);
|
||||
utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len);
|
||||
cur += len;
|
||||
}
|
||||
*cur = '\0';
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// UTF-8
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ssize_t utf8_length(const char *src)
|
||||
{
|
||||
const char *cur = src;
|
||||
size_t ret = 0;
|
||||
while (*cur != '\0') {
|
||||
const char first_char = *cur++;
|
||||
if ((first_char & 0x80) == 0) { // ASCII
|
||||
ret += 1;
|
||||
continue;
|
||||
}
|
||||
// (UTF-8's character must not be like 10xxxxxx,
|
||||
// but 110xxxxx, 1110xxxx, ... or 1111110x)
|
||||
if ((first_char & 0x40) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t mask, to_ignore_mask;
|
||||
size_t num_to_read = 0;
|
||||
char32_t utf32 = 0;
|
||||
for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
|
||||
num_to_read < 5 && (first_char & mask);
|
||||
num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
|
||||
if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx
|
||||
return -1;
|
||||
}
|
||||
// 0x3F == 00111111
|
||||
utf32 = (utf32 << 6) + (*cur++ & 0x3F);
|
||||
}
|
||||
// "first_char" must be (110xxxxx - 11110xxx)
|
||||
if (num_to_read == 5) {
|
||||
return -1;
|
||||
}
|
||||
to_ignore_mask |= mask;
|
||||
utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
|
||||
if (utf32 > kUnicodeMaxCodepoint) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret += num_to_read;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
|
||||
{
|
||||
if (src == NULL || src_len == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t ret = 0;
|
||||
const char16_t* const end = src + src_len;
|
||||
while (src < end) {
|
||||
if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
|
||||
&& (*++src & 0xFC00) == 0xDC00) {
|
||||
// surrogate pairs are always 4 bytes.
|
||||
ret += 4;
|
||||
src++;
|
||||
} else {
|
||||
ret += utf32_codepoint_utf8_length((char32_t) *src++);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1-4 based on the number of leading bits.
|
||||
*
|
||||
* 1111 -> 4
|
||||
* 1110 -> 3
|
||||
* 110x -> 2
|
||||
* 10xx -> 1
|
||||
* 0xxx -> 1
|
||||
*/
|
||||
static inline size_t utf8_codepoint_len(uint8_t ch)
|
||||
{
|
||||
return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1;
|
||||
}
|
||||
|
||||
static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte)
|
||||
{
|
||||
*codePoint <<= 6;
|
||||
*codePoint |= 0x3F & byte;
|
||||
}
|
||||
|
||||
size_t utf8_to_utf32_length(const char *src, size_t src_len)
|
||||
{
|
||||
if (src == NULL || src_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
size_t ret = 0;
|
||||
const char* cur;
|
||||
const char* end;
|
||||
size_t num_to_skip;
|
||||
for (cur = src, end = src + src_len, num_to_skip = 1;
|
||||
cur < end;
|
||||
cur += num_to_skip, ret++) {
|
||||
const char first_char = *cur;
|
||||
num_to_skip = 1;
|
||||
if ((first_char & 0x80) == 0) { // ASCII
|
||||
continue;
|
||||
}
|
||||
int32_t mask;
|
||||
|
||||
for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) {
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst)
|
||||
{
|
||||
if (src == NULL || src_len == 0 || dst == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char* cur = src;
|
||||
const char* const end = src + src_len;
|
||||
char32_t* cur_utf32 = dst;
|
||||
while (cur < end) {
|
||||
size_t num_read;
|
||||
*cur_utf32++ = static_cast<char32_t>(utf32_at_internal(cur, &num_read));
|
||||
cur += num_read;
|
||||
}
|
||||
*cur_utf32 = 0;
|
||||
}
|
||||
|
||||
static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length)
|
||||
{
|
||||
uint32_t unicode;
|
||||
|
||||
switch (length)
|
||||
{
|
||||
case 1:
|
||||
return src[0];
|
||||
case 2:
|
||||
unicode = src[0] & 0x1f;
|
||||
utf8_shift_and_mask(&unicode, src[1]);
|
||||
return unicode;
|
||||
case 3:
|
||||
unicode = src[0] & 0x0f;
|
||||
utf8_shift_and_mask(&unicode, src[1]);
|
||||
utf8_shift_and_mask(&unicode, src[2]);
|
||||
return unicode;
|
||||
case 4:
|
||||
unicode = src[0] & 0x07;
|
||||
utf8_shift_and_mask(&unicode, src[1]);
|
||||
utf8_shift_and_mask(&unicode, src[2]);
|
||||
utf8_shift_and_mask(&unicode, src[3]);
|
||||
return unicode;
|
||||
default:
|
||||
return 0xffff;
|
||||
}
|
||||
|
||||
//printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result);
|
||||
}
|
||||
|
||||
ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len)
|
||||
{
|
||||
const uint8_t* const u8end = u8str + u8len;
|
||||
const uint8_t* u8cur = u8str;
|
||||
|
||||
/* Validate that the UTF-8 is the correct len */
|
||||
size_t u16measuredLen = 0;
|
||||
while (u8cur < u8end) {
|
||||
u16measuredLen++;
|
||||
int u8charLen = utf8_codepoint_len(*u8cur);
|
||||
uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen);
|
||||
if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16
|
||||
u8cur += u8charLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that we ended where we thought we would and the output UTF-16
|
||||
* will be exactly how long we were told it would be.
|
||||
*/
|
||||
if (u8cur != u8end) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return u16measuredLen;
|
||||
}
|
||||
|
||||
char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* u8str, size_t u8len, char16_t* u16str)
|
||||
{
|
||||
const uint8_t* const u8end = u8str + u8len;
|
||||
const uint8_t* u8cur = u8str;
|
||||
char16_t* u16cur = u16str;
|
||||
|
||||
while (u8cur < u8end) {
|
||||
size_t u8len = utf8_codepoint_len(*u8cur);
|
||||
uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len);
|
||||
|
||||
// Convert the UTF32 codepoint to one or more UTF16 codepoints
|
||||
if (codepoint <= 0xFFFF) {
|
||||
// Single UTF16 character
|
||||
*u16cur++ = (char16_t) codepoint;
|
||||
} else {
|
||||
// Multiple UTF16 characters with surrogates
|
||||
codepoint = codepoint - 0x10000;
|
||||
*u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800);
|
||||
*u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
|
||||
}
|
||||
|
||||
u8cur += u8len;
|
||||
}
|
||||
return u16cur;
|
||||
}
|
||||
|
||||
void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) {
|
||||
char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str);
|
||||
*end = 0;
|
||||
}
|
||||
|
||||
}
|
@ -1,174 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_UNICODE_H
|
||||
#define ANDROID_UNICODE_H
|
||||
|
||||
#define __STDC_LIMIT_MACROS 1
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
// char32_t and char16_t are built-in types as of c++0x.
|
||||
#if !defined(__GXX_EXPERIMENTAL_CXX0X__) && __cplusplus < 201103L
|
||||
typedef uint32_t char32_t;
|
||||
typedef uint16_t char16_t;
|
||||
#endif
|
||||
|
||||
// Standard string functions on char16_t strings.
|
||||
int strcmp16(const char16_t *, const char16_t *);
|
||||
int strncmp16(const char16_t *s1, const char16_t *s2, size_t n);
|
||||
size_t strlen16(const char16_t *);
|
||||
size_t strnlen16(const char16_t *, size_t);
|
||||
char16_t *strcpy16(char16_t *, const char16_t *);
|
||||
char16_t *strncpy16(char16_t *, const char16_t *, size_t);
|
||||
|
||||
// Version of comparison that supports embedded nulls.
|
||||
// This is different than strncmp() because we don't stop
|
||||
// at a nul character and consider the strings to be different
|
||||
// if the lengths are different (thus we need to supply the
|
||||
// lengths of both strings). This can also be used when
|
||||
// your string is not nul-terminated as it will have the
|
||||
// equivalent result as strcmp16 (unlike strncmp16).
|
||||
int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2);
|
||||
|
||||
// Version of strzcmp16 for comparing strings in different endianness.
|
||||
int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2);
|
||||
|
||||
// Standard string functions on char32_t strings.
|
||||
size_t strlen32(const char32_t *);
|
||||
size_t strnlen32(const char32_t *, size_t);
|
||||
|
||||
/**
|
||||
* Measure the length of a UTF-32 string in UTF-8. If the string is invalid
|
||||
* such as containing a surrogate character, -1 will be returned.
|
||||
*/
|
||||
ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len);
|
||||
|
||||
/**
|
||||
* Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not
|
||||
* large enough to store the string, the part of the "src" string is stored
|
||||
* into "dst" as much as possible. See the examples for more detail.
|
||||
* Returns the size actually used for storing the string.
|
||||
* dst" is not null-terminated when dst_len is fully used (like strncpy).
|
||||
*
|
||||
* Example 1
|
||||
* "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
|
||||
* "src_len" == 2
|
||||
* "dst_len" >= 7
|
||||
* ->
|
||||
* Returned value == 6
|
||||
* "dst" becomes \xE3\x81\x82\xE3\x81\x84\0
|
||||
* (note that "dst" is null-terminated)
|
||||
*
|
||||
* Example 2
|
||||
* "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
|
||||
* "src_len" == 2
|
||||
* "dst_len" == 5
|
||||
* ->
|
||||
* Returned value == 3
|
||||
* "dst" becomes \xE3\x81\x82\0
|
||||
* (note that "dst" is null-terminated, but \u3044 is not stored in "dst"
|
||||
* since "dst" does not have enough size to store the character)
|
||||
*
|
||||
* Example 3
|
||||
* "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
|
||||
* "src_len" == 2
|
||||
* "dst_len" == 6
|
||||
* ->
|
||||
* Returned value == 6
|
||||
* "dst" becomes \xE3\x81\x82\xE3\x81\x84
|
||||
* (note that "dst" is NOT null-terminated, like strncpy)
|
||||
*/
|
||||
void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst);
|
||||
|
||||
/**
|
||||
* Returns the unicode value at "index".
|
||||
* Returns -1 when the index is invalid (equals to or more than "src_len").
|
||||
* If returned value is positive, it is able to be converted to char32_t, which
|
||||
* is unsigned. Then, if "next_index" is not NULL, the next index to be used is
|
||||
* stored in "next_index". "next_index" can be NULL.
|
||||
*/
|
||||
int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the UTF-8 length of UTF-16 string "src".
|
||||
*/
|
||||
ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len);
|
||||
|
||||
/**
|
||||
* Converts a UTF-16 string to UTF-8. The destination buffer must be large
|
||||
* enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added
|
||||
* NULL terminator.
|
||||
*/
|
||||
void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst);
|
||||
|
||||
/**
|
||||
* Returns the length of "src" when "src" is valid UTF-8 string.
|
||||
* Returns 0 if src is NULL or 0-length string. Returns -1 when the source
|
||||
* is an invalid string.
|
||||
*
|
||||
* This function should be used to determine whether "src" is valid UTF-8
|
||||
* characters with valid unicode codepoints. "src" must be null-terminated.
|
||||
*
|
||||
* If you are going to use other utf8_to_... functions defined in this header
|
||||
* with string which may not be valid UTF-8 with valid codepoint (form 0 to
|
||||
* 0x10FFFF), you should use this function before calling others, since the
|
||||
* other functions do not check whether the string is valid UTF-8 or not.
|
||||
*
|
||||
* If you do not care whether "src" is valid UTF-8 or not, you should use
|
||||
* strlen() as usual, which should be much faster.
|
||||
*/
|
||||
ssize_t utf8_length(const char *src);
|
||||
|
||||
/**
|
||||
* Measure the length of a UTF-32 string.
|
||||
*/
|
||||
size_t utf8_to_utf32_length(const char *src, size_t src_len);
|
||||
|
||||
/**
|
||||
* Stores a UTF-32 string converted from "src" in "dst". "dst" must be large
|
||||
* enough to store the entire converted string as measured by
|
||||
* utf8_to_utf32_length plus space for a NULL terminator.
|
||||
*/
|
||||
void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst);
|
||||
|
||||
/**
|
||||
* Returns the UTF-16 length of UTF-8 string "src".
|
||||
*/
|
||||
ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen);
|
||||
|
||||
/**
|
||||
* Convert UTF-8 to UTF-16 including surrogate pairs.
|
||||
* Returns a pointer to the end of the string (where a null terminator might go
|
||||
* if you wanted to add one).
|
||||
*/
|
||||
char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* src, size_t srcLen, char16_t* dst);
|
||||
|
||||
/**
|
||||
* Convert UTF-8 to UTF-16 including surrogate pairs. The destination buffer
|
||||
* must be large enough to hold the result as measured by utf8_to_utf16_length
|
||||
* plus an added NULL terminator.
|
||||
*/
|
||||
void utf8_to_utf16(const uint8_t* src, size_t srcLen, char16_t* dst);
|
||||
|
||||
}
|
||||
|
||||
#undef __STDC_LIMIT_MACROS
|
||||
#endif
|
110
widget/gonk/libui/VelocityControl.cpp
Normal file
110
widget/gonk/libui/VelocityControl.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 "VelocityControl"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
// Log debug messages about acceleration.
|
||||
#define DEBUG_ACCELERATION 0
|
||||
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "VelocityControl.h"
|
||||
#include <utils/BitSet.h>
|
||||
#include <utils/Timers.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// --- VelocityControl ---
|
||||
|
||||
const nsecs_t VelocityControl::STOP_TIME;
|
||||
|
||||
VelocityControl::VelocityControl() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void VelocityControl::setParameters(const VelocityControlParameters& parameters) {
|
||||
mParameters = parameters;
|
||||
reset();
|
||||
}
|
||||
|
||||
void VelocityControl::reset() {
|
||||
mLastMovementTime = LLONG_MIN;
|
||||
mRawPosition.x = 0;
|
||||
mRawPosition.y = 0;
|
||||
mVelocityTracker.clear();
|
||||
}
|
||||
|
||||
void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) {
|
||||
if ((deltaX && *deltaX) || (deltaY && *deltaY)) {
|
||||
if (eventTime >= mLastMovementTime + STOP_TIME) {
|
||||
#if DEBUG_ACCELERATION
|
||||
ALOGD("VelocityControl: stopped, last movement was %0.3fms ago",
|
||||
(eventTime - mLastMovementTime) * 0.000001f);
|
||||
#endif
|
||||
reset();
|
||||
}
|
||||
|
||||
mLastMovementTime = eventTime;
|
||||
if (deltaX) {
|
||||
mRawPosition.x += *deltaX;
|
||||
}
|
||||
if (deltaY) {
|
||||
mRawPosition.y += *deltaY;
|
||||
}
|
||||
mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition);
|
||||
|
||||
float vx, vy;
|
||||
float scale = mParameters.scale;
|
||||
if (mVelocityTracker.getVelocity(0, &vx, &vy)) {
|
||||
float speed = hypotf(vx, vy) * scale;
|
||||
if (speed >= mParameters.highThreshold) {
|
||||
// Apply full acceleration above the high speed threshold.
|
||||
scale *= mParameters.acceleration;
|
||||
} else if (speed > mParameters.lowThreshold) {
|
||||
// Linearly interpolate the acceleration to apply between the low and high
|
||||
// speed thresholds.
|
||||
scale *= 1 + (speed - mParameters.lowThreshold)
|
||||
/ (mParameters.highThreshold - mParameters.lowThreshold)
|
||||
* (mParameters.acceleration - 1);
|
||||
}
|
||||
|
||||
#if DEBUG_ACCELERATION
|
||||
ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): "
|
||||
"vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
|
||||
mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
|
||||
mParameters.acceleration,
|
||||
vx, vy, speed, scale / mParameters.scale);
|
||||
#endif
|
||||
} else {
|
||||
#if DEBUG_ACCELERATION
|
||||
ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity",
|
||||
mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
|
||||
mParameters.acceleration);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (deltaX) {
|
||||
*deltaX *= scale;
|
||||
}
|
||||
if (deltaY) {
|
||||
*deltaY *= scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
107
widget/gonk/libui/VelocityControl.h
Normal file
107
widget/gonk/libui/VelocityControl.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef _ANDROIDFW_VELOCITY_CONTROL_H
|
||||
#define _ANDROIDFW_VELOCITY_CONTROL_H
|
||||
|
||||
#include "Input.h"
|
||||
#include "VelocityTracker.h"
|
||||
#include <utils/Timers.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Specifies parameters that govern pointer or wheel acceleration.
|
||||
*/
|
||||
struct VelocityControlParameters {
|
||||
// A scale factor that is multiplied with the raw velocity deltas
|
||||
// prior to applying any other velocity control factors. The scale
|
||||
// factor should be used to adapt the input device resolution
|
||||
// (eg. counts per inch) to the output device resolution (eg. pixels per inch).
|
||||
//
|
||||
// Must be a positive value.
|
||||
// Default is 1.0 (no scaling).
|
||||
float scale;
|
||||
|
||||
// The scaled speed at which acceleration begins to be applied.
|
||||
// This value establishes the upper bound of a low speed regime for
|
||||
// small precise motions that are performed without any acceleration.
|
||||
//
|
||||
// Must be a non-negative value.
|
||||
// Default is 0.0 (no low threshold).
|
||||
float lowThreshold;
|
||||
|
||||
// The scaled speed at which maximum acceleration is applied.
|
||||
// The difference between highThreshold and lowThreshold controls
|
||||
// the range of speeds over which the acceleration factor is interpolated.
|
||||
// The wider the range, the smoother the acceleration.
|
||||
//
|
||||
// Must be a non-negative value greater than or equal to lowThreshold.
|
||||
// Default is 0.0 (no high threshold).
|
||||
float highThreshold;
|
||||
|
||||
// The acceleration factor.
|
||||
// When the speed is above the low speed threshold, the velocity will scaled
|
||||
// by an interpolated value between 1.0 and this amount.
|
||||
//
|
||||
// Must be a positive greater than or equal to 1.0.
|
||||
// Default is 1.0 (no acceleration).
|
||||
float acceleration;
|
||||
|
||||
VelocityControlParameters() :
|
||||
scale(1.0f), lowThreshold(0.0f), highThreshold(0.0f), acceleration(1.0f) {
|
||||
}
|
||||
|
||||
VelocityControlParameters(float scale, float lowThreshold,
|
||||
float highThreshold, float acceleration) :
|
||||
scale(scale), lowThreshold(lowThreshold),
|
||||
highThreshold(highThreshold), acceleration(acceleration) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Implements mouse pointer and wheel speed control and acceleration.
|
||||
*/
|
||||
class VelocityControl {
|
||||
public:
|
||||
VelocityControl();
|
||||
|
||||
/* Sets the various parameters. */
|
||||
void setParameters(const VelocityControlParameters& parameters);
|
||||
|
||||
/* Resets the current movement counters to zero.
|
||||
* This has the effect of nullifying any acceleration. */
|
||||
void reset();
|
||||
|
||||
/* Translates a raw movement delta into an appropriately
|
||||
* scaled / accelerated delta based on the current velocity. */
|
||||
void move(nsecs_t eventTime, float* deltaX, float* deltaY);
|
||||
|
||||
private:
|
||||
// If no movements are received within this amount of time,
|
||||
// we assume the movement has stopped and reset the movement counters.
|
||||
static const nsecs_t STOP_TIME = 500 * 1000000; // 500 ms
|
||||
|
||||
VelocityControlParameters mParameters;
|
||||
|
||||
nsecs_t mLastMovementTime;
|
||||
VelocityTracker::Position mRawPosition;
|
||||
VelocityTracker mVelocityTracker;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_VELOCITY_CONTROL_H
|
929
widget/gonk/libui/VelocityTracker.cpp
Normal file
929
widget/gonk/libui/VelocityTracker.cpp
Normal file
@ -0,0 +1,929 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 "VelocityTracker"
|
||||
//#define LOG_NDEBUG 0
|
||||
#include "cutils_log.h"
|
||||
|
||||
// Log debug messages about velocity tracking.
|
||||
#define DEBUG_VELOCITY 0
|
||||
|
||||
// Log debug messages about the progress of the algorithm itself.
|
||||
#define DEBUG_STRATEGY 0
|
||||
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "VelocityTracker.h"
|
||||
#include <utils/BitSet.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Timers.h>
|
||||
|
||||
#include <cutils/properties.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// Nanoseconds per milliseconds.
|
||||
static const nsecs_t NANOS_PER_MS = 1000000;
|
||||
|
||||
// Threshold for determining that a pointer has stopped moving.
|
||||
// Some input devices do not send ACTION_MOVE events in the case where a pointer has
|
||||
// stopped. We need to detect this case so that we can accurately predict the
|
||||
// velocity after the pointer starts moving again.
|
||||
static const nsecs_t ASSUME_POINTER_STOPPED_TIME = 40 * NANOS_PER_MS;
|
||||
|
||||
|
||||
static float vectorDot(const float* a, const float* b, uint32_t m) {
|
||||
float r = 0;
|
||||
while (m--) {
|
||||
r += *(a++) * *(b++);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static float vectorNorm(const float* a, uint32_t m) {
|
||||
float r = 0;
|
||||
while (m--) {
|
||||
float t = *(a++);
|
||||
r += t * t;
|
||||
}
|
||||
return sqrtf(r);
|
||||
}
|
||||
|
||||
#if DEBUG_STRATEGY || DEBUG_VELOCITY
|
||||
static String8 vectorToString(const float* a, uint32_t m) {
|
||||
String8 str;
|
||||
str.append("[");
|
||||
while (m--) {
|
||||
str.appendFormat(" %f", *(a++));
|
||||
if (m) {
|
||||
str.append(",");
|
||||
}
|
||||
}
|
||||
str.append(" ]");
|
||||
return str;
|
||||
}
|
||||
|
||||
static String8 matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
|
||||
String8 str;
|
||||
str.append("[");
|
||||
for (size_t i = 0; i < m; i++) {
|
||||
if (i) {
|
||||
str.append(",");
|
||||
}
|
||||
str.append(" [");
|
||||
for (size_t j = 0; j < n; j++) {
|
||||
if (j) {
|
||||
str.append(",");
|
||||
}
|
||||
str.appendFormat(" %f", a[rowMajor ? i * n + j : j * m + i]);
|
||||
}
|
||||
str.append(" ]");
|
||||
}
|
||||
str.append(" ]");
|
||||
return str;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// --- VelocityTracker ---
|
||||
|
||||
// The default velocity tracker strategy.
|
||||
// Although other strategies are available for testing and comparison purposes,
|
||||
// this is the strategy that applications will actually use. Be very careful
|
||||
// when adjusting the default strategy because it can dramatically affect
|
||||
// (often in a bad way) the user experience.
|
||||
const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
|
||||
|
||||
VelocityTracker::VelocityTracker(const char* strategy) :
|
||||
mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
|
||||
// Allow the default strategy to be overridden using a system property for debugging.
|
||||
if (!strategy) {
|
||||
int length = property_get("debug.velocitytracker.strategy", value, NULL);
|
||||
if (length > 0) {
|
||||
strategy = value;
|
||||
} else {
|
||||
strategy = DEFAULT_STRATEGY;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the strategy.
|
||||
if (!configureStrategy(strategy)) {
|
||||
ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy);
|
||||
if (!configureStrategy(DEFAULT_STRATEGY)) {
|
||||
LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!",
|
||||
strategy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VelocityTracker::~VelocityTracker() {
|
||||
delete mStrategy;
|
||||
}
|
||||
|
||||
bool VelocityTracker::configureStrategy(const char* strategy) {
|
||||
mStrategy = createStrategy(strategy);
|
||||
return mStrategy != NULL;
|
||||
}
|
||||
|
||||
VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
|
||||
if (!strcmp("lsq1", strategy)) {
|
||||
// 1st order least squares. Quality: POOR.
|
||||
// Frequently underfits the touch data especially when the finger accelerates
|
||||
// or changes direction. Often underestimates velocity. The direction
|
||||
// is overly influenced by historical touch points.
|
||||
return new LeastSquaresVelocityTrackerStrategy(1);
|
||||
}
|
||||
if (!strcmp("lsq2", strategy)) {
|
||||
// 2nd order least squares. Quality: VERY GOOD.
|
||||
// Pretty much ideal, but can be confused by certain kinds of touch data,
|
||||
// particularly if the panel has a tendency to generate delayed,
|
||||
// duplicate or jittery touch coordinates when the finger is released.
|
||||
return new LeastSquaresVelocityTrackerStrategy(2);
|
||||
}
|
||||
if (!strcmp("lsq3", strategy)) {
|
||||
// 3rd order least squares. Quality: UNUSABLE.
|
||||
// Frequently overfits the touch data yielding wildly divergent estimates
|
||||
// of the velocity when the finger is released.
|
||||
return new LeastSquaresVelocityTrackerStrategy(3);
|
||||
}
|
||||
if (!strcmp("wlsq2-delta", strategy)) {
|
||||
// 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL
|
||||
return new LeastSquaresVelocityTrackerStrategy(2,
|
||||
LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA);
|
||||
}
|
||||
if (!strcmp("wlsq2-central", strategy)) {
|
||||
// 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL
|
||||
return new LeastSquaresVelocityTrackerStrategy(2,
|
||||
LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL);
|
||||
}
|
||||
if (!strcmp("wlsq2-recent", strategy)) {
|
||||
// 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL
|
||||
return new LeastSquaresVelocityTrackerStrategy(2,
|
||||
LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT);
|
||||
}
|
||||
if (!strcmp("int1", strategy)) {
|
||||
// 1st order integrating filter. Quality: GOOD.
|
||||
// Not as good as 'lsq2' because it cannot estimate acceleration but it is
|
||||
// more tolerant of errors. Like 'lsq1', this strategy tends to underestimate
|
||||
// the velocity of a fling but this strategy tends to respond to changes in
|
||||
// direction more quickly and accurately.
|
||||
return new IntegratingVelocityTrackerStrategy(1);
|
||||
}
|
||||
if (!strcmp("int2", strategy)) {
|
||||
// 2nd order integrating filter. Quality: EXPERIMENTAL.
|
||||
// For comparison purposes only. Unlike 'int1' this strategy can compensate
|
||||
// for acceleration but it typically overestimates the effect.
|
||||
return new IntegratingVelocityTrackerStrategy(2);
|
||||
}
|
||||
if (!strcmp("legacy", strategy)) {
|
||||
// Legacy velocity tracker algorithm. Quality: POOR.
|
||||
// For comparison purposes only. This algorithm is strongly influenced by
|
||||
// old data points, consistently underestimates velocity and takes a very long
|
||||
// time to adjust to changes in direction.
|
||||
return new LegacyVelocityTrackerStrategy();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void VelocityTracker::clear() {
|
||||
mCurrentPointerIdBits.clear();
|
||||
mActivePointerId = -1;
|
||||
|
||||
mStrategy->clear();
|
||||
}
|
||||
|
||||
void VelocityTracker::clearPointers(BitSet32 idBits) {
|
||||
BitSet32 remainingIdBits(mCurrentPointerIdBits.value & ~idBits.value);
|
||||
mCurrentPointerIdBits = remainingIdBits;
|
||||
|
||||
if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) {
|
||||
mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1;
|
||||
}
|
||||
|
||||
mStrategy->clearPointers(idBits);
|
||||
}
|
||||
|
||||
void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
|
||||
while (idBits.count() > MAX_POINTERS) {
|
||||
idBits.clearLastMarkedBit();
|
||||
}
|
||||
|
||||
if ((mCurrentPointerIdBits.value & idBits.value)
|
||||
&& eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) {
|
||||
#if DEBUG_VELOCITY
|
||||
ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
|
||||
(eventTime - mLastEventTime) * 0.000001f);
|
||||
#endif
|
||||
// We have not received any movements for too long. Assume that all pointers
|
||||
// have stopped.
|
||||
mStrategy->clear();
|
||||
}
|
||||
mLastEventTime = eventTime;
|
||||
|
||||
mCurrentPointerIdBits = idBits;
|
||||
if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) {
|
||||
mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit();
|
||||
}
|
||||
|
||||
mStrategy->addMovement(eventTime, idBits, positions);
|
||||
|
||||
#if DEBUG_VELOCITY
|
||||
ALOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d",
|
||||
eventTime, idBits.value, mActivePointerId);
|
||||
for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
|
||||
uint32_t id = iterBits.firstMarkedBit();
|
||||
uint32_t index = idBits.getIndexOfBit(id);
|
||||
iterBits.clearBit(id);
|
||||
Estimator estimator;
|
||||
getEstimator(id, &estimator);
|
||||
ALOGD(" %d: position (%0.3f, %0.3f), "
|
||||
"estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
|
||||
id, positions[index].x, positions[index].y,
|
||||
int(estimator.degree),
|
||||
vectorToString(estimator.xCoeff, estimator.degree + 1).string(),
|
||||
vectorToString(estimator.yCoeff, estimator.degree + 1).string(),
|
||||
estimator.confidence);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VelocityTracker::addMovement(const MotionEvent* event) {
|
||||
int32_t actionMasked = event->getActionMasked();
|
||||
|
||||
switch (actionMasked) {
|
||||
case AMOTION_EVENT_ACTION_DOWN:
|
||||
case AMOTION_EVENT_ACTION_HOVER_ENTER:
|
||||
// Clear all pointers on down before adding the new movement.
|
||||
clear();
|
||||
break;
|
||||
case AMOTION_EVENT_ACTION_POINTER_DOWN: {
|
||||
// Start a new movement trace for a pointer that just went down.
|
||||
// We do this on down instead of on up because the client may want to query the
|
||||
// final velocity for a pointer that just went up.
|
||||
BitSet32 downIdBits;
|
||||
downIdBits.markBit(event->getPointerId(event->getActionIndex()));
|
||||
clearPointers(downIdBits);
|
||||
break;
|
||||
}
|
||||
case AMOTION_EVENT_ACTION_MOVE:
|
||||
case AMOTION_EVENT_ACTION_HOVER_MOVE:
|
||||
break;
|
||||
default:
|
||||
// Ignore all other actions because they do not convey any new information about
|
||||
// pointer movement. We also want to preserve the last known velocity of the pointers.
|
||||
// Note that ACTION_UP and ACTION_POINTER_UP always report the last known position
|
||||
// of the pointers that went up. ACTION_POINTER_UP does include the new position of
|
||||
// pointers that remained down but we will also receive an ACTION_MOVE with this
|
||||
// information if any of them actually moved. Since we don't know how many pointers
|
||||
// will be going up at once it makes sense to just wait for the following ACTION_MOVE
|
||||
// before adding the movement.
|
||||
return;
|
||||
}
|
||||
|
||||
size_t pointerCount = event->getPointerCount();
|
||||
if (pointerCount > MAX_POINTERS) {
|
||||
pointerCount = MAX_POINTERS;
|
||||
}
|
||||
|
||||
BitSet32 idBits;
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
idBits.markBit(event->getPointerId(i));
|
||||
}
|
||||
|
||||
uint32_t pointerIndex[MAX_POINTERS];
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i));
|
||||
}
|
||||
|
||||
nsecs_t eventTime;
|
||||
Position positions[pointerCount];
|
||||
|
||||
size_t historySize = event->getHistorySize();
|
||||
for (size_t h = 0; h < historySize; h++) {
|
||||
eventTime = event->getHistoricalEventTime(h);
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
uint32_t index = pointerIndex[i];
|
||||
positions[index].x = event->getHistoricalX(i, h);
|
||||
positions[index].y = event->getHistoricalY(i, h);
|
||||
}
|
||||
addMovement(eventTime, idBits, positions);
|
||||
}
|
||||
|
||||
eventTime = event->getEventTime();
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
uint32_t index = pointerIndex[i];
|
||||
positions[index].x = event->getX(i);
|
||||
positions[index].y = event->getY(i);
|
||||
}
|
||||
addMovement(eventTime, idBits, positions);
|
||||
}
|
||||
|
||||
bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
|
||||
Estimator estimator;
|
||||
if (getEstimator(id, &estimator) && estimator.degree >= 1) {
|
||||
*outVx = estimator.xCoeff[1];
|
||||
*outVy = estimator.yCoeff[1];
|
||||
return true;
|
||||
}
|
||||
*outVx = 0;
|
||||
*outVy = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const {
|
||||
return mStrategy->getEstimator(id, outEstimator);
|
||||
}
|
||||
|
||||
|
||||
// --- LeastSquaresVelocityTrackerStrategy ---
|
||||
|
||||
const nsecs_t LeastSquaresVelocityTrackerStrategy::HORIZON;
|
||||
const uint32_t LeastSquaresVelocityTrackerStrategy::HISTORY_SIZE;
|
||||
|
||||
LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(
|
||||
uint32_t degree, Weighting weighting) :
|
||||
mDegree(degree), mWeighting(weighting) {
|
||||
clear();
|
||||
}
|
||||
|
||||
LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {
|
||||
}
|
||||
|
||||
void LeastSquaresVelocityTrackerStrategy::clear() {
|
||||
mIndex = 0;
|
||||
mMovements[0].idBits.clear();
|
||||
}
|
||||
|
||||
void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
|
||||
BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
|
||||
mMovements[mIndex].idBits = remainingIdBits;
|
||||
}
|
||||
|
||||
void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions) {
|
||||
if (++mIndex == HISTORY_SIZE) {
|
||||
mIndex = 0;
|
||||
}
|
||||
|
||||
Movement& movement = mMovements[mIndex];
|
||||
movement.eventTime = eventTime;
|
||||
movement.idBits = idBits;
|
||||
uint32_t count = idBits.count();
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
movement.positions[i] = positions[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Solves a linear least squares problem to obtain a N degree polynomial that fits
|
||||
* the specified input data as nearly as possible.
|
||||
*
|
||||
* Returns true if a solution is found, false otherwise.
|
||||
*
|
||||
* The input consists of two vectors of data points X and Y with indices 0..m-1
|
||||
* along with a weight vector W of the same size.
|
||||
*
|
||||
* The output is a vector B with indices 0..n that describes a polynomial
|
||||
* that fits the data, such the sum of W[i] * W[i] * abs(Y[i] - (B[0] + B[1] X[i]
|
||||
* + B[2] X[i]^2 ... B[n] X[i]^n)) for all i between 0 and m-1 is minimized.
|
||||
*
|
||||
* Accordingly, the weight vector W should be initialized by the caller with the
|
||||
* reciprocal square root of the variance of the error in each input data point.
|
||||
* In other words, an ideal choice for W would be W[i] = 1 / var(Y[i]) = 1 / stddev(Y[i]).
|
||||
* The weights express the relative importance of each data point. If the weights are
|
||||
* all 1, then the data points are considered to be of equal importance when fitting
|
||||
* the polynomial. It is a good idea to choose weights that diminish the importance
|
||||
* of data points that may have higher than usual error margins.
|
||||
*
|
||||
* Errors among data points are assumed to be independent. W is represented here
|
||||
* as a vector although in the literature it is typically taken to be a diagonal matrix.
|
||||
*
|
||||
* That is to say, the function that generated the input data can be approximated
|
||||
* by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n.
|
||||
*
|
||||
* The coefficient of determination (R^2) is also returned to describe the goodness
|
||||
* of fit of the model for the given data. It is a value between 0 and 1, where 1
|
||||
* indicates perfect correspondence.
|
||||
*
|
||||
* This function first expands the X vector to a m by n matrix A such that
|
||||
* A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n, then
|
||||
* multiplies it by w[i]./
|
||||
*
|
||||
* Then it calculates the QR decomposition of A yielding an m by m orthonormal matrix Q
|
||||
* and an m by n upper triangular matrix R. Because R is upper triangular (lower
|
||||
* part is all zeroes), we can simplify the decomposition into an m by n matrix
|
||||
* Q1 and a n by n matrix R1 such that A = Q1 R1.
|
||||
*
|
||||
* Finally we solve the system of linear equations given by R1 B = (Qtranspose W Y)
|
||||
* to find B.
|
||||
*
|
||||
* For efficiency, we lay out A and Q column-wise in memory because we frequently
|
||||
* operate on the column vectors. Conversely, we lay out R row-wise.
|
||||
*
|
||||
* http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
|
||||
* http://en.wikipedia.org/wiki/Gram-Schmidt
|
||||
*/
|
||||
static bool solveLeastSquares(const float* x, const float* y,
|
||||
const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) {
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
|
||||
vectorToString(x, m).string(), vectorToString(y, m).string(),
|
||||
vectorToString(w, m).string());
|
||||
#endif
|
||||
|
||||
// Expand the X vector to a matrix A, pre-multiplied by the weights.
|
||||
float a[n][m]; // column-major order
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
a[0][h] = w[h];
|
||||
for (uint32_t i = 1; i < n; i++) {
|
||||
a[i][h] = a[i - 1][h] * x[h];
|
||||
}
|
||||
}
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).string());
|
||||
#endif
|
||||
|
||||
// Apply the Gram-Schmidt process to A to obtain its QR decomposition.
|
||||
float q[n][m]; // orthonormal basis, column-major order
|
||||
float r[n][n]; // upper triangular matrix, row-major order
|
||||
for (uint32_t j = 0; j < n; j++) {
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
q[j][h] = a[j][h];
|
||||
}
|
||||
for (uint32_t i = 0; i < j; i++) {
|
||||
float dot = vectorDot(&q[j][0], &q[i][0], m);
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
q[j][h] -= dot * q[i][h];
|
||||
}
|
||||
}
|
||||
|
||||
float norm = vectorNorm(&q[j][0], m);
|
||||
if (norm < 0.000001f) {
|
||||
// vectors are linearly dependent or zero so no solution
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD(" - no solution, norm=%f", norm);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
float invNorm = 1.0f / norm;
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
q[j][h] *= invNorm;
|
||||
}
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m);
|
||||
}
|
||||
}
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).string());
|
||||
ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).string());
|
||||
|
||||
// calculate QR, if we factored A correctly then QR should equal A
|
||||
float qr[n][m];
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
qr[i][h] = 0;
|
||||
for (uint32_t j = 0; j < n; j++) {
|
||||
qr[i][h] += q[j][h] * r[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).string());
|
||||
#endif
|
||||
|
||||
// Solve R B = Qt W Y to find B. This is easy because R is upper triangular.
|
||||
// We just work from bottom-right to top-left calculating B's coefficients.
|
||||
float wy[m];
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
wy[h] = y[h] * w[h];
|
||||
}
|
||||
for (uint32_t i = n; i-- != 0; ) {
|
||||
outB[i] = vectorDot(&q[i][0], wy, m);
|
||||
for (uint32_t j = n - 1; j > i; j--) {
|
||||
outB[i] -= r[i][j] * outB[j];
|
||||
}
|
||||
outB[i] /= r[i][i];
|
||||
}
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD(" - b=%s", vectorToString(outB, n).string());
|
||||
#endif
|
||||
|
||||
// Calculate the coefficient of determination as 1 - (SSerr / SStot) where
|
||||
// SSerr is the residual sum of squares (variance of the error),
|
||||
// and SStot is the total sum of squares (variance of the data) where each
|
||||
// has been weighted.
|
||||
float ymean = 0;
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
ymean += y[h];
|
||||
}
|
||||
ymean /= m;
|
||||
|
||||
float sserr = 0;
|
||||
float sstot = 0;
|
||||
for (uint32_t h = 0; h < m; h++) {
|
||||
float err = y[h] - outB[0];
|
||||
float term = 1;
|
||||
for (uint32_t i = 1; i < n; i++) {
|
||||
term *= x[h];
|
||||
err -= term * outB[i];
|
||||
}
|
||||
sserr += w[h] * w[h] * err * err;
|
||||
float var = y[h] - ymean;
|
||||
sstot += w[h] * w[h] * var * var;
|
||||
}
|
||||
*outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD(" - sserr=%f", sserr);
|
||||
ALOGD(" - sstot=%f", sstot);
|
||||
ALOGD(" - det=%f", *outDet);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id,
|
||||
VelocityTracker::Estimator* outEstimator) const {
|
||||
outEstimator->clear();
|
||||
|
||||
// Iterate over movement samples in reverse time order and collect samples.
|
||||
float x[HISTORY_SIZE];
|
||||
float y[HISTORY_SIZE];
|
||||
float w[HISTORY_SIZE];
|
||||
float time[HISTORY_SIZE];
|
||||
uint32_t m = 0;
|
||||
uint32_t index = mIndex;
|
||||
const Movement& newestMovement = mMovements[mIndex];
|
||||
do {
|
||||
const Movement& movement = mMovements[index];
|
||||
if (!movement.idBits.hasBit(id)) {
|
||||
break;
|
||||
}
|
||||
|
||||
nsecs_t age = newestMovement.eventTime - movement.eventTime;
|
||||
if (age > HORIZON) {
|
||||
break;
|
||||
}
|
||||
|
||||
const VelocityTracker::Position& position = movement.getPosition(id);
|
||||
x[m] = position.x;
|
||||
y[m] = position.y;
|
||||
w[m] = chooseWeight(index);
|
||||
time[m] = -age * 0.000000001f;
|
||||
index = (index == 0 ? HISTORY_SIZE : index) - 1;
|
||||
} while (++m < HISTORY_SIZE);
|
||||
|
||||
if (m == 0) {
|
||||
return false; // no data
|
||||
}
|
||||
|
||||
// Calculate a least squares polynomial fit.
|
||||
uint32_t degree = mDegree;
|
||||
if (degree > m - 1) {
|
||||
degree = m - 1;
|
||||
}
|
||||
if (degree >= 1) {
|
||||
float xdet, ydet;
|
||||
uint32_t n = degree + 1;
|
||||
if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet)
|
||||
&& solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) {
|
||||
outEstimator->time = newestMovement.eventTime;
|
||||
outEstimator->degree = degree;
|
||||
outEstimator->confidence = xdet * ydet;
|
||||
#if DEBUG_STRATEGY
|
||||
ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
|
||||
int(outEstimator->degree),
|
||||
vectorToString(outEstimator->xCoeff, n).string(),
|
||||
vectorToString(outEstimator->yCoeff, n).string(),
|
||||
outEstimator->confidence);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No velocity data available for this pointer, but we do have its current position.
|
||||
outEstimator->xCoeff[0] = x[0];
|
||||
outEstimator->yCoeff[0] = y[0];
|
||||
outEstimator->time = newestMovement.eventTime;
|
||||
outEstimator->degree = 0;
|
||||
outEstimator->confidence = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const {
|
||||
switch (mWeighting) {
|
||||
case WEIGHTING_DELTA: {
|
||||
// Weight points based on how much time elapsed between them and the next
|
||||
// point so that points that "cover" a shorter time span are weighed less.
|
||||
// delta 0ms: 0.5
|
||||
// delta 10ms: 1.0
|
||||
if (index == mIndex) {
|
||||
return 1.0f;
|
||||
}
|
||||
uint32_t nextIndex = (index + 1) % HISTORY_SIZE;
|
||||
float deltaMillis = (mMovements[nextIndex].eventTime- mMovements[index].eventTime)
|
||||
* 0.000001f;
|
||||
if (deltaMillis < 0) {
|
||||
return 0.5f;
|
||||
}
|
||||
if (deltaMillis < 10) {
|
||||
return 0.5f + deltaMillis * 0.05;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
case WEIGHTING_CENTRAL: {
|
||||
// Weight points based on their age, weighing very recent and very old points less.
|
||||
// age 0ms: 0.5
|
||||
// age 10ms: 1.0
|
||||
// age 50ms: 1.0
|
||||
// age 60ms: 0.5
|
||||
float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime)
|
||||
* 0.000001f;
|
||||
if (ageMillis < 0) {
|
||||
return 0.5f;
|
||||
}
|
||||
if (ageMillis < 10) {
|
||||
return 0.5f + ageMillis * 0.05;
|
||||
}
|
||||
if (ageMillis < 50) {
|
||||
return 1.0f;
|
||||
}
|
||||
if (ageMillis < 60) {
|
||||
return 0.5f + (60 - ageMillis) * 0.05;
|
||||
}
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
case WEIGHTING_RECENT: {
|
||||
// Weight points based on their age, weighing older points less.
|
||||
// age 0ms: 1.0
|
||||
// age 50ms: 1.0
|
||||
// age 100ms: 0.5
|
||||
float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime)
|
||||
* 0.000001f;
|
||||
if (ageMillis < 50) {
|
||||
return 1.0f;
|
||||
}
|
||||
if (ageMillis < 100) {
|
||||
return 0.5f + (100 - ageMillis) * 0.01f;
|
||||
}
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
case WEIGHTING_NONE:
|
||||
default:
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- IntegratingVelocityTrackerStrategy ---
|
||||
|
||||
IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t degree) :
|
||||
mDegree(degree) {
|
||||
}
|
||||
|
||||
IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() {
|
||||
}
|
||||
|
||||
void IntegratingVelocityTrackerStrategy::clear() {
|
||||
mPointerIdBits.clear();
|
||||
}
|
||||
|
||||
void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
|
||||
mPointerIdBits.value &= ~idBits.value;
|
||||
}
|
||||
|
||||
void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions) {
|
||||
uint32_t index = 0;
|
||||
for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) {
|
||||
uint32_t id = iterIdBits.clearFirstMarkedBit();
|
||||
State& state = mPointerState[id];
|
||||
const VelocityTracker::Position& position = positions[index++];
|
||||
if (mPointerIdBits.hasBit(id)) {
|
||||
updateState(state, eventTime, position.x, position.y);
|
||||
} else {
|
||||
initState(state, eventTime, position.x, position.y);
|
||||
}
|
||||
}
|
||||
|
||||
mPointerIdBits = idBits;
|
||||
}
|
||||
|
||||
bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id,
|
||||
VelocityTracker::Estimator* outEstimator) const {
|
||||
outEstimator->clear();
|
||||
|
||||
if (mPointerIdBits.hasBit(id)) {
|
||||
const State& state = mPointerState[id];
|
||||
populateEstimator(state, outEstimator);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void IntegratingVelocityTrackerStrategy::initState(State& state,
|
||||
nsecs_t eventTime, float xpos, float ypos) const {
|
||||
state.updateTime = eventTime;
|
||||
state.degree = 0;
|
||||
|
||||
state.xpos = xpos;
|
||||
state.xvel = 0;
|
||||
state.xaccel = 0;
|
||||
state.ypos = ypos;
|
||||
state.yvel = 0;
|
||||
state.yaccel = 0;
|
||||
}
|
||||
|
||||
void IntegratingVelocityTrackerStrategy::updateState(State& state,
|
||||
nsecs_t eventTime, float xpos, float ypos) const {
|
||||
const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS;
|
||||
const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds
|
||||
|
||||
if (eventTime <= state.updateTime + MIN_TIME_DELTA) {
|
||||
return;
|
||||
}
|
||||
|
||||
float dt = (eventTime - state.updateTime) * 0.000000001f;
|
||||
state.updateTime = eventTime;
|
||||
|
||||
float xvel = (xpos - state.xpos) / dt;
|
||||
float yvel = (ypos - state.ypos) / dt;
|
||||
if (state.degree == 0) {
|
||||
state.xvel = xvel;
|
||||
state.yvel = yvel;
|
||||
state.degree = 1;
|
||||
} else {
|
||||
float alpha = dt / (FILTER_TIME_CONSTANT + dt);
|
||||
if (mDegree == 1) {
|
||||
state.xvel += (xvel - state.xvel) * alpha;
|
||||
state.yvel += (yvel - state.yvel) * alpha;
|
||||
} else {
|
||||
float xaccel = (xvel - state.xvel) / dt;
|
||||
float yaccel = (yvel - state.yvel) / dt;
|
||||
if (state.degree == 1) {
|
||||
state.xaccel = xaccel;
|
||||
state.yaccel = yaccel;
|
||||
state.degree = 2;
|
||||
} else {
|
||||
state.xaccel += (xaccel - state.xaccel) * alpha;
|
||||
state.yaccel += (yaccel - state.yaccel) * alpha;
|
||||
}
|
||||
state.xvel += (state.xaccel * dt) * alpha;
|
||||
state.yvel += (state.yaccel * dt) * alpha;
|
||||
}
|
||||
}
|
||||
state.xpos = xpos;
|
||||
state.ypos = ypos;
|
||||
}
|
||||
|
||||
void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state,
|
||||
VelocityTracker::Estimator* outEstimator) const {
|
||||
outEstimator->time = state.updateTime;
|
||||
outEstimator->confidence = 1.0f;
|
||||
outEstimator->degree = state.degree;
|
||||
outEstimator->xCoeff[0] = state.xpos;
|
||||
outEstimator->xCoeff[1] = state.xvel;
|
||||
outEstimator->xCoeff[2] = state.xaccel / 2;
|
||||
outEstimator->yCoeff[0] = state.ypos;
|
||||
outEstimator->yCoeff[1] = state.yvel;
|
||||
outEstimator->yCoeff[2] = state.yaccel / 2;
|
||||
}
|
||||
|
||||
|
||||
// --- LegacyVelocityTrackerStrategy ---
|
||||
|
||||
const nsecs_t LegacyVelocityTrackerStrategy::HORIZON;
|
||||
const uint32_t LegacyVelocityTrackerStrategy::HISTORY_SIZE;
|
||||
const nsecs_t LegacyVelocityTrackerStrategy::MIN_DURATION;
|
||||
|
||||
LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {
|
||||
clear();
|
||||
}
|
||||
|
||||
LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() {
|
||||
}
|
||||
|
||||
void LegacyVelocityTrackerStrategy::clear() {
|
||||
mIndex = 0;
|
||||
mMovements[0].idBits.clear();
|
||||
}
|
||||
|
||||
void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
|
||||
BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
|
||||
mMovements[mIndex].idBits = remainingIdBits;
|
||||
}
|
||||
|
||||
void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions) {
|
||||
if (++mIndex == HISTORY_SIZE) {
|
||||
mIndex = 0;
|
||||
}
|
||||
|
||||
Movement& movement = mMovements[mIndex];
|
||||
movement.eventTime = eventTime;
|
||||
movement.idBits = idBits;
|
||||
uint32_t count = idBits.count();
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
movement.positions[i] = positions[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id,
|
||||
VelocityTracker::Estimator* outEstimator) const {
|
||||
outEstimator->clear();
|
||||
|
||||
const Movement& newestMovement = mMovements[mIndex];
|
||||
if (!newestMovement.idBits.hasBit(id)) {
|
||||
return false; // no data
|
||||
}
|
||||
|
||||
// Find the oldest sample that contains the pointer and that is not older than HORIZON.
|
||||
nsecs_t minTime = newestMovement.eventTime - HORIZON;
|
||||
uint32_t oldestIndex = mIndex;
|
||||
uint32_t numTouches = 1;
|
||||
do {
|
||||
uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1;
|
||||
const Movement& nextOldestMovement = mMovements[nextOldestIndex];
|
||||
if (!nextOldestMovement.idBits.hasBit(id)
|
||||
|| nextOldestMovement.eventTime < minTime) {
|
||||
break;
|
||||
}
|
||||
oldestIndex = nextOldestIndex;
|
||||
} while (++numTouches < HISTORY_SIZE);
|
||||
|
||||
// Calculate an exponentially weighted moving average of the velocity estimate
|
||||
// at different points in time measured relative to the oldest sample.
|
||||
// This is essentially an IIR filter. Newer samples are weighted more heavily
|
||||
// than older samples. Samples at equal time points are weighted more or less
|
||||
// equally.
|
||||
//
|
||||
// One tricky problem is that the sample data may be poorly conditioned.
|
||||
// Sometimes samples arrive very close together in time which can cause us to
|
||||
// overestimate the velocity at that time point. Most samples might be measured
|
||||
// 16ms apart but some consecutive samples could be only 0.5sm apart because
|
||||
// the hardware or driver reports them irregularly or in bursts.
|
||||
float accumVx = 0;
|
||||
float accumVy = 0;
|
||||
uint32_t index = oldestIndex;
|
||||
uint32_t samplesUsed = 0;
|
||||
const Movement& oldestMovement = mMovements[oldestIndex];
|
||||
const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id);
|
||||
nsecs_t lastDuration = 0;
|
||||
|
||||
while (numTouches-- > 1) {
|
||||
if (++index == HISTORY_SIZE) {
|
||||
index = 0;
|
||||
}
|
||||
const Movement& movement = mMovements[index];
|
||||
nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
|
||||
|
||||
// If the duration between samples is small, we may significantly overestimate
|
||||
// the velocity. Consequently, we impose a minimum duration constraint on the
|
||||
// samples that we include in the calculation.
|
||||
if (duration >= MIN_DURATION) {
|
||||
const VelocityTracker::Position& position = movement.getPosition(id);
|
||||
float scale = 1000000000.0f / duration; // one over time delta in seconds
|
||||
float vx = (position.x - oldestPosition.x) * scale;
|
||||
float vy = (position.y - oldestPosition.y) * scale;
|
||||
accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration);
|
||||
accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration);
|
||||
lastDuration = duration;
|
||||
samplesUsed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Report velocity.
|
||||
const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id);
|
||||
outEstimator->time = newestMovement.eventTime;
|
||||
outEstimator->confidence = 1;
|
||||
outEstimator->xCoeff[0] = newestPosition.x;
|
||||
outEstimator->yCoeff[0] = newestPosition.y;
|
||||
if (samplesUsed) {
|
||||
outEstimator->xCoeff[1] = accumVx;
|
||||
outEstimator->yCoeff[1] = accumVy;
|
||||
outEstimator->degree = 1;
|
||||
} else {
|
||||
outEstimator->degree = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace android
|
269
widget/gonk/libui/VelocityTracker.h
Normal file
269
widget/gonk/libui/VelocityTracker.h
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef _ANDROIDFW_VELOCITY_TRACKER_H
|
||||
#define _ANDROIDFW_VELOCITY_TRACKER_H
|
||||
|
||||
#include "Input.h"
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/BitSet.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class VelocityTrackerStrategy;
|
||||
|
||||
/*
|
||||
* Calculates the velocity of pointer movements over time.
|
||||
*/
|
||||
class VelocityTracker {
|
||||
public:
|
||||
struct Position {
|
||||
float x, y;
|
||||
};
|
||||
|
||||
struct Estimator {
|
||||
static const size_t MAX_DEGREE = 4;
|
||||
|
||||
// Estimator time base.
|
||||
nsecs_t time;
|
||||
|
||||
// Polynomial coefficients describing motion in X and Y.
|
||||
float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1];
|
||||
|
||||
// Polynomial degree (number of coefficients), or zero if no information is
|
||||
// available.
|
||||
uint32_t degree;
|
||||
|
||||
// Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
|
||||
float confidence;
|
||||
|
||||
inline void clear() {
|
||||
time = 0;
|
||||
degree = 0;
|
||||
confidence = 0;
|
||||
for (size_t i = 0; i <= MAX_DEGREE; i++) {
|
||||
xCoeff[i] = 0;
|
||||
yCoeff[i] = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Creates a velocity tracker using the specified strategy.
|
||||
// If strategy is NULL, uses the default strategy for the platform.
|
||||
VelocityTracker(const char* strategy = NULL);
|
||||
|
||||
~VelocityTracker();
|
||||
|
||||
// Resets the velocity tracker state.
|
||||
void clear();
|
||||
|
||||
// Resets the velocity tracker state for specific pointers.
|
||||
// Call this method when some pointers have changed and may be reusing
|
||||
// an id that was assigned to a different pointer earlier.
|
||||
void clearPointers(BitSet32 idBits);
|
||||
|
||||
// Adds movement information for a set of pointers.
|
||||
// The idBits bitfield specifies the pointer ids of the pointers whose positions
|
||||
// are included in the movement.
|
||||
// The positions array contains position information for each pointer in order by
|
||||
// increasing id. Its size should be equal to the number of one bits in idBits.
|
||||
void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);
|
||||
|
||||
// Adds movement information for all pointers in a MotionEvent, including historical samples.
|
||||
void addMovement(const MotionEvent* event);
|
||||
|
||||
// Gets the velocity of the specified pointer id in position units per second.
|
||||
// Returns false and sets the velocity components to zero if there is
|
||||
// insufficient movement information for the pointer.
|
||||
bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
|
||||
|
||||
// Gets an estimator for the recent movements of the specified pointer id.
|
||||
// Returns false and clears the estimator if there is no information available
|
||||
// about the pointer.
|
||||
bool getEstimator(uint32_t id, Estimator* outEstimator) const;
|
||||
|
||||
// Gets the active pointer id, or -1 if none.
|
||||
inline int32_t getActivePointerId() const { return mActivePointerId; }
|
||||
|
||||
// Gets a bitset containing all pointer ids from the most recent movement.
|
||||
inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
|
||||
|
||||
private:
|
||||
static const char* DEFAULT_STRATEGY;
|
||||
|
||||
nsecs_t mLastEventTime;
|
||||
BitSet32 mCurrentPointerIdBits;
|
||||
int32_t mActivePointerId;
|
||||
VelocityTrackerStrategy* mStrategy;
|
||||
|
||||
bool configureStrategy(const char* strategy);
|
||||
|
||||
static VelocityTrackerStrategy* createStrategy(const char* strategy);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Implements a particular velocity tracker algorithm.
|
||||
*/
|
||||
class VelocityTrackerStrategy {
|
||||
protected:
|
||||
VelocityTrackerStrategy() { }
|
||||
|
||||
public:
|
||||
virtual ~VelocityTrackerStrategy() { }
|
||||
|
||||
virtual void clear() = 0;
|
||||
virtual void clearPointers(BitSet32 idBits) = 0;
|
||||
virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions) = 0;
|
||||
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Velocity tracker algorithm based on least-squares linear regression.
|
||||
*/
|
||||
class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy {
|
||||
public:
|
||||
enum Weighting {
|
||||
// No weights applied. All data points are equally reliable.
|
||||
WEIGHTING_NONE,
|
||||
|
||||
// Weight by time delta. Data points clustered together are weighted less.
|
||||
WEIGHTING_DELTA,
|
||||
|
||||
// Weight such that points within a certain horizon are weighed more than those
|
||||
// outside of that horizon.
|
||||
WEIGHTING_CENTRAL,
|
||||
|
||||
// Weight such that points older than a certain amount are weighed less.
|
||||
WEIGHTING_RECENT,
|
||||
};
|
||||
|
||||
// Degree must be no greater than Estimator::MAX_DEGREE.
|
||||
LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = WEIGHTING_NONE);
|
||||
virtual ~LeastSquaresVelocityTrackerStrategy();
|
||||
|
||||
virtual void clear();
|
||||
virtual void clearPointers(BitSet32 idBits);
|
||||
virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions);
|
||||
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
|
||||
|
||||
private:
|
||||
// Sample horizon.
|
||||
// We don't use too much history by default since we want to react to quick
|
||||
// changes in direction.
|
||||
static const nsecs_t HORIZON = 100 * 1000000; // 100 ms
|
||||
|
||||
// Number of samples to keep.
|
||||
static const uint32_t HISTORY_SIZE = 20;
|
||||
|
||||
struct Movement {
|
||||
nsecs_t eventTime;
|
||||
BitSet32 idBits;
|
||||
VelocityTracker::Position positions[MAX_POINTERS];
|
||||
|
||||
inline const VelocityTracker::Position& getPosition(uint32_t id) const {
|
||||
return positions[idBits.getIndexOfBit(id)];
|
||||
}
|
||||
};
|
||||
|
||||
float chooseWeight(uint32_t index) const;
|
||||
|
||||
const uint32_t mDegree;
|
||||
const Weighting mWeighting;
|
||||
uint32_t mIndex;
|
||||
Movement mMovements[HISTORY_SIZE];
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Velocity tracker algorithm that uses an IIR filter.
|
||||
*/
|
||||
class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy {
|
||||
public:
|
||||
// Degree must be 1 or 2.
|
||||
IntegratingVelocityTrackerStrategy(uint32_t degree);
|
||||
~IntegratingVelocityTrackerStrategy();
|
||||
|
||||
virtual void clear();
|
||||
virtual void clearPointers(BitSet32 idBits);
|
||||
virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions);
|
||||
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
|
||||
|
||||
private:
|
||||
// Current state estimate for a particular pointer.
|
||||
struct State {
|
||||
nsecs_t updateTime;
|
||||
uint32_t degree;
|
||||
|
||||
float xpos, xvel, xaccel;
|
||||
float ypos, yvel, yaccel;
|
||||
};
|
||||
|
||||
const uint32_t mDegree;
|
||||
BitSet32 mPointerIdBits;
|
||||
State mPointerState[MAX_POINTER_ID + 1];
|
||||
|
||||
void initState(State& state, nsecs_t eventTime, float xpos, float ypos) const;
|
||||
void updateState(State& state, nsecs_t eventTime, float xpos, float ypos) const;
|
||||
void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Velocity tracker strategy used prior to ICS.
|
||||
*/
|
||||
class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy {
|
||||
public:
|
||||
LegacyVelocityTrackerStrategy();
|
||||
virtual ~LegacyVelocityTrackerStrategy();
|
||||
|
||||
virtual void clear();
|
||||
virtual void clearPointers(BitSet32 idBits);
|
||||
virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
|
||||
const VelocityTracker::Position* positions);
|
||||
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
|
||||
|
||||
private:
|
||||
// Oldest sample to consider when calculating the velocity.
|
||||
static const nsecs_t HORIZON = 200 * 1000000; // 100 ms
|
||||
|
||||
// Number of samples to keep.
|
||||
static const uint32_t HISTORY_SIZE = 20;
|
||||
|
||||
// The minimum duration between samples when estimating velocity.
|
||||
static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
|
||||
|
||||
struct Movement {
|
||||
nsecs_t eventTime;
|
||||
BitSet32 idBits;
|
||||
VelocityTracker::Position positions[MAX_POINTERS];
|
||||
|
||||
inline const VelocityTracker::Position& getPosition(uint32_t id) const {
|
||||
return positions[idBits.getIndexOfBit(id)];
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t mIndex;
|
||||
Movement mMovements[HISTORY_SIZE];
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_VELOCITY_TRACKER_H
|
@ -15,14 +15,14 @@
|
||||
*/
|
||||
|
||||
#define LOG_TAG "VirtualKeyMap"
|
||||
#include "cutils_log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "utils_Log.h"
|
||||
#include "VirtualKeyMap.h"
|
||||
#include <utils/Errors.h>
|
||||
#include "Tokenizer.h"
|
||||
#include "Timers.h"
|
||||
#include <utils/Timers.h>
|
||||
|
||||
// Enables debug output for the parser.
|
||||
#define DEBUG_PARSER 0
|
||||
|
@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _UI_VIRTUAL_KEY_MAP_H
|
||||
#define _UI_VIRTUAL_KEY_MAP_H
|
||||
#ifndef _ANDROIDFW_VIRTUAL_KEY_MAP_H
|
||||
#define _ANDROIDFW_VIRTUAL_KEY_MAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -23,8 +23,8 @@
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include "Tokenizer.h"
|
||||
#include "String8.h"
|
||||
#include "Unicode.h"
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Unicode.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
@ -43,6 +43,8 @@ struct VirtualKeyDefinition {
|
||||
/**
|
||||
* Describes a collection of virtual keys on a touch screen in terms of
|
||||
* virtual scan codes and hit rectangles.
|
||||
*
|
||||
* This object is immutable after it has been loaded.
|
||||
*/
|
||||
class VirtualKeyMap {
|
||||
public:
|
||||
@ -76,4 +78,4 @@ private:
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UI_KEY_CHARACTER_MAP_H
|
||||
#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H
|
||||
|
@ -428,6 +428,7 @@ enum {
|
||||
enum {
|
||||
AINPUT_SOURCE_CLASS_MASK = 0x000000ff,
|
||||
|
||||
AINPUT_SOURCE_CLASS_NONE = 0x00000000,
|
||||
AINPUT_SOURCE_CLASS_BUTTON = 0x00000001,
|
||||
AINPUT_SOURCE_CLASS_POINTER = 0x00000002,
|
||||
AINPUT_SOURCE_CLASS_NAVIGATION = 0x00000004,
|
||||
@ -446,6 +447,7 @@ enum {
|
||||
AINPUT_SOURCE_STYLUS = 0x00004000 | AINPUT_SOURCE_CLASS_POINTER,
|
||||
AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION,
|
||||
AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION,
|
||||
AINPUT_SOURCE_TOUCH_NAVIGATION = 0x00200000 | AINPUT_SOURCE_CLASS_NONE,
|
||||
AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK,
|
||||
|
||||
AINPUT_SOURCE_ANY = 0xffffff00,
|
||||
|
@ -263,6 +263,8 @@ enum {
|
||||
AKEYCODE_RO = 217,
|
||||
AKEYCODE_KANA = 218,
|
||||
AKEYCODE_ASSIST = 219,
|
||||
AKEYCODE_BRIGHTNESS_DOWN = 220,
|
||||
AKEYCODE_BRIGHTNESS_UP = 221,
|
||||
|
||||
// NOTE: If you add a new keycode here you must also add it to several other files.
|
||||
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
|
||||
|
@ -79,10 +79,6 @@ extern "C" {
|
||||
#else
|
||||
#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef LOGV
|
||||
#define LOGV ALOGV
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
|
||||
@ -96,10 +92,6 @@ extern "C" {
|
||||
? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef LOGV_IF
|
||||
#define LOGV_IF ALOGV_IF
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -107,10 +99,6 @@ extern "C" {
|
||||
*/
|
||||
#ifndef ALOGD
|
||||
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef LOGD
|
||||
#define LOGD ALOGD
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ALOGD_IF
|
||||
@ -118,10 +106,6 @@ extern "C" {
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef LOGD_IF
|
||||
#define LOGD_IF ALOGD_IF
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -129,10 +113,6 @@ extern "C" {
|
||||
*/
|
||||
#ifndef ALOGI
|
||||
#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef LOGI
|
||||
#define LOGI ALOGI
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ALOGI_IF
|
||||
@ -140,10 +120,6 @@ extern "C" {
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef LOGI_IF
|
||||
#define LOGI_IF ALOGI_IF
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -151,10 +127,6 @@ extern "C" {
|
||||
*/
|
||||
#ifndef ALOGW
|
||||
#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef LOGW
|
||||
#define LOGW ALOGW
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ALOGW_IF
|
||||
@ -162,10 +134,6 @@ extern "C" {
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef LOGW_IF
|
||||
#define LOGW_IF ALOGW_IF
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -173,10 +141,6 @@ extern "C" {
|
||||
*/
|
||||
#ifndef ALOGE
|
||||
#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef LOGE
|
||||
#define LOGE ALOGE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ALOGE_IF
|
||||
@ -184,10 +148,6 @@ extern "C" {
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef LOGE_IF
|
||||
#define LOGE_IF ALOGE_IF
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
@ -202,10 +162,6 @@ extern "C" {
|
||||
#else
|
||||
#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
|
||||
#endif
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef IF_LOGV
|
||||
#define IF_LOGV IF_ALOGV
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -214,10 +170,6 @@ extern "C" {
|
||||
*/
|
||||
#ifndef IF_ALOGD
|
||||
#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef IF_LOGD
|
||||
#define IF_LOGD IF_ALOGD
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -226,10 +178,6 @@ extern "C" {
|
||||
*/
|
||||
#ifndef IF_ALOGI
|
||||
#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef IF_LOGI
|
||||
#define IF_LOGI IF_ALOGI
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -238,10 +186,6 @@ extern "C" {
|
||||
*/
|
||||
#ifndef IF_ALOGW
|
||||
#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef IF_LOGW
|
||||
#define IF_LOGW IF_ALOGW
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -250,10 +194,6 @@ extern "C" {
|
||||
*/
|
||||
#ifndef IF_ALOGE
|
||||
#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef IF_LOGE
|
||||
#define IF_LOGE IF_ALOGE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@ -339,7 +279,88 @@ extern "C" {
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Simplified macro to send a verbose radio log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGV
|
||||
#if LOG_NDEBUG
|
||||
#define RLOGV(...) ((void)0)
|
||||
#else
|
||||
#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
|
||||
|
||||
#ifndef RLOGV_IF
|
||||
#if LOG_NDEBUG
|
||||
#define RLOGV_IF(cond, ...) ((void)0)
|
||||
#else
|
||||
#define RLOGV_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a debug radio log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGD
|
||||
#define RLOGD(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGD_IF
|
||||
#define RLOGD_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an info radio log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGI
|
||||
#define RLOGI(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGI_IF
|
||||
#define RLOGI_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a warning radio log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGW
|
||||
#define RLOGW(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGW_IF
|
||||
#define RLOGW_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an error radio log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef RLOGE
|
||||
#define RLOGE(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef RLOGE_IF
|
||||
#define RLOGE_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
@ -392,10 +413,6 @@ extern "C" {
|
||||
#ifndef ALOG_ASSERT
|
||||
#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__)
|
||||
//#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef LOG_ASSERT
|
||||
#define LOG_ASSERT ALOG_ASSERT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
@ -411,10 +428,6 @@ extern "C" {
|
||||
#ifndef ALOG
|
||||
#define ALOG(priority, tag, ...) \
|
||||
LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef LOG
|
||||
#define LOG ALOG
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -439,10 +452,6 @@ extern "C" {
|
||||
#ifndef IF_ALOG
|
||||
#define IF_ALOG(priority, tag) \
|
||||
if (android_testLog(ANDROID_##priority, tag))
|
||||
// Temporary measure for code still using old LOG macros.
|
||||
#ifndef IF_LOG
|
||||
#define IF_LOG IF_ALOG
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
276
widget/gonk/libui/cutils_trace.h
Normal file
276
widget/gonk/libui/cutils_trace.h
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
*/
|
||||
|
||||
#ifndef _LIBS_CUTILS_TRACE_H
|
||||
#define _LIBS_CUTILS_TRACE_H
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <cutils/compiler.h>
|
||||
|
||||
#ifdef ANDROID_SMP
|
||||
#include <cutils/atomic-inline.h>
|
||||
#else
|
||||
#include <cutils/atomic.h>
|
||||
#endif
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* The ATRACE_TAG macro can be defined before including this header to trace
|
||||
* using one of the tags defined below. It must be defined to one of the
|
||||
* following ATRACE_TAG_* macros. The trace tag is used to filter tracing in
|
||||
* userland to avoid some of the runtime cost of tracing when it is not desired.
|
||||
*
|
||||
* Defining ATRACE_TAG to be ATRACE_TAG_ALWAYS will result in the tracing always
|
||||
* being enabled - this should ONLY be done for debug code, as userland tracing
|
||||
* has a performance cost even when the trace is not being recorded. Defining
|
||||
* ATRACE_TAG to be ATRACE_TAG_NEVER or leaving ATRACE_TAG undefined will result
|
||||
* in the tracing always being disabled.
|
||||
*
|
||||
* ATRACE_TAG_HAL should be bitwise ORed with the relevant tags for tracing
|
||||
* within a hardware module. For example a camera hardware module would set:
|
||||
* #define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL)
|
||||
*
|
||||
* Keep these in sync with frameworks/base/core/java/android/os/Trace.java.
|
||||
*/
|
||||
#define ATRACE_TAG_NEVER 0 // This tag is never enabled.
|
||||
#define ATRACE_TAG_ALWAYS (1<<0) // This tag is always enabled.
|
||||
#define ATRACE_TAG_GRAPHICS (1<<1)
|
||||
#define ATRACE_TAG_INPUT (1<<2)
|
||||
#define ATRACE_TAG_VIEW (1<<3)
|
||||
#define ATRACE_TAG_WEBVIEW (1<<4)
|
||||
#define ATRACE_TAG_WINDOW_MANAGER (1<<5)
|
||||
#define ATRACE_TAG_ACTIVITY_MANAGER (1<<6)
|
||||
#define ATRACE_TAG_SYNC_MANAGER (1<<7)
|
||||
#define ATRACE_TAG_AUDIO (1<<8)
|
||||
#define ATRACE_TAG_VIDEO (1<<9)
|
||||
#define ATRACE_TAG_CAMERA (1<<10)
|
||||
#define ATRACE_TAG_HAL (1<<11)
|
||||
#define ATRACE_TAG_APP (1<<12)
|
||||
#define ATRACE_TAG_RESOURCES (1<<13)
|
||||
#define ATRACE_TAG_DALVIK (1<<14)
|
||||
#define ATRACE_TAG_LAST ATRACE_TAG_DALVIK
|
||||
|
||||
// Reserved for initialization.
|
||||
#define ATRACE_TAG_NOT_READY (1LL<<63)
|
||||
|
||||
#define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)
|
||||
|
||||
#ifndef ATRACE_TAG
|
||||
#define ATRACE_TAG ATRACE_TAG_NEVER
|
||||
#elif ATRACE_TAG > ATRACE_TAG_VALID_MASK
|
||||
#error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
/**
|
||||
* Maximum size of a message that can be logged to the trace buffer.
|
||||
* Note this message includes a tag, the pid, and the string given as the name.
|
||||
* Names should be kept short to get the most use of the trace buffer.
|
||||
*/
|
||||
#define ATRACE_MESSAGE_LENGTH 1024
|
||||
|
||||
/**
|
||||
* Opens the trace file for writing and reads the property for initial tags.
|
||||
* The atrace.tags.enableflags property sets the tags to trace.
|
||||
* This function should not be explicitly called, the first call to any normal
|
||||
* trace function will cause it to be run safely.
|
||||
*/
|
||||
void atrace_setup();
|
||||
|
||||
/**
|
||||
* If tracing is ready, set atrace_enabled_tags to the system property
|
||||
* debug.atrace.tags.enableflags. Can be used as a sysprop change callback.
|
||||
*/
|
||||
void atrace_update_tags();
|
||||
|
||||
/**
|
||||
* Set whether the process is debuggable. By default the process is not
|
||||
* considered debuggable. If the process is not debuggable then application-
|
||||
* level tracing is not allowed unless the ro.debuggable system property is
|
||||
* set to '1'.
|
||||
*/
|
||||
void atrace_set_debuggable(bool debuggable);
|
||||
|
||||
/**
|
||||
* Set whether tracing is enabled for the current process. This is used to
|
||||
* prevent tracing within the Zygote process.
|
||||
*/
|
||||
void atrace_set_tracing_enabled(bool enabled);
|
||||
|
||||
/**
|
||||
* Flag indicating whether setup has been completed, initialized to 0.
|
||||
* Nonzero indicates setup has completed.
|
||||
* Note: This does NOT indicate whether or not setup was successful.
|
||||
*/
|
||||
extern volatile int32_t atrace_is_ready;
|
||||
|
||||
/**
|
||||
* Set of ATRACE_TAG flags to trace for, initialized to ATRACE_TAG_NOT_READY.
|
||||
* A value of zero indicates setup has failed.
|
||||
* Any other nonzero value indicates setup has succeeded, and tracing is on.
|
||||
*/
|
||||
extern uint64_t atrace_enabled_tags;
|
||||
|
||||
/**
|
||||
* Handle to the kernel's trace buffer, initialized to -1.
|
||||
* Any other value indicates setup has succeeded, and is a valid fd for tracing.
|
||||
*/
|
||||
extern int atrace_marker_fd;
|
||||
|
||||
/**
|
||||
* atrace_init readies the process for tracing by opening the trace_marker file.
|
||||
* Calling any trace function causes this to be run, so calling it is optional.
|
||||
* This can be explicitly run to avoid setup delay on first trace function.
|
||||
*/
|
||||
#define ATRACE_INIT() atrace_init()
|
||||
static inline void atrace_init()
|
||||
{
|
||||
if (CC_UNLIKELY(!android_atomic_acquire_load(&atrace_is_ready))) {
|
||||
atrace_setup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mask of all tags currently enabled.
|
||||
* It can be used as a guard condition around more expensive trace calculations.
|
||||
* Every trace function calls this, which ensures atrace_init is run.
|
||||
*/
|
||||
#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()
|
||||
static inline uint64_t atrace_get_enabled_tags()
|
||||
{
|
||||
atrace_init();
|
||||
return atrace_enabled_tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a given tag is currently enabled.
|
||||
* Returns nonzero if the tag is enabled, otherwise zero.
|
||||
* It can be used as a guard condition around more expensive trace calculations.
|
||||
*/
|
||||
#define ATRACE_ENABLED() atrace_is_tag_enabled(ATRACE_TAG)
|
||||
static inline uint64_t atrace_is_tag_enabled(uint64_t tag)
|
||||
{
|
||||
return atrace_get_enabled_tags() & tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace the beginning of a context. name is used to identify the context.
|
||||
* This is often used to time function execution.
|
||||
*/
|
||||
#define ATRACE_BEGIN(name) atrace_begin(ATRACE_TAG, name)
|
||||
static inline void atrace_begin(uint64_t tag, const char* name)
|
||||
{
|
||||
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
|
||||
char buf[ATRACE_MESSAGE_LENGTH];
|
||||
size_t len;
|
||||
|
||||
len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "B|%d|%s", getpid(), name);
|
||||
write(atrace_marker_fd, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace the end of a context.
|
||||
* This should match up (and occur after) a corresponding ATRACE_BEGIN.
|
||||
*/
|
||||
#define ATRACE_END() atrace_end(ATRACE_TAG)
|
||||
static inline void atrace_end(uint64_t tag)
|
||||
{
|
||||
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
|
||||
char c = 'E';
|
||||
write(atrace_marker_fd, &c, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END
|
||||
* contexts, asynchronous events do not need to be nested. The name describes
|
||||
* the event, and the cookie provides a unique identifier for distinguishing
|
||||
* simultaneous events. The name and cookie used to begin an event must be
|
||||
* used to end it.
|
||||
*/
|
||||
#define ATRACE_ASYNC_BEGIN(name, cookie) \
|
||||
atrace_async_begin(ATRACE_TAG, name, cookie)
|
||||
static inline void atrace_async_begin(uint64_t tag, const char* name,
|
||||
int32_t cookie)
|
||||
{
|
||||
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
|
||||
char buf[ATRACE_MESSAGE_LENGTH];
|
||||
size_t len;
|
||||
|
||||
len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%d", getpid(),
|
||||
name, cookie);
|
||||
write(atrace_marker_fd, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace the end of an asynchronous event.
|
||||
* This should have a corresponding ATRACE_ASYNC_BEGIN.
|
||||
*/
|
||||
#define ATRACE_ASYNC_END(name, cookie) atrace_async_end(ATRACE_TAG, name, cookie)
|
||||
static inline void atrace_async_end(uint64_t tag, const char* name,
|
||||
int32_t cookie)
|
||||
{
|
||||
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
|
||||
char buf[ATRACE_MESSAGE_LENGTH];
|
||||
size_t len;
|
||||
|
||||
len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%d", getpid(),
|
||||
name, cookie);
|
||||
write(atrace_marker_fd, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Traces an integer counter value. name is used to identify the counter.
|
||||
* This can be used to track how a value changes over time.
|
||||
*/
|
||||
#define ATRACE_INT(name, value) atrace_int(ATRACE_TAG, name, value)
|
||||
static inline void atrace_int(uint64_t tag, const char* name, int32_t value)
|
||||
{
|
||||
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
|
||||
char buf[ATRACE_MESSAGE_LENGTH];
|
||||
size_t len;
|
||||
|
||||
len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%d",
|
||||
getpid(), name, value);
|
||||
write(atrace_marker_fd, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
#else // not HAVE_ANDROID_OS
|
||||
|
||||
#define ATRACE_INIT()
|
||||
#define ATRACE_GET_ENABLED_TAGS()
|
||||
#define ATRACE_ENABLED()
|
||||
#define ATRACE_BEGIN(name)
|
||||
#define ATRACE_END()
|
||||
#define ATRACE_ASYNC_BEGIN(name, cookie)
|
||||
#define ATRACE_ASYNC_END(name, cookie)
|
||||
#define ATRACE_INT(name, value)
|
||||
|
||||
#endif // not HAVE_ANDROID_OS
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif // _LIBS_CUTILS_TRACE_H
|
File diff suppressed because it is too large
Load Diff
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// C/C++ logging functions. See the logging documentation for API details.
|
||||
//
|
||||
// We'd like these to be available from C code (in case we import some from
|
||||
// somewhere), so this has a C interface.
|
||||
//
|
||||
// The output will be correct when the log file is shared between multiple
|
||||
// threads and/or multiple processes so long as the operating system
|
||||
// supports O_APPEND. These calls have mutex-protected data structures
|
||||
// and so are NOT reentrant. Do not use LOG in a signal handler.
|
||||
//
|
||||
#ifndef _LIBS_UTILS_LOG_H
|
||||
#define _LIBS_UTILS_LOG_H
|
||||
|
||||
#include "cutils_log.h"
|
||||
|
||||
#endif // _LIBS_UTILS_LOG_H
|
@ -22,13 +22,12 @@ EXPORTS += [
|
||||
|
||||
DIRS += ['libdisplay', 'nativewindow']
|
||||
|
||||
# libui files
|
||||
CPP_SOURCES += [
|
||||
'EventHub.cpp',
|
||||
'Framebuffer.cpp',
|
||||
'GonkMemoryPressureMonitoring.cpp',
|
||||
'HwcUtils.cpp',
|
||||
'Input.cpp',
|
||||
'InputApplication.cpp',
|
||||
'InputDevice.cpp',
|
||||
'InputDispatcher.cpp',
|
||||
'InputListener.cpp',
|
||||
'InputReader.cpp',
|
||||
@ -37,25 +36,26 @@ CPP_SOURCES += [
|
||||
'KeyCharacterMap.cpp',
|
||||
'KeyLayoutMap.cpp',
|
||||
'Keyboard.cpp',
|
||||
'PointerController.cpp',
|
||||
'SpriteController.cpp',
|
||||
'Tokenizer.cpp',
|
||||
'VelocityControl.cpp',
|
||||
'VelocityTracker.cpp',
|
||||
'VirtualKeyMap.cpp',
|
||||
]
|
||||
|
||||
CPP_SOURCES += [
|
||||
'Framebuffer.cpp',
|
||||
'GfxInfo.cpp',
|
||||
'HwcUtils.cpp',
|
||||
'GonkMemoryPressureMonitoring.cpp',
|
||||
'OrientationObserver.cpp',
|
||||
'ProcessOrientation.cpp',
|
||||
'PixelFormat.cpp',
|
||||
'PointerController.cpp',
|
||||
'PropertyMap.cpp',
|
||||
'SpriteController.cpp',
|
||||
'Static.cpp',
|
||||
'String16.cpp',
|
||||
'String8.cpp',
|
||||
'Timers.cpp',
|
||||
'Tokenizer.cpp',
|
||||
'Unicode.cpp',
|
||||
'VirtualKeyMap.cpp',
|
||||
'nsAppShell.cpp',
|
||||
'nsIdleServiceGonk.cpp',
|
||||
'nsLookAndFeel.cpp',
|
||||
'nsWidgetFactory.cpp',
|
||||
'nsWindow.cpp',
|
||||
'GfxInfo.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['ANDROID_VERSION'] == '15':
|
||||
|
@ -321,16 +321,18 @@ GeckoPointerController::getBounds(float* outMinX,
|
||||
{
|
||||
int32_t width, height, orientation;
|
||||
|
||||
mConfig->getDisplayInfo(0, false, &width, &height, &orientation);
|
||||
DisplayViewport viewport;
|
||||
|
||||
mConfig->getDisplayInfo(false, &viewport);
|
||||
|
||||
*outMinX = *outMinY = 0;
|
||||
if (orientation == DISPLAY_ORIENTATION_90 ||
|
||||
orientation == DISPLAY_ORIENTATION_270) {
|
||||
*outMaxX = height;
|
||||
*outMaxY = width;
|
||||
*outMaxX = viewport.deviceHeight;
|
||||
*outMaxY = viewport.deviceWidth;
|
||||
} else {
|
||||
*outMaxX = width;
|
||||
*outMaxY = height;
|
||||
*outMaxX = viewport.deviceWidth;
|
||||
*outMaxY = viewport.deviceHeight;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -382,6 +384,16 @@ deviceId)
|
||||
{
|
||||
return new GeckoPointerController(&mConfig);
|
||||
};
|
||||
virtual void notifyInputDevicesChanged(const android::Vector<InputDeviceInfo>& inputDevices) {};
|
||||
virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor)
|
||||
{
|
||||
return NULL;
|
||||
};
|
||||
virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier)
|
||||
{
|
||||
return String8::empty();
|
||||
};
|
||||
|
||||
void setDisplayInfo();
|
||||
|
||||
protected:
|
||||
@ -454,7 +466,11 @@ GeckoInputReaderPolicy::setDisplayInfo()
|
||||
DISPLAY_ORIENTATION_270,
|
||||
"Orientation enums not matched!");
|
||||
|
||||
mConfig.setDisplayInfo(0, false, gScreenBounds.width, gScreenBounds.height, nsScreenGonk::GetRotation());
|
||||
DisplayViewport viewport;
|
||||
viewport.setNonDisplayViewport(gScreenBounds.width, gScreenBounds.height);
|
||||
viewport.displayId = 0;
|
||||
viewport.orientation = nsScreenGonk::GetRotation();
|
||||
mConfig.setDisplayInfo(false, viewport);
|
||||
}
|
||||
|
||||
void GeckoInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig)
|
||||
@ -596,16 +612,22 @@ void GeckoInputDispatcher::notifySwitch(const NotifySwitchArgs* args)
|
||||
if (!sDevInputAudioJack)
|
||||
return;
|
||||
|
||||
switch (args->switchCode) {
|
||||
case SW_HEADPHONE_INSERT:
|
||||
sHeadphoneState = args->switchValue;
|
||||
updateHeadphoneSwitch();
|
||||
break;
|
||||
case SW_MICROPHONE_INSERT:
|
||||
sMicrophoneState = args->switchValue;
|
||||
updateHeadphoneSwitch();
|
||||
break;
|
||||
bool needSwitchUpdate = false;
|
||||
|
||||
if (args->switchMask & (1 << SW_HEADPHONE_INSERT)) {
|
||||
sHeadphoneState = (args->switchValues & (1 << SW_HEADPHONE_INSERT)) ?
|
||||
AKEY_STATE_DOWN : AKEY_STATE_UP;
|
||||
needSwitchUpdate = true;
|
||||
}
|
||||
|
||||
if (args->switchMask & (1 << SW_MICROPHONE_INSERT)) {
|
||||
sMicrophoneState = (args->switchValues & (1 << SW_MICROPHONE_INSERT)) ?
|
||||
AKEY_STATE_DOWN : AKEY_STATE_UP;
|
||||
needSwitchUpdate = true;
|
||||
}
|
||||
|
||||
if (needSwitchUpdate)
|
||||
updateHeadphoneSwitch();
|
||||
}
|
||||
|
||||
void GeckoInputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args)
|
||||
|
Loading…
x
Reference in New Issue
Block a user