import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,435 @@
/*
* 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 _RUNTIME_EVENT_HUB_H
#define _RUNTIME_EVENT_HUB_H
#include "Input.h"
#include "InputDevice.h"
#include "Keyboard.h"
#include "KeyLayoutMap.h"
#include "KeyCharacterMap.h"
#include "VirtualKeyMap.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 <utils/PropertyMap.h>
#include <utils/Vector.h>
#include <utils/KeyedVector.h>
#include "linux_input.h"
#include <sys/epoll.h>
/* Convenience constants. */
#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.
*/
struct RawEvent {
nsecs_t when;
int32_t deviceId;
int32_t type;
int32_t code;
int32_t value;
};
/* Describes an absolute axis. */
struct RawAbsoluteAxisInfo {
bool valid; // true if the information is valid, false otherwise
int32_t minValue; // minimum value
int32_t maxValue; // maximum value
int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8
int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
int32_t resolution; // resolution in units per mm or radians per mm
inline void clear() {
valid = false;
minValue = 0;
maxValue = 0;
flat = 0;
fuzz = 0;
resolution = 0;
}
};
/*
* Input device classes.
*/
enum {
/* The input device is a keyboard or has buttons. */
INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
/* The input device is an alpha-numeric keyboard (not just a dial pad). */
INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
/* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
/* The input device is a cursor device such as a trackball or mouse. */
INPUT_DEVICE_CLASS_CURSOR = 0x00000008,
/* The input device is a multi-touch touchscreen. */
INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010,
/* The input device is a directional pad (implies keyboard, has DPAD keys). */
INPUT_DEVICE_CLASS_DPAD = 0x00000020,
/* The input device is a gamepad (implies keyboard, has BUTTON keys). */
INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
/* The input device has switches. */
INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
/* 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,
};
/*
* Gets the class that owns an axis, in cases where multiple classes might claim
* the same axis for different purposes.
*/
extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses);
/*
* Grand Central Station for events.
*
* The event hub aggregates input events received across all known input
* devices on the system, including devices that may be emulated by the simulator
* environment. In addition, the event hub generates fake input events to indicate
* when devices are added or removed.
*
* The event hub provides a stream of input events (via the getEvent function).
* It also supports querying the current actual state of input devices such as identifying
* which keys are currently down. Finally, the event hub keeps track of the capabilities of
* individual input devices, such as their class and the set of key codes that they support.
*/
class EventHubInterface : public virtual RefBase {
protected:
EventHubInterface() { }
virtual ~EventHubInterface() { }
public:
// Synthetic raw event type codes produced when devices are added or removed.
enum {
// Sent when a device is added.
DEVICE_ADDED = 0x10000000,
// Sent when a device is removed.
DEVICE_REMOVED = 0x20000000,
// Sent when all added/removed devices from the most recent scan have been reported.
// This event is always sent at least once.
FINISHED_DEVICE_SCAN = 0x30000000,
FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
};
virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const = 0;
virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0;
virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const = 0;
virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0;
virtual bool hasInputProperty(int32_t deviceId, int property) const = 0;
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, int32_t scanCode,
AxisInfo* outAxisInfo) const = 0;
// Sets devices that are excluded from opening.
// This can be used to ignore input devices for sensors.
virtual void setExcludedDevices(const Vector<String8>& devices) = 0;
/*
* Wait for events to become available and returns them.
* After returning, the EventHub holds onto a wake lock until the next call to getEvent.
* This ensures that the device will not go to sleep while the event is being processed.
* If the device needs to remain awake longer than that, then the caller is responsible
* for taking care of it (say, by poking the power manager user activity timer).
*
* The timeout is advisory only. If the device is asleep, it will not wake just to
* service the timeout.
*
* Returns the number of events obtained, or 0 if the timeout expired.
*/
virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0;
/*
* Query current input state.
*/
virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
int32_t* outValue) const = 0;
/*
* Examine key input devices for specific framework keycode support
*/
virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
uint8_t* outFlags) const = 0;
virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0;
virtual bool hasLed(int32_t deviceId, int32_t led) const = 0;
virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0;
virtual void getVirtualKeyDefinitions(int32_t deviceId,
Vector<VirtualKeyDefinition>& outVirtualKeys) 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;
/* Wakes up getEvents() if it is blocked on a read. */
virtual void wake() = 0;
/* Dump EventHub state to a string. */
virtual void dump(String8& dump) = 0;
/* Called by the heatbeat to ensures that the reader has not deadlocked. */
virtual void monitor() = 0;
};
class EventHub : public EventHubInterface
{
public:
EventHub();
virtual uint32_t getDeviceClasses(int32_t deviceId) const;
virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const;
virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const;
virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const;
virtual bool hasRelativeAxis(int32_t deviceId, int axis) const;
virtual bool hasInputProperty(int32_t deviceId, int property) const;
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, int32_t scanCode,
AxisInfo* outAxisInfo) const;
virtual void setExcludedDevices(const Vector<String8>& devices);
virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const;
virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const;
virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) const;
virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize);
virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const;
virtual bool hasLed(int32_t deviceId, int32_t led) const;
virtual void setLedState(int32_t deviceId, int32_t led, bool on);
virtual void getVirtualKeyDefinitions(int32_t deviceId,
Vector<VirtualKeyDefinition>& outVirtualKeys) 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();
virtual void wake();
virtual void dump(String8& dump);
virtual void monitor();
protected:
virtual ~EventHub();
private:
struct Device {
Device* next;
int fd; // may be -1 if device is virtual
const int32_t id;
const String8 path;
const InputDeviceIdentifier identifier;
uint32_t classes;
uint8_t keyBitmask[(KEY_MAX + 1) / 8];
uint8_t absBitmask[(ABS_MAX + 1) / 8];
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;
PropertyMap* configuration;
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);
void createVirtualKeyboardLocked();
void addDeviceLocked(Device* device);
status_t closeDeviceByPathLocked(const char *devicePath);
void closeDeviceLocked(Device* device);
void closeAllDevicesLocked();
status_t scanDirLocked(const char *dirname);
void scanDevicesLocked();
status_t readNotifyLocked();
Device* getDeviceLocked(int32_t deviceId) const;
Device* getDeviceByPathLocked(const char* devicePath) const;
bool hasKeycodeLocked(Device* device, int keycode) const;
void loadConfigurationLocked(Device* device);
status_t loadVirtualKeyMapLocked(Device* device);
status_t loadKeyMapLocked(Device* device);
bool isExternalDeviceLocked(Device* device);
// Protect all internal state.
mutable Mutex mLock;
// 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;
KeyedVector<int32_t, Device*> mDevices;
Device *mOpeningDevices;
Device *mClosingDevices;
bool mNeedToSendFinishedDeviceScan;
bool mNeedToReopenDevices;
bool mNeedToScanDevices;
Vector<String8> mExcludedDevices;
int mEpollFd;
int mINotifyFd;
int mWakeReadPipeFd;
int mWakeWritePipeFd;
// Ids used for epoll notifications not associated with devices.
static const uint32_t EPOLL_ID_INOTIFY = 0x80000001;
static const uint32_t EPOLL_ID_WAKE = 0x80000002;
// Epoll FD list size hint.
static const int EPOLL_SIZE_HINT = 8;
// Maximum number of signalled FDs to handle at a time.
static const int EPOLL_MAX_EVENTS = 16;
// The array of pending epoll events and the index of the next event to be handled.
struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS];
size_t mPendingEventCount;
size_t mPendingEventIndex;
bool mPendingINotify;
};
}; // namespace android
#endif // _RUNTIME_EVENT_HUB_H

635
widget/gonk/libui/Input.cpp Normal file
View file

@ -0,0 +1,635 @@
/*
* 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
#include "cutils_log.h"
#include <math.h>
#include <limits.h>
#include "Input.h"
#ifdef HAVE_ANDROID_OS
#include <binder/Parcel.h>
#include "SkPoint.h"
#include "SkMatrix.h"
#include "SkScalar.h"
#endif
namespace android {
// --- InputEvent ---
void InputEvent::initialize(int32_t deviceId, int32_t source) {
mDeviceId = deviceId;
mSource = source;
}
void InputEvent::initialize(const InputEvent& from) {
mDeviceId = from.mDeviceId;
mSource = from.mSource;
}
// --- KeyEvent ---
bool KeyEvent::hasDefaultAction(int32_t keyCode) {
switch (keyCode) {
case AKEYCODE_HOME:
case AKEYCODE_BACK:
case AKEYCODE_CALL:
case AKEYCODE_ENDCALL:
case AKEYCODE_VOLUME_UP:
case AKEYCODE_VOLUME_DOWN:
case AKEYCODE_VOLUME_MUTE:
case AKEYCODE_POWER:
case AKEYCODE_CAMERA:
case AKEYCODE_HEADSETHOOK:
case AKEYCODE_MENU:
case AKEYCODE_NOTIFICATION:
case AKEYCODE_FOCUS:
case AKEYCODE_SEARCH:
case AKEYCODE_MEDIA_PLAY:
case AKEYCODE_MEDIA_PAUSE:
case AKEYCODE_MEDIA_PLAY_PAUSE:
case AKEYCODE_MEDIA_STOP:
case AKEYCODE_MEDIA_NEXT:
case AKEYCODE_MEDIA_PREVIOUS:
case AKEYCODE_MEDIA_REWIND:
case AKEYCODE_MEDIA_RECORD:
case AKEYCODE_MEDIA_FAST_FORWARD:
case AKEYCODE_MUTE:
case AKEYCODE_BRIGHTNESS_DOWN:
case AKEYCODE_BRIGHTNESS_UP:
return true;
}
return false;
}
bool KeyEvent::hasDefaultAction() const {
return hasDefaultAction(getKeyCode());
}
bool KeyEvent::isSystemKey(int32_t keyCode) {
switch (keyCode) {
case AKEYCODE_MENU:
case AKEYCODE_SOFT_RIGHT:
case AKEYCODE_HOME:
case AKEYCODE_BACK:
case AKEYCODE_CALL:
case AKEYCODE_ENDCALL:
case AKEYCODE_VOLUME_UP:
case AKEYCODE_VOLUME_DOWN:
case AKEYCODE_VOLUME_MUTE:
case AKEYCODE_MUTE:
case AKEYCODE_POWER:
case AKEYCODE_HEADSETHOOK:
case AKEYCODE_MEDIA_PLAY:
case AKEYCODE_MEDIA_PAUSE:
case AKEYCODE_MEDIA_PLAY_PAUSE:
case AKEYCODE_MEDIA_STOP:
case AKEYCODE_MEDIA_NEXT:
case AKEYCODE_MEDIA_PREVIOUS:
case AKEYCODE_MEDIA_REWIND:
case AKEYCODE_MEDIA_RECORD:
case AKEYCODE_MEDIA_FAST_FORWARD:
case AKEYCODE_CAMERA:
case AKEYCODE_FOCUS:
case AKEYCODE_SEARCH:
case AKEYCODE_BRIGHTNESS_DOWN:
case AKEYCODE_BRIGHTNESS_UP:
return true;
}
return false;
}
bool KeyEvent::isSystemKey() const {
return isSystemKey(getKeyCode());
}
void KeyEvent::initialize(
int32_t deviceId,
int32_t source,
int32_t action,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime) {
InputEvent::initialize(deviceId, source);
mAction = action;
mFlags = flags;
mKeyCode = keyCode;
mScanCode = scanCode;
mMetaState = metaState;
mRepeatCount = repeatCount;
mDownTime = downTime;
mEventTime = eventTime;
}
void KeyEvent::initialize(const KeyEvent& from) {
InputEvent::initialize(from);
mAction = from.mAction;
mFlags = from.mFlags;
mKeyCode = from.mKeyCode;
mScanCode = from.mScanCode;
mMetaState = from.mMetaState;
mRepeatCount = from.mRepeatCount;
mDownTime = from.mDownTime;
mEventTime = from.mEventTime;
}
// --- PointerCoords ---
float PointerCoords::getAxisValue(int32_t axis) const {
if (axis < 0 || axis > 63) {
return 0;
}
uint64_t axisBit = 1LL << axis;
if (!(bits & axisBit)) {
return 0;
}
uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
return values[index];
}
status_t PointerCoords::setAxisValue(int32_t axis, float value) {
if (axis < 0 || axis > 63) {
return NAME_NOT_FOUND;
}
uint64_t axisBit = 1LL << axis;
uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
if (!(bits & axisBit)) {
if (value == 0) {
return OK; // axes with value 0 do not need to be stored
}
uint32_t count = __builtin_popcountll(bits);
if (count >= MAX_AXES) {
tooManyAxes(axis);
return NO_MEMORY;
}
bits |= axisBit;
for (uint32_t i = count; i > index; i--) {
values[i] = values[i - 1];
}
}
values[index] = value;
return OK;
}
static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) {
float value = c.getAxisValue(axis);
if (value != 0) {
c.setAxisValue(axis, value * scaleFactor);
}
}
void PointerCoords::scale(float scaleFactor) {
// No need to scale pressure or size since they are normalized.
// No need to scale orientation since it is meaningless to do so.
scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor);
scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor);
scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor);
scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor);
scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor);
scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor);
}
#ifdef HAVE_ANDROID_OS
status_t PointerCoords::readFromParcel(Parcel* parcel) {
bits = parcel->readInt64();
uint32_t count = __builtin_popcountll(bits);
if (count > MAX_AXES) {
return BAD_VALUE;
}
for (uint32_t i = 0; i < count; i++) {
values[i] = parcel->readFloat();
}
return OK;
}
status_t PointerCoords::writeToParcel(Parcel* parcel) const {
parcel->writeInt64(bits);
uint32_t count = __builtin_popcountll(bits);
for (uint32_t i = 0; i < count; i++) {
parcel->writeFloat(values[i]);
}
return OK;
}
#endif
void PointerCoords::tooManyAxes(int axis) {
ALOGW("Could not set value for axis %d because the PointerCoords structure is full and "
"cannot contain more than %d axis values.", axis, int(MAX_AXES));
}
bool PointerCoords::operator==(const PointerCoords& other) const {
if (bits != other.bits) {
return false;
}
uint32_t count = __builtin_popcountll(bits);
for (uint32_t i = 0; i < count; i++) {
if (values[i] != other.values[i]) {
return false;
}
}
return true;
}
void PointerCoords::copyFrom(const PointerCoords& other) {
bits = other.bits;
uint32_t count = __builtin_popcountll(bits);
for (uint32_t i = 0; i < count; i++) {
values[i] = other.values[i];
}
}
// --- PointerProperties ---
bool PointerProperties::operator==(const PointerProperties& other) const {
return id == other.id
&& toolType == other.toolType;
}
void PointerProperties::copyFrom(const PointerProperties& other) {
id = other.id;
toolType = other.toolType;
}
// --- MotionEvent ---
void MotionEvent::initialize(
int32_t deviceId,
int32_t source,
int32_t action,
int32_t flags,
int32_t edgeFlags,
int32_t metaState,
int32_t buttonState,
float xOffset,
float yOffset,
float xPrecision,
float yPrecision,
nsecs_t downTime,
nsecs_t eventTime,
size_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
InputEvent::initialize(deviceId, source);
mAction = action;
mFlags = flags;
mEdgeFlags = edgeFlags;
mMetaState = metaState;
mButtonState = buttonState;
mXOffset = xOffset;
mYOffset = yOffset;
mXPrecision = xPrecision;
mYPrecision = yPrecision;
mDownTime = downTime;
mPointerProperties.clear();
mPointerProperties.appendArray(pointerProperties, pointerCount);
mSampleEventTimes.clear();
mSamplePointerCoords.clear();
addSample(eventTime, pointerCoords);
}
void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
InputEvent::initialize(other->mDeviceId, other->mSource);
mAction = other->mAction;
mFlags = other->mFlags;
mEdgeFlags = other->mEdgeFlags;
mMetaState = other->mMetaState;
mButtonState = other->mButtonState;
mXOffset = other->mXOffset;
mYOffset = other->mYOffset;
mXPrecision = other->mXPrecision;
mYPrecision = other->mYPrecision;
mDownTime = other->mDownTime;
mPointerProperties = other->mPointerProperties;
if (keepHistory) {
mSampleEventTimes = other->mSampleEventTimes;
mSamplePointerCoords = other->mSamplePointerCoords;
} else {
mSampleEventTimes.clear();
mSampleEventTimes.push(other->getEventTime());
mSamplePointerCoords.clear();
size_t pointerCount = other->getPointerCount();
size_t historySize = other->getHistorySize();
mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array()
+ (historySize * pointerCount), pointerCount);
}
}
void MotionEvent::addSample(
int64_t eventTime,
const PointerCoords* pointerCoords) {
mSampleEventTimes.push(eventTime);
mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
}
const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex];
}
float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const {
return getRawPointerCoords(pointerIndex)->getAxisValue(axis);
}
float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {
float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
switch (axis) {
case AMOTION_EVENT_AXIS_X:
return value + mXOffset;
case AMOTION_EVENT_AXIS_Y:
return value + mYOffset;
}
return value;
}
const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
size_t pointerIndex, size_t historicalIndex) const {
return &mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex];
}
float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
}
float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
switch (axis) {
case AMOTION_EVENT_AXIS_X:
return value + mXOffset;
case AMOTION_EVENT_AXIS_Y:
return value + mYOffset;
}
return value;
}
ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
size_t pointerCount = mPointerProperties.size();
for (size_t i = 0; i < pointerCount; i++) {
if (mPointerProperties.itemAt(i).id == pointerId) {
return i;
}
}
return -1;
}
void MotionEvent::offsetLocation(float xOffset, float yOffset) {
mXOffset += xOffset;
mYOffset += yOffset;
}
void MotionEvent::scale(float scaleFactor) {
mXOffset *= scaleFactor;
mYOffset *= scaleFactor;
mXPrecision *= scaleFactor;
mYPrecision *= scaleFactor;
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
mSamplePointerCoords.editItemAt(i).scale(scaleFactor);
}
}
#ifdef HAVE_ANDROID_OS
static inline float transformAngle(const SkMatrix* matrix, float angleRadians) {
// Construct and transform a vector oriented at the specified clockwise angle from vertical.
// Coordinate system: down is increasing Y, right is increasing X.
SkPoint vector;
vector.fX = SkFloatToScalar(sinf(angleRadians));
vector.fY = SkFloatToScalar(-cosf(angleRadians));
matrix->mapVectors(& vector, 1);
// Derive the transformed vector's clockwise angle from vertical.
float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(-vector.fY));
if (result < - M_PI_2) {
result += M_PI;
} else if (result > M_PI_2) {
result -= M_PI;
}
return result;
}
void MotionEvent::transform(const SkMatrix* matrix) {
float oldXOffset = mXOffset;
float oldYOffset = mYOffset;
// The tricky part of this implementation is to preserve the value of
// rawX and rawY. So we apply the transformation to the first point
// then derive an appropriate new X/Y offset that will preserve rawX and rawY.
SkPoint point;
float rawX = getRawX(0);
float rawY = getRawY(0);
matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset),
& point);
float newX = SkScalarToFloat(point.fX);
float newY = SkScalarToFloat(point.fY);
float newXOffset = newX - rawX;
float newYOffset = newY - rawY;
mXOffset = newXOffset;
mYOffset = newYOffset;
// Apply the transformation to all samples.
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
PointerCoords& c = mSamplePointerCoords.editItemAt(i);
float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset;
float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset;
matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), &point);
c.setAxisValue(AMOTION_EVENT_AXIS_X, SkScalarToFloat(point.fX) - newXOffset);
c.setAxisValue(AMOTION_EVENT_AXIS_Y, SkScalarToFloat(point.fY) - newYOffset);
float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(matrix, orientation));
}
}
status_t MotionEvent::readFromParcel(Parcel* parcel) {
size_t pointerCount = parcel->readInt32();
size_t sampleCount = parcel->readInt32();
if (pointerCount == 0 || pointerCount > MAX_POINTERS || sampleCount == 0) {
return BAD_VALUE;
}
mDeviceId = parcel->readInt32();
mSource = parcel->readInt32();
mAction = parcel->readInt32();
mFlags = parcel->readInt32();
mEdgeFlags = parcel->readInt32();
mMetaState = parcel->readInt32();
mButtonState = parcel->readInt32();
mXOffset = parcel->readFloat();
mYOffset = parcel->readFloat();
mXPrecision = parcel->readFloat();
mYPrecision = parcel->readFloat();
mDownTime = parcel->readInt64();
mPointerProperties.clear();
mPointerProperties.setCapacity(pointerCount);
mSampleEventTimes.clear();
mSampleEventTimes.setCapacity(sampleCount);
mSamplePointerCoords.clear();
mSamplePointerCoords.setCapacity(sampleCount * pointerCount);
for (size_t i = 0; i < pointerCount; i++) {
mPointerProperties.push();
PointerProperties& properties = mPointerProperties.editTop();
properties.id = parcel->readInt32();
properties.toolType = parcel->readInt32();
}
while (sampleCount-- > 0) {
mSampleEventTimes.push(parcel->readInt64());
for (size_t i = 0; i < pointerCount; i++) {
mSamplePointerCoords.push();
status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel);
if (status) {
return status;
}
}
}
return OK;
}
status_t MotionEvent::writeToParcel(Parcel* parcel) const {
size_t pointerCount = mPointerProperties.size();
size_t sampleCount = mSampleEventTimes.size();
parcel->writeInt32(pointerCount);
parcel->writeInt32(sampleCount);
parcel->writeInt32(mDeviceId);
parcel->writeInt32(mSource);
parcel->writeInt32(mAction);
parcel->writeInt32(mFlags);
parcel->writeInt32(mEdgeFlags);
parcel->writeInt32(mMetaState);
parcel->writeInt32(mButtonState);
parcel->writeFloat(mXOffset);
parcel->writeFloat(mYOffset);
parcel->writeFloat(mXPrecision);
parcel->writeFloat(mYPrecision);
parcel->writeInt64(mDownTime);
for (size_t i = 0; i < pointerCount; i++) {
const PointerProperties& properties = mPointerProperties.itemAt(i);
parcel->writeInt32(properties.id);
parcel->writeInt32(properties.toolType);
}
const PointerCoords* pc = mSamplePointerCoords.array();
for (size_t h = 0; h < sampleCount; h++) {
parcel->writeInt64(mSampleEventTimes.itemAt(h));
for (size_t i = 0; i < pointerCount; i++) {
status_t status = (pc++)->writeToParcel(parcel);
if (status) {
return status;
}
}
}
return OK;
}
#endif
bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
if (source & AINPUT_SOURCE_CLASS_POINTER) {
// Specifically excludes HOVER_MOVE and SCROLL.
switch (action & AMOTION_EVENT_ACTION_MASK) {
case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_MOVE:
case AMOTION_EVENT_ACTION_UP:
case AMOTION_EVENT_ACTION_POINTER_DOWN:
case AMOTION_EVENT_ACTION_POINTER_UP:
case AMOTION_EVENT_ACTION_CANCEL:
case AMOTION_EVENT_ACTION_OUTSIDE:
return true;
}
}
return false;
}
// --- PooledInputEventFactory ---
PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
mMaxPoolSize(maxPoolSize) {
}
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);
}
}
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;
}
break;
case AINPUT_EVENT_TYPE_MOTION:
if (mMotionEventPool.size() < mMaxPoolSize) {
mMotionEventPool.push(static_cast<MotionEvent*>(event));
return;
}
break;
}
delete event;
}
} // namespace android

622
widget/gonk/libui/Input.h Normal file
View file

@ -0,0 +1,622 @@
/*
* 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 _ANDROIDFW_INPUT_H
#define _ANDROIDFW_INPUT_H
/**
* Native input event structures.
*/
#include "android_input.h"
#include <utils/Vector.h>
#include <utils/KeyedVector.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
#ifdef HAVE_ANDROID_OS
class SkMatrix;
#endif
/*
* 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,
/* Key event is inconsistent with previously sent key events. */
AKEY_EVENT_FLAG_TAINTED = 0x80000000,
};
enum {
/* Motion event is inconsistent with previously sent motion events. */
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.
* This input source flag is hidden from the API because switches are only used by the system
* and applications have no way to interact with them.
*/
AINPUT_SOURCE_SWITCH = 0x80000000,
};
/*
* SystemUiVisibility constants from View.
*/
enum {
ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0,
ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001,
};
/*
* Maximum number of pointers supported per motion event.
* Smallest number of pointers is 1.
* (We want at least 10 but some touch controllers obstensibly configured for 10 pointers
* will occasionally emit 11. There is not much harm making this constant bigger.)
*/
#define MAX_POINTERS 16
/*
* Maximum pointer id value supported in a motion event.
* Smallest pointer id is 0.
* (This is limited by our use of BitSet32 to track pointer assignments.)
*/
#define MAX_POINTER_ID 31
/*
* Declare a concrete type for the NDK's input event forward declaration.
*/
struct AInputEvent {
virtual ~AInputEvent() { }
};
/*
* Declare a concrete type for the NDK's input device forward declaration.
*/
struct AInputDevice {
virtual ~AInputDevice() { }
};
namespace android {
#ifdef HAVE_ANDROID_OS
class Parcel;
#endif
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
*
* These flags are also defined in frameworks/base/core/java/android/view/WindowManagerPolicy.java.
*/
enum {
/* These flags originate in RawEvents and are generally set in the key map.
* NOTE: If you edit these flags, also edit labels in KeycodeLabels.h. */
POLICY_FLAG_WAKE = 0x00000001,
POLICY_FLAG_WAKE_DROPPED = 0x00000002,
POLICY_FLAG_SHIFT = 0x00000004,
POLICY_FLAG_CAPS_LOCK = 0x00000008,
POLICY_FLAG_ALT = 0x00000010,
POLICY_FLAG_ALT_GR = 0x00000020,
POLICY_FLAG_MENU = 0x00000040,
POLICY_FLAG_LAUNCHER = 0x00000080,
POLICY_FLAG_VIRTUAL = 0x00000100,
POLICY_FLAG_FUNCTION = 0x00000200,
POLICY_FLAG_RAW_MASK = 0x0000ffff,
/* These flags are set by the input dispatcher. */
// Indicates that the input event was injected.
POLICY_FLAG_INJECTED = 0x01000000,
// Indicates that the input event is from a trusted source such as a directly attached
// input device or an application with system-wide event injection permission.
POLICY_FLAG_TRUSTED = 0x02000000,
// Indicates that the input event has passed through an input filter.
POLICY_FLAG_FILTERED = 0x04000000,
// Disables automatic key repeating behavior.
POLICY_FLAG_DISABLE_KEY_REPEAT = 0x08000000,
/* These flags are set by the input reader policy as it intercepts each event. */
// Indicates that the screen was off when the event was received and the event
// should wake the device.
POLICY_FLAG_WOKE_HERE = 0x10000000,
// Indicates that the screen was dim when the event was received and the event
// should brighten the device.
POLICY_FLAG_BRIGHT_HERE = 0x20000000,
// Indicates that the event should be dispatched to applications.
// The input event should still be sent to the InputDispatcher so that it can see all
// input events received include those that it will not deliver.
POLICY_FLAG_PASS_TO_USER = 0x40000000,
};
/*
* Pointer coordinate data.
*/
struct PointerCoords {
enum { MAX_AXES = 14 }; // 14 so that sizeof(PointerCoords) == 64
// Bitfield of axes that are present in this structure.
uint64_t bits;
// Values of axes that are stored in this structure packed in order by axis id
// for each axis that is present in the structure according to 'bits'.
float values[MAX_AXES];
inline void clear() {
bits = 0;
}
float getAxisValue(int32_t axis) const;
status_t setAxisValue(int32_t axis, float value);
void scale(float scale);
inline float getX() const {
return getAxisValue(AMOTION_EVENT_AXIS_X);
}
inline float getY() const {
return getAxisValue(AMOTION_EVENT_AXIS_Y);
}
#ifdef HAVE_ANDROID_OS
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
#endif
bool operator==(const PointerCoords& other) const;
inline bool operator!=(const PointerCoords& other) const {
return !(*this == other);
}
void copyFrom(const PointerCoords& other);
private:
void tooManyAxes(int axis);
};
/*
* Pointer property data.
*/
struct PointerProperties {
// The id of the pointer.
int32_t id;
// The pointer tool type.
int32_t toolType;
inline void clear() {
id = -1;
toolType = 0;
}
bool operator==(const PointerProperties& other) const;
inline bool operator!=(const PointerProperties& other) const {
return !(*this == other);
}
void copyFrom(const PointerProperties& other);
};
/*
* Input events.
*/
class InputEvent : public AInputEvent {
public:
virtual ~InputEvent() { }
virtual int32_t getType() const = 0;
inline int32_t getDeviceId() const { return mDeviceId; }
inline int32_t getSource() const { return mSource; }
inline void setSource(int32_t source) { mSource = source; }
protected:
void initialize(int32_t deviceId, int32_t source);
void initialize(const InputEvent& from);
int32_t mDeviceId;
int32_t mSource;
};
/*
* Key events.
*/
class KeyEvent : public InputEvent {
public:
virtual ~KeyEvent() { }
virtual int32_t getType() const { return AINPUT_EVENT_TYPE_KEY; }
inline int32_t getAction() const { return mAction; }
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; }
inline int32_t getMetaState() const { return mMetaState; }
inline int32_t getRepeatCount() const { return mRepeatCount; }
inline nsecs_t getDownTime() const { return mDownTime; }
inline nsecs_t getEventTime() const { return mEventTime; }
// Return true if this event may have a default action implementation.
static bool hasDefaultAction(int32_t keyCode);
bool hasDefaultAction() const;
// Return true if this event represents a system key.
static bool isSystemKey(int32_t keyCode);
bool isSystemKey() const;
void initialize(
int32_t deviceId,
int32_t source,
int32_t action,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime);
void initialize(const KeyEvent& from);
protected:
int32_t mAction;
int32_t mFlags;
int32_t mKeyCode;
int32_t mScanCode;
int32_t mMetaState;
int32_t mRepeatCount;
nsecs_t mDownTime;
nsecs_t mEventTime;
};
/*
* Motion events.
*/
class MotionEvent : public InputEvent {
public:
virtual ~MotionEvent() { }
virtual int32_t getType() const { return AINPUT_EVENT_TYPE_MOTION; }
inline int32_t getAction() const { return mAction; }
inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; }
inline int32_t getActionIndex() const {
return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
>> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
inline void setAction(int32_t action) { mAction = action; }
inline int32_t getFlags() const { return mFlags; }
inline void setFlags(int32_t flags) { mFlags = flags; }
inline int32_t getEdgeFlags() const { return mEdgeFlags; }
inline void setEdgeFlags(int32_t edgeFlags) { mEdgeFlags = edgeFlags; }
inline int32_t getMetaState() const { return mMetaState; }
inline void setMetaState(int32_t metaState) { mMetaState = metaState; }
inline int32_t getButtonState() const { return mButtonState; }
inline float getXOffset() const { return mXOffset; }
inline float getYOffset() const { return mYOffset; }
inline float getXPrecision() const { return mXPrecision; }
inline float getYPrecision() const { return mYPrecision; }
inline nsecs_t getDownTime() const { return mDownTime; }
inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; }
inline size_t getPointerCount() const { return mPointerProperties.size(); }
inline const PointerProperties* getPointerProperties(size_t pointerIndex) const {
return &mPointerProperties[pointerIndex];
}
inline int32_t getPointerId(size_t pointerIndex) const {
return mPointerProperties[pointerIndex].id;
}
inline int32_t getToolType(size_t pointerIndex) const {
return mPointerProperties[pointerIndex].toolType;
}
inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; }
const PointerCoords* getRawPointerCoords(size_t pointerIndex) const;
float getRawAxisValue(int32_t axis, size_t pointerIndex) const;
inline float getRawX(size_t pointerIndex) const {
return getRawAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex);
}
inline float getRawY(size_t pointerIndex) const {
return getRawAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex);
}
float getAxisValue(int32_t axis, size_t pointerIndex) const;
inline float getX(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex);
}
inline float getY(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex);
}
inline float getPressure(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerIndex);
}
inline float getSize(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_SIZE, pointerIndex);
}
inline float getTouchMajor(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex);
}
inline float getTouchMinor(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex);
}
inline float getToolMajor(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex);
}
inline float getToolMinor(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex);
}
inline float getOrientation(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex);
}
inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; }
inline nsecs_t getHistoricalEventTime(size_t historicalIndex) const {
return mSampleEventTimes[historicalIndex];
}
const PointerCoords* getHistoricalRawPointerCoords(
size_t pointerIndex, size_t historicalIndex) const;
float getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const;
inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalRawAxisValue(
AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex);
}
inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalRawAxisValue(
AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex);
}
float getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const;
inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalAxisValue(
AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex);
}
inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalAxisValue(
AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex);
}
inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalAxisValue(
AMOTION_EVENT_AXIS_PRESSURE, pointerIndex, historicalIndex);
}
inline float getHistoricalSize(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalAxisValue(
AMOTION_EVENT_AXIS_SIZE, pointerIndex, historicalIndex);
}
inline float getHistoricalTouchMajor(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalAxisValue(
AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex, historicalIndex);
}
inline float getHistoricalTouchMinor(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalAxisValue(
AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex, historicalIndex);
}
inline float getHistoricalToolMajor(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalAxisValue(
AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex, historicalIndex);
}
inline float getHistoricalToolMinor(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalAxisValue(
AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex, historicalIndex);
}
inline float getHistoricalOrientation(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalAxisValue(
AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex);
}
ssize_t findPointerIndex(int32_t pointerId) const;
void initialize(
int32_t deviceId,
int32_t source,
int32_t action,
int32_t flags,
int32_t edgeFlags,
int32_t metaState,
int32_t buttonState,
float xOffset,
float yOffset,
float xPrecision,
float yPrecision,
nsecs_t downTime,
nsecs_t eventTime,
size_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
void copyFrom(const MotionEvent* other, bool keepHistory);
void addSample(
nsecs_t eventTime,
const PointerCoords* pointerCoords);
void offsetLocation(float xOffset, float yOffset);
void scale(float scaleFactor);
#ifdef HAVE_ANDROID_OS
void transform(const SkMatrix* matrix);
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
#endif
static bool isTouchEvent(int32_t source, int32_t action);
inline bool isTouchEvent() const {
return isTouchEvent(mSource, mAction);
}
// Low-level accessors.
inline const PointerProperties* getPointerProperties() const {
return mPointerProperties.array();
}
inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); }
inline const PointerCoords* getSamplePointerCoords() const {
return mSamplePointerCoords.array();
}
protected:
int32_t mAction;
int32_t mFlags;
int32_t mEdgeFlags;
int32_t mMetaState;
int32_t mButtonState;
float mXOffset;
float mYOffset;
float mXPrecision;
float mYPrecision;
nsecs_t mDownTime;
Vector<PointerProperties> mPointerProperties;
Vector<nsecs_t> mSampleEventTimes;
Vector<PointerCoords> mSamplePointerCoords;
};
/*
* Input event factory.
*/
class InputEventFactoryInterface {
protected:
virtual ~InputEventFactoryInterface() { }
public:
InputEventFactoryInterface() { }
virtual KeyEvent* createKeyEvent() = 0;
virtual MotionEvent* createMotionEvent() = 0;
};
/*
* A simple input event factory implementation that uses a single preallocated instance
* of each type of input event that are reused for each request.
*/
class PreallocatedInputEventFactory : public InputEventFactoryInterface {
public:
PreallocatedInputEventFactory() { }
virtual ~PreallocatedInputEventFactory() { }
virtual KeyEvent* createKeyEvent() { return & mKeyEvent; }
virtual MotionEvent* createMotionEvent() { return & mMotionEvent; }
private:
KeyEvent mKeyEvent;
MotionEvent mMotionEvent;
};
/*
* An input event factory implementation that maintains a pool of input events.
*/
class PooledInputEventFactory : public InputEventFactoryInterface {
public:
PooledInputEventFactory(size_t maxPoolSize = 20);
virtual ~PooledInputEventFactory();
virtual KeyEvent* createKeyEvent();
virtual MotionEvent* createMotionEvent();
void recycle(InputEvent* event);
private:
const size_t mMaxPoolSize;
Vector<KeyEvent*> mKeyEventPool;
Vector<MotionEvent*> mMotionEventPool;
};
} // namespace android
#endif // _ANDROIDFW_INPUT_H

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2011 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 "InputApplication"
#include "InputApplication.h"
#include "cutils_log.h"
namespace android {
// --- InputApplicationHandle ---
InputApplicationHandle::InputApplicationHandle() :
mInfo(NULL) {
}
InputApplicationHandle::~InputApplicationHandle() {
delete mInfo;
}
void InputApplicationHandle::releaseInfo() {
if (mInfo) {
delete mInfo;
mInfo = NULL;
}
}
} // namespace android

View file

@ -0,0 +1,83 @@
/*
* Copyright (C) 2011 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_APPLICATION_H
#define _UI_INPUT_APPLICATION_H
#include "Input.h"
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <utils/String8.h>
namespace android {
/*
* Describes the properties of an application that can receive input.
*/
struct InputApplicationInfo {
String8 name;
nsecs_t dispatchingTimeout;
};
/*
* Handle for an application that can receive input.
*
* Used by the native input dispatcher as a handle for the window manager objects
* that describe an application.
*/
class InputApplicationHandle : public RefBase {
public:
inline const InputApplicationInfo* getInfo() const {
return mInfo;
}
inline String8 getName() const {
return mInfo ? mInfo->name : String8("<invalid>");
}
inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
return mInfo ? mInfo->dispatchingTimeout : defaultValue;
}
/**
* Requests that the state of this object be updated to reflect
* the most current available information about the application.
*
* This method should only be called from within the input dispatcher's
* critical section.
*
* Returns true on success, or false if the handle is no longer valid.
*/
virtual bool updateInfo() = 0;
/**
* Releases the storage used by the associated information when it is
* no longer needed.
*/
void releaseInfo();
protected:
InputApplicationHandle();
virtual ~InputApplicationHandle();
InputApplicationInfo* mInfo;
};
} // namespace android
#endif // _UI_INPUT_APPLICATION_H

View 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 &range;
}
}
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

View 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,182 @@
/*
* Copyright (C) 2011 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 "InputListener"
//#define LOG_NDEBUG 0
#include "InputListener.h"
#include "cutils_log.h"
namespace android {
// --- NotifyConfigurationChangedArgs ---
NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(nsecs_t eventTime) :
eventTime(eventTime) {
}
NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
const NotifyConfigurationChangedArgs& other) :
eventTime(other.eventTime) {
}
void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyConfigurationChanged(this);
}
// --- NotifyKeyArgs ---
NotifyKeyArgs::NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source,
uint32_t policyFlags,
int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
int32_t metaState, nsecs_t downTime) :
eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags),
action(action), flags(flags), keyCode(keyCode), scanCode(scanCode),
metaState(metaState), downTime(downTime) {
}
NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other) :
eventTime(other.eventTime), deviceId(other.deviceId), source(other.source),
policyFlags(other.policyFlags),
action(other.action), flags(other.flags),
keyCode(other.keyCode), scanCode(other.scanCode),
metaState(other.metaState), downTime(other.downTime) {
}
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyKey(this);
}
// --- NotifyMotionArgs ---
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, 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), displayId(displayId), pointerCount(pointerCount),
xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime) {
for (uint32_t i = 0; i < pointerCount; i++) {
this->pointerProperties[i].copyFrom(pointerProperties[i]);
this->pointerCoords[i].copyFrom(pointerCoords[i]);
}
}
NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) :
eventTime(other.eventTime), deviceId(other.deviceId), source(other.source),
policyFlags(other.policyFlags),
action(other.action), flags(other.flags),
metaState(other.metaState), buttonState(other.buttonState),
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]);
pointerCoords[i].copyFrom(other.pointerCoords[i]);
}
}
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyMotion(this);
}
// --- NotifySwitchArgs ---
NotifySwitchArgs::NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags,
uint32_t switchValues, uint32_t switchMask) :
eventTime(eventTime), policyFlags(policyFlags),
switchValues(switchValues), switchMask(switchMask) {
}
NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) :
eventTime(other.eventTime), policyFlags(other.policyFlags),
switchValues(other.switchValues), switchMask(other.switchMask) {
}
void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifySwitch(this);
}
// --- NotifyDeviceResetArgs ---
NotifyDeviceResetArgs::NotifyDeviceResetArgs(nsecs_t eventTime, int32_t deviceId) :
eventTime(eventTime), deviceId(deviceId) {
}
NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) :
eventTime(other.eventTime), deviceId(other.deviceId) {
}
void NotifyDeviceResetArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyDeviceReset(this);
}
// --- QueuedInputListener ---
QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
mInnerListener(innerListener) {
}
QueuedInputListener::~QueuedInputListener() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
delete mArgsQueue[i];
}
}
void QueuedInputListener::notifyConfigurationChanged(
const NotifyConfigurationChangedArgs* args) {
mArgsQueue.push(new NotifyConfigurationChangedArgs(*args));
}
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
mArgsQueue.push(new NotifyKeyArgs(*args));
}
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
mArgsQueue.push(new NotifyMotionArgs(*args));
}
void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
mArgsQueue.push(new NotifySwitchArgs(*args));
}
void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
mArgsQueue.push(new NotifyDeviceResetArgs(*args));
}
void QueuedInputListener::flush() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
NotifyArgs* args = mArgsQueue[i];
args->notify(mInnerListener);
delete args;
}
mArgsQueue.clear();
}
} // namespace android

View file

@ -0,0 +1,196 @@
/*
* Copyright (C) 2011 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_LISTENER_H
#define _UI_INPUT_LISTENER_H
#include "Input.h"
#include <utils/RefBase.h>
#include <utils/Vector.h>
namespace android {
class InputListenerInterface;
/* Superclass of all input event argument objects */
struct NotifyArgs {
virtual ~NotifyArgs() { }
virtual void notify(const sp<InputListenerInterface>& listener) const = 0;
};
/* Describes a configuration change event. */
struct NotifyConfigurationChangedArgs : public NotifyArgs {
nsecs_t eventTime;
inline NotifyConfigurationChangedArgs() { }
NotifyConfigurationChangedArgs(nsecs_t eventTime);
NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other);
virtual ~NotifyConfigurationChangedArgs() { }
virtual void notify(const sp<InputListenerInterface>& listener) const;
};
/* Describes a key event. */
struct NotifyKeyArgs : public NotifyArgs {
nsecs_t eventTime;
int32_t deviceId;
uint32_t source;
uint32_t policyFlags;
int32_t action;
int32_t flags;
int32_t keyCode;
int32_t scanCode;
int32_t metaState;
nsecs_t downTime;
inline NotifyKeyArgs() { }
NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
int32_t metaState, nsecs_t downTime);
NotifyKeyArgs(const NotifyKeyArgs& other);
virtual ~NotifyKeyArgs() { }
virtual void notify(const sp<InputListenerInterface>& listener) const;
};
/* Describes a motion event. */
struct NotifyMotionArgs : public NotifyArgs {
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 displayId;
uint32_t pointerCount;
PointerProperties pointerProperties[MAX_POINTERS];
PointerCoords pointerCoords[MAX_POINTERS];
float xPrecision;
float yPrecision;
nsecs_t downTime;
inline 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, int32_t displayId, uint32_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
float xPrecision, float yPrecision, nsecs_t downTime);
NotifyMotionArgs(const NotifyMotionArgs& other);
virtual ~NotifyMotionArgs() { }
virtual void notify(const sp<InputListenerInterface>& listener) const;
};
/* Describes a switch event. */
struct NotifySwitchArgs : public NotifyArgs {
nsecs_t eventTime;
uint32_t policyFlags;
uint32_t switchValues;
uint32_t switchMask;
inline NotifySwitchArgs() { }
NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags,
uint32_t switchValues, uint32_t switchMask);
NotifySwitchArgs(const NotifySwitchArgs& other);
virtual ~NotifySwitchArgs() { }
virtual void notify(const sp<InputListenerInterface>& listener) const;
};
/* Describes a device reset event, such as when a device is added,
* reconfigured, or removed. */
struct NotifyDeviceResetArgs : public NotifyArgs {
nsecs_t eventTime;
int32_t deviceId;
inline NotifyDeviceResetArgs() { }
NotifyDeviceResetArgs(nsecs_t eventTime, int32_t deviceId);
NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other);
virtual ~NotifyDeviceResetArgs() { }
virtual void notify(const sp<InputListenerInterface>& listener) const;
};
/*
* The interface used by the InputReader to notify the InputListener about input events.
*/
class InputListenerInterface : public virtual RefBase {
protected:
InputListenerInterface() { }
virtual ~InputListenerInterface() { }
public:
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) = 0;
virtual void notifyKey(const NotifyKeyArgs* args) = 0;
virtual void notifyMotion(const NotifyMotionArgs* args) = 0;
virtual void notifySwitch(const NotifySwitchArgs* args) = 0;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) = 0;
};
/*
* An implementation of the listener interface that queues up and defers dispatch
* of decoded events until flushed.
*/
class QueuedInputListener : public InputListenerInterface {
protected:
virtual ~QueuedInputListener();
public:
QueuedInputListener(const sp<InputListenerInterface>& innerListener);
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
virtual void notifyKey(const NotifyKeyArgs* args);
virtual void notifyMotion(const NotifyMotionArgs* args);
virtual void notifySwitch(const NotifySwitchArgs* args);
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
void flush();
private:
sp<InputListenerInterface> mInnerListener;
Vector<NotifyArgs*> mArgsQueue;
};
} // namespace android
#endif // _UI_INPUT_LISTENER_H

View 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

View 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,957 @@
//
// Copyright 2010 The Android Open Source Project
//
// Provides a shared memory transport for input events.
//
#define LOG_TAG "InputTransport"
//#define LOG_NDEBUG 0
// Log debug messages about channel messages (send message, receive message)
#define DEBUG_CHANNEL_MESSAGES 0
// Log debug messages whenever InputChannel objects are created/destroyed
#define DEBUG_CHANNEL_LIFECYCLE 0
// Log debug messages about transport actions
#define DEBUG_TRANSPORT_ACTIONS 0
// Log debug messages about touch event resampling
#define DEBUG_RESAMPLING 0
#include "cutils_log.h"
#include <cutils/properties.h>
#include <errno.h>
#include <fcntl.h>
#include "InputTransport.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <math.h>
namespace android {
// Socket buffer size. The default is typically about 128KB, which is much larger than
// we really need. So we make it smaller. It just needs to be big enough to hold
// a few dozen large multi-finger motion events in the case where an application gets
// behind processing touches.
static const size_t SOCKET_BUFFER_SIZE = 32 * 1024;
// Nanoseconds per milliseconds.
static const nsecs_t NANOS_PER_MS = 1000000;
// Latency added during resampling. A few milliseconds doesn't hurt much but
// reduces the impact of mispredicted touch positions.
static const nsecs_t RESAMPLE_LATENCY = 5 * NANOS_PER_MS;
// Minimum time difference between consecutive samples before attempting to resample.
static const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS;
// Maximum time to predict forward from the last known state, to avoid predicting too
// far into the future. This time is further bounded by 50% of the last time delta.
static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS;
template<typename T>
inline static T min(const T& a, const T& b) {
return a < b ? a : b;
}
inline static float lerp(float a, float b, float alpha) {
return a + alpha * (b - a);
}
// --- InputMessage ---
bool InputMessage::isValid(size_t actualSize) const {
if (size() == actualSize) {
switch (header.type) {
case TYPE_KEY:
return true;
case TYPE_MOTION:
return body.motion.pointerCount > 0
&& body.motion.pointerCount <= MAX_POINTERS;
case TYPE_FINISHED:
return true;
}
}
return false;
}
size_t InputMessage::size() const {
switch (header.type) {
case TYPE_KEY:
return sizeof(Header) + body.key.size();
case TYPE_MOTION:
return sizeof(Header) + body.motion.size();
case TYPE_FINISHED:
return sizeof(Header) + body.finished.size();
}
return sizeof(Header);
}
// --- InputChannel ---
InputChannel::InputChannel(const String8& name, int fd) :
mName(name), mFd(fd) {
#if DEBUG_CHANNEL_LIFECYCLE
ALOGD("Input channel constructed: name='%s', fd=%d",
mName.string(), fd);
#endif
int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket "
"non-blocking. errno=%d", mName.string(), errno);
}
InputChannel::~InputChannel() {
#if DEBUG_CHANNEL_LIFECYCLE
ALOGD("Input channel destroyed: name='%s', fd=%d",
mName.string(), mFd);
#endif
::close(mFd);
}
status_t InputChannel::openInputChannelPair(const String8& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
name.string(), errno);
outServerChannel.clear();
outClientChannel.clear();
return result;
}
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
String8 serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
String8 clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
status_t InputChannel::sendMessage(const InputMessage* msg) {
size_t msgLength = msg->size();
ssize_t nWrite;
do {
nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
int error = errno;
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(),
msg->header.type, error);
#endif
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
if (error == EPIPE || error == ENOTCONN) {
return DEAD_OBJECT;
}
return -error;
}
if (size_t(nWrite) != msgLength) {
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
mName.string(), msg->header.type);
#endif
return DEAD_OBJECT;
}
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type);
#endif
return OK;
}
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
if (nRead < 0) {
int error = errno;
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.string(), errno);
#endif
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
if (error == EPIPE || error == ENOTCONN) {
return DEAD_OBJECT;
}
return -error;
}
if (nRead == 0) { // check for EOF
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.string());
#endif
return DEAD_OBJECT;
}
if (!msg->isValid(nRead)) {
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ received invalid message", mName.string());
#endif
return BAD_VALUE;
}
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type);
#endif
return OK;
}
sp<InputChannel> InputChannel::dup() const {
int fd = ::dup(getFd());
return fd >= 0 ? new InputChannel(getName(), fd) : NULL;
}
// --- InputPublisher ---
InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
mChannel(channel) {
}
InputPublisher::~InputPublisher() {
}
status_t InputPublisher::publishKeyEvent(
uint32_t seq,
int32_t deviceId,
int32_t source,
int32_t action,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
"action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
"downTime=%lld, eventTime=%lld",
mChannel->getName().string(), seq,
deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
downTime, eventTime);
#endif
if (!seq) {
ALOGE("Attempted to publish a key event with sequence number 0.");
return BAD_VALUE;
}
InputMessage msg;
msg.header.type = InputMessage::TYPE_KEY;
msg.body.key.seq = seq;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
msg.body.key.scanCode = scanCode;
msg.body.key.metaState = metaState;
msg.body.key.repeatCount = repeatCount;
msg.body.key.downTime = downTime;
msg.body.key.eventTime = eventTime;
return mChannel->sendMessage(&msg);
}
status_t InputPublisher::publishMotionEvent(
uint32_t seq,
int32_t deviceId,
int32_t source,
int32_t action,
int32_t flags,
int32_t edgeFlags,
int32_t metaState,
int32_t buttonState,
float xOffset,
float yOffset,
float xPrecision,
float yPrecision,
nsecs_t downTime,
nsecs_t eventTime,
size_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
"action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, "
"xOffset=%f, yOffset=%f, "
"xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, "
"pointerCount=%d",
mChannel->getName().string(), seq,
deviceId, source, action, flags, edgeFlags, metaState, buttonState,
xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
#endif
if (!seq) {
ALOGE("Attempted to publish a motion event with sequence number 0.");
return BAD_VALUE;
}
if (pointerCount > MAX_POINTERS || pointerCount < 1) {
ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.",
mChannel->getName().string(), pointerCount);
return BAD_VALUE;
}
InputMessage msg;
msg.header.type = InputMessage::TYPE_MOTION;
msg.body.motion.seq = seq;
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
msg.body.motion.action = action;
msg.body.motion.flags = flags;
msg.body.motion.edgeFlags = edgeFlags;
msg.body.motion.metaState = metaState;
msg.body.motion.buttonState = buttonState;
msg.body.motion.xOffset = xOffset;
msg.body.motion.yOffset = yOffset;
msg.body.motion.xPrecision = xPrecision;
msg.body.motion.yPrecision = yPrecision;
msg.body.motion.downTime = downTime;
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
for (size_t i = 0; i < pointerCount; i++) {
msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
}
return mChannel->sendMessage(&msg);
}
status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
mChannel->getName().string());
#endif
InputMessage msg;
status_t result = mChannel->receiveMessage(&msg);
if (result) {
*outSeq = 0;
*outHandled = false;
return result;
}
if (msg.header.type != InputMessage::TYPE_FINISHED) {
ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
mChannel->getName().string(), msg.header.type);
return UNKNOWN_ERROR;
}
*outSeq = msg.body.finished.seq;
*outHandled = msg.body.finished.handled;
return OK;
}
// --- InputConsumer ---
InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
mResampleTouch(isTouchResamplingEnabled()),
mChannel(channel), mMsgDeferred(false) {
}
InputConsumer::~InputConsumer() {
}
bool InputConsumer::isTouchResamplingEnabled() {
char value[PROPERTY_VALUE_MAX];
int length = property_get("debug.inputconsumer.resample", value, NULL);
if (length > 0) {
if (!strcmp("0", value)) {
return false;
}
if (strcmp("1", value)) {
ALOGD("Unrecognized property value for 'debug.inputconsumer.resample'. "
"Use '1' or '0'.");
}
}
return true;
}
status_t InputConsumer::consume(InputEventFactoryInterface* factory,
bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%lld",
mChannel->getName().string(), consumeBatches ? "true" : "false", frameTime);
#endif
*outSeq = 0;
*outEvent = NULL;
// Fetch the next input message.
// Loop until an event can be returned or no additional events are received.
while (!*outEvent) {
if (mMsgDeferred) {
// mMsg contains a valid input message from the previous call to consume
// that has not yet been processed.
mMsgDeferred = false;
} else {
// Receive a fresh message.
status_t result = mChannel->receiveMessage(&mMsg);
if (result) {
// Consume the next batched event unless batches are being held for later.
if (consumeBatches || result != WOULD_BLOCK) {
result = consumeBatch(factory, frameTime, outSeq, outEvent);
if (*outEvent) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
mChannel->getName().string(), *outSeq);
#endif
break;
}
}
return result;
}
}
switch (mMsg.header.type) {
case InputMessage::TYPE_KEY: {
KeyEvent* keyEvent = factory->createKeyEvent();
if (!keyEvent) return NO_MEMORY;
initializeKeyEvent(keyEvent, &mMsg);
*outSeq = mMsg.body.key.seq;
*outEvent = keyEvent;
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
mChannel->getName().string(), *outSeq);
#endif
break;
}
case AINPUT_EVENT_TYPE_MOTION: {
ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
if (batchIndex >= 0) {
Batch& batch = mBatches.editItemAt(batchIndex);
if (canAddSample(batch, &mMsg)) {
batch.samples.push(mMsg);
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' consumer ~ appended to batch event",
mChannel->getName().string());
#endif
break;
} else {
// We cannot append to the batch in progress, so we need to consume
// the previous batch right now and defer the new message until later.
mMsgDeferred = true;
status_t result = consumeSamples(factory,
batch, batch.samples.size(), outSeq, outEvent);
mBatches.removeAt(batchIndex);
if (result) {
return result;
}
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' consumer ~ consumed batch event and "
"deferred current event, seq=%u",
mChannel->getName().string(), *outSeq);
#endif
break;
}
}
// Start a new batch if needed.
if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE
|| mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
mBatches.push();
Batch& batch = mBatches.editTop();
batch.samples.push(mMsg);
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' consumer ~ started batch event",
mChannel->getName().string());
#endif
break;
}
MotionEvent* motionEvent = factory->createMotionEvent();
if (! motionEvent) return NO_MEMORY;
updateTouchState(&mMsg);
initializeMotionEvent(motionEvent, &mMsg);
*outSeq = mMsg.body.motion.seq;
*outEvent = motionEvent;
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
mChannel->getName().string(), *outSeq);
#endif
break;
}
default:
ALOGE("channel '%s' consumer ~ Received unexpected message of type %d",
mChannel->getName().string(), mMsg.header.type);
return UNKNOWN_ERROR;
}
}
return OK;
}
status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory,
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
status_t result;
for (size_t i = mBatches.size(); i-- > 0; ) {
Batch& batch = mBatches.editItemAt(i);
if (frameTime < 0) {
result = consumeSamples(factory, batch, batch.samples.size(),
outSeq, outEvent);
mBatches.removeAt(i);
return result;
}
nsecs_t sampleTime = frameTime - RESAMPLE_LATENCY;
ssize_t split = findSampleNoLaterThan(batch, sampleTime);
if (split < 0) {
continue;
}
result = consumeSamples(factory, batch, split + 1, outSeq, outEvent);
const InputMessage* next;
if (batch.samples.isEmpty()) {
mBatches.removeAt(i);
next = NULL;
} else {
next = &batch.samples.itemAt(0);
}
if (!result) {
resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next);
}
return result;
}
return WOULD_BLOCK;
}
status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory,
Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) {
MotionEvent* motionEvent = factory->createMotionEvent();
if (! motionEvent) return NO_MEMORY;
uint32_t chain = 0;
for (size_t i = 0; i < count; i++) {
InputMessage& msg = batch.samples.editItemAt(i);
updateTouchState(&msg);
if (i) {
SeqChain seqChain;
seqChain.seq = msg.body.motion.seq;
seqChain.chain = chain;
mSeqChains.push(seqChain);
addSample(motionEvent, &msg);
} else {
initializeMotionEvent(motionEvent, &msg);
}
chain = msg.body.motion.seq;
}
batch.samples.removeItemsAt(0, count);
*outSeq = chain;
*outEvent = motionEvent;
return OK;
}
void InputConsumer::updateTouchState(InputMessage* msg) {
if (!mResampleTouch ||
!(msg->body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) {
return;
}
int32_t deviceId = msg->body.motion.deviceId;
int32_t source = msg->body.motion.source;
nsecs_t eventTime = msg->body.motion.eventTime;
// Update the touch state history to incorporate the new input message.
// If the message is in the past relative to the most recently produced resampled
// touch, then use the resampled time and coordinates instead.
switch (msg->body.motion.action & AMOTION_EVENT_ACTION_MASK) {
case AMOTION_EVENT_ACTION_DOWN: {
ssize_t index = findTouchState(deviceId, source);
if (index < 0) {
mTouchStates.push();
index = mTouchStates.size() - 1;
}
TouchState& touchState = mTouchStates.editItemAt(index);
touchState.initialize(deviceId, source);
touchState.addHistory(msg);
break;
}
case AMOTION_EVENT_ACTION_MOVE: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
TouchState& touchState = mTouchStates.editItemAt(index);
touchState.addHistory(msg);
if (eventTime < touchState.lastResample.eventTime) {
rewriteMessage(touchState, msg);
} else {
touchState.lastResample.idBits.clear();
}
}
break;
}
case AMOTION_EVENT_ACTION_POINTER_DOWN: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
TouchState& touchState = mTouchStates.editItemAt(index);
touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId());
rewriteMessage(touchState, msg);
}
break;
}
case AMOTION_EVENT_ACTION_POINTER_UP: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
TouchState& touchState = mTouchStates.editItemAt(index);
rewriteMessage(touchState, msg);
touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId());
}
break;
}
case AMOTION_EVENT_ACTION_SCROLL: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
const TouchState& touchState = mTouchStates.itemAt(index);
rewriteMessage(touchState, msg);
}
break;
}
case AMOTION_EVENT_ACTION_UP:
case AMOTION_EVENT_ACTION_CANCEL: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
const TouchState& touchState = mTouchStates.itemAt(index);
rewriteMessage(touchState, msg);
mTouchStates.removeAt(index);
}
break;
}
}
}
void InputConsumer::rewriteMessage(const TouchState& state, InputMessage* msg) {
for (size_t i = 0; i < msg->body.motion.pointerCount; i++) {
uint32_t id = msg->body.motion.pointers[i].properties.id;
if (state.lastResample.idBits.hasBit(id)) {
PointerCoords& msgCoords = msg->body.motion.pointers[i].coords;
const PointerCoords& resampleCoords = state.lastResample.getPointerById(id);
#if DEBUG_RESAMPLING
ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_X),
resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_Y),
msgCoords.getAxisValue(AMOTION_EVENT_AXIS_X),
msgCoords.getAxisValue(AMOTION_EVENT_AXIS_Y));
#endif
msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX());
msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY());
}
}
}
void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event,
const InputMessage* next) {
if (!mResampleTouch
|| !(event->getSource() & AINPUT_SOURCE_CLASS_POINTER)
|| event->getAction() != AMOTION_EVENT_ACTION_MOVE) {
return;
}
ssize_t index = findTouchState(event->getDeviceId(), event->getSource());
if (index < 0) {
#if DEBUG_RESAMPLING
ALOGD("Not resampled, no touch state for device.");
#endif
return;
}
TouchState& touchState = mTouchStates.editItemAt(index);
if (touchState.historySize < 1) {
#if DEBUG_RESAMPLING
ALOGD("Not resampled, no history for device.");
#endif
return;
}
// Ensure that the current sample has all of the pointers that need to be reported.
const History* current = touchState.getHistory(0);
size_t pointerCount = event->getPointerCount();
for (size_t i = 0; i < pointerCount; i++) {
uint32_t id = event->getPointerId(i);
if (!current->idBits.hasBit(id)) {
#if DEBUG_RESAMPLING
ALOGD("Not resampled, missing id %d", id);
#endif
return;
}
}
// Find the data to use for resampling.
const History* other;
History future;
float alpha;
if (next) {
// Interpolate between current sample and future sample.
// So current->eventTime <= sampleTime <= future.eventTime.
future.initializeFrom(next);
other = &future;
nsecs_t delta = future.eventTime - current->eventTime;
if (delta < RESAMPLE_MIN_DELTA) {
#if DEBUG_RESAMPLING
ALOGD("Not resampled, delta time is %lld ns.", delta);
#endif
return;
}
alpha = float(sampleTime - current->eventTime) / delta;
} else if (touchState.historySize >= 2) {
// Extrapolate future sample using current sample and past sample.
// So other->eventTime <= current->eventTime <= sampleTime.
other = touchState.getHistory(1);
nsecs_t delta = current->eventTime - other->eventTime;
if (delta < RESAMPLE_MIN_DELTA) {
#if DEBUG_RESAMPLING
ALOGD("Not resampled, delta time is %lld ns.", delta);
#endif
return;
}
nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION);
if (sampleTime > maxPredict) {
#if DEBUG_RESAMPLING
ALOGD("Sample time is too far in the future, adjusting prediction "
"from %lld to %lld ns.",
sampleTime - current->eventTime, maxPredict - current->eventTime);
#endif
sampleTime = maxPredict;
}
alpha = float(current->eventTime - sampleTime) / delta;
} else {
#if DEBUG_RESAMPLING
ALOGD("Not resampled, insufficient data.");
#endif
return;
}
// Resample touch coordinates.
touchState.lastResample.eventTime = sampleTime;
touchState.lastResample.idBits.clear();
for (size_t i = 0; i < pointerCount; i++) {
uint32_t id = event->getPointerId(i);
touchState.lastResample.idToIndex[id] = i;
touchState.lastResample.idBits.markBit(id);
PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
const PointerCoords& currentCoords = current->getPointerById(id);
if (other->idBits.hasBit(id)
&& shouldResampleTool(event->getToolType(i))) {
const PointerCoords& otherCoords = other->getPointerById(id);
resampledCoords.copyFrom(currentCoords);
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
lerp(currentCoords.getX(), otherCoords.getX(), alpha));
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
lerp(currentCoords.getY(), otherCoords.getY(), alpha));
#if DEBUG_RESAMPLING
ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
"other (%0.3f, %0.3f), alpha %0.3f",
id, resampledCoords.getX(), resampledCoords.getY(),
currentCoords.getX(), currentCoords.getY(),
otherCoords.getX(), otherCoords.getY(),
alpha);
#endif
} else {
resampledCoords.copyFrom(currentCoords);
#if DEBUG_RESAMPLING
ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)",
id, resampledCoords.getX(), resampledCoords.getY(),
currentCoords.getX(), currentCoords.getY());
#endif
}
}
event->addSample(sampleTime, touchState.lastResample.pointers);
}
bool InputConsumer::shouldResampleTool(int32_t toolType) {
return toolType == AMOTION_EVENT_TOOL_TYPE_FINGER
|| toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
}
status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
mChannel->getName().string(), seq, handled ? "true" : "false");
#endif
if (!seq) {
ALOGE("Attempted to send a finished signal with sequence number 0.");
return BAD_VALUE;
}
// Send finished signals for the batch sequence chain first.
size_t seqChainCount = mSeqChains.size();
if (seqChainCount) {
uint32_t currentSeq = seq;
uint32_t chainSeqs[seqChainCount];
size_t chainIndex = 0;
for (size_t i = seqChainCount; i-- > 0; ) {
const SeqChain& seqChain = mSeqChains.itemAt(i);
if (seqChain.seq == currentSeq) {
currentSeq = seqChain.chain;
chainSeqs[chainIndex++] = currentSeq;
mSeqChains.removeAt(i);
}
}
status_t status = OK;
while (!status && chainIndex-- > 0) {
status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);
}
if (status) {
// An error occurred so at least one signal was not sent, reconstruct the chain.
do {
SeqChain seqChain;
seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
seqChain.chain = chainSeqs[chainIndex];
mSeqChains.push(seqChain);
} while (chainIndex-- > 0);
return status;
}
}
// Send finished signal for the last message in the batch.
return sendUnchainedFinishedSignal(seq, handled);
}
status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
InputMessage msg;
msg.header.type = InputMessage::TYPE_FINISHED;
msg.body.finished.seq = seq;
msg.body.finished.handled = handled;
return mChannel->sendMessage(&msg);
}
bool InputConsumer::hasDeferredEvent() const {
return mMsgDeferred;
}
bool InputConsumer::hasPendingBatch() const {
return !mBatches.isEmpty();
}
ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
for (size_t i = 0; i < mBatches.size(); i++) {
const Batch& batch = mBatches.itemAt(i);
const InputMessage& head = batch.samples.itemAt(0);
if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) {
return i;
}
}
return -1;
}
ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const {
for (size_t i = 0; i < mTouchStates.size(); i++) {
const TouchState& touchState = mTouchStates.itemAt(i);
if (touchState.deviceId == deviceId && touchState.source == source) {
return i;
}
}
return -1;
}
void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
event->initialize(
msg->body.key.deviceId,
msg->body.key.source,
msg->body.key.action,
msg->body.key.flags,
msg->body.key.keyCode,
msg->body.key.scanCode,
msg->body.key.metaState,
msg->body.key.repeatCount,
msg->body.key.downTime,
msg->body.key.eventTime);
}
void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
size_t pointerCount = msg->body.motion.pointerCount;
PointerProperties pointerProperties[pointerCount];
PointerCoords pointerCoords[pointerCount];
for (size_t i = 0; i < pointerCount; i++) {
pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties);
pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
}
event->initialize(
msg->body.motion.deviceId,
msg->body.motion.source,
msg->body.motion.action,
msg->body.motion.flags,
msg->body.motion.edgeFlags,
msg->body.motion.metaState,
msg->body.motion.buttonState,
msg->body.motion.xOffset,
msg->body.motion.yOffset,
msg->body.motion.xPrecision,
msg->body.motion.yPrecision,
msg->body.motion.downTime,
msg->body.motion.eventTime,
pointerCount,
pointerProperties,
pointerCoords);
}
void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
size_t pointerCount = msg->body.motion.pointerCount;
PointerCoords pointerCoords[pointerCount];
for (size_t i = 0; i < pointerCount; i++) {
pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
}
event->setMetaState(event->getMetaState() | msg->body.motion.metaState);
event->addSample(msg->body.motion.eventTime, pointerCoords);
}
bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) {
const InputMessage& head = batch.samples.itemAt(0);
size_t pointerCount = msg->body.motion.pointerCount;
if (head.body.motion.pointerCount != pointerCount
|| head.body.motion.action != msg->body.motion.action) {
return false;
}
for (size_t i = 0; i < pointerCount; i++) {
if (head.body.motion.pointers[i].properties
!= msg->body.motion.pointers[i].properties) {
return false;
}
}
return true;
}
ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) {
size_t numSamples = batch.samples.size();
size_t index = 0;
while (index < numSamples
&& batch.samples.itemAt(index).body.motion.eventTime <= time) {
index += 1;
}
return ssize_t(index) - 1;
}
} // namespace android

View file

@ -0,0 +1,443 @@
/*
* 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 _ANDROIDFW_INPUT_TRANSPORT_H
#define _ANDROIDFW_INPUT_TRANSPORT_H
/**
* Native input transport.
*
* The InputChannel provides a mechanism for exchanging InputMessage structures across processes.
*
* 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 "Input.h"
#include <utils/Errors.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/BitSet.h>
namespace android {
/*
* Intermediate representation used to send input events and related signals.
*/
struct InputMessage {
enum {
TYPE_KEY = 1,
TYPE_MOTION = 2,
TYPE_FINISHED = 3,
};
struct Header {
uint32_t type;
uint32_t padding; // 8 byte alignment for the body that follows
} header;
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;
int32_t scanCode;
int32_t metaState;
int32_t repeatCount;
nsecs_t downTime;
inline size_t size() const {
return sizeof(Key);
}
} key;
struct Motion {
uint32_t seq;
nsecs_t eventTime;
int32_t deviceId;
int32_t source;
int32_t action;
int32_t flags;
int32_t metaState;
int32_t buttonState;
int32_t edgeFlags;
nsecs_t downTime;
float xOffset;
float yOffset;
float xPrecision;
float yPrecision;
size_t pointerCount;
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;
struct Finished {
uint32_t seq;
bool handled;
inline size_t size() const {
return sizeof(Finished);
}
} finished;
} body;
bool isValid(size_t actualSize) const;
size_t size() const;
};
/*
* 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:
/* Creates a publisher associated with an input channel. */
explicit InputPublisher(const sp<InputChannel>& channel);
/* Destroys the publisher and releases its input channel. */
~InputPublisher();
/* Gets the underlying input channel. */
inline sp<InputChannel> getChannel() { return mChannel; }
/* Publishes a key event to the input channel.
*
* Returns OK on success.
* 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,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime);
/* Publishes a motion event to the input channel.
*
* Returns OK on success.
* 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,
int32_t flags,
int32_t edgeFlags,
int32_t metaState,
int32_t buttonState,
float xOffset,
float yOffset,
float xPrecision,
float yPrecision,
nsecs_t downTime,
nsecs_t eventTime,
size_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
/* Receives the finished signal from the consumer in reply to the original dispatch signal.
* 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(uint32_t* outSeq, bool* outHandled);
private:
sp<InputChannel> mChannel;
};
/*
* Consumes input events from an input channel.
*/
class InputConsumer {
public:
/* Creates a consumer associated with an input channel. */
explicit InputConsumer(const sp<InputChannel>& channel);
/* Destroys the consumer and releases its input channel. */
~InputConsumer();
/* Gets the underlying input channel. */
inline sp<InputChannel> getChannel() { return mChannel; }
/* Consumes an input event from the input channel and copies its contents into
* an InputEvent object created using the specified factory.
*
* 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 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.
* Other errors probably indicate that the channel is broken.
*/
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;
// The current input message.
InputMessage mMsg;
// 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 // _ANDROIDFW_INPUT_TRANSPORT_H

View file

@ -0,0 +1,64 @@
/*
* Copyright (C) 2011 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 "InputWindow"
#include "InputWindow.h"
#include "cutils_log.h"
namespace android {
// --- InputWindowInfo ---
bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
return touchableRegion.contains(x, y);
}
bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
return x >= frameLeft && x <= frameRight
&& y >= frameTop && y <= frameBottom;
}
bool InputWindowInfo::isTrustedOverlay() const {
return layoutParamsType == TYPE_INPUT_METHOD
|| layoutParamsType == TYPE_INPUT_METHOD_DIALOG
|| layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY;
}
bool InputWindowInfo::supportsSplitTouch() const {
return layoutParamsFlags & FLAG_SPLIT_TOUCH;
}
// --- InputWindowHandle ---
InputWindowHandle::InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) :
inputApplicationHandle(inputApplicationHandle), mInfo(NULL) {
}
InputWindowHandle::~InputWindowHandle() {
delete mInfo;
}
void InputWindowHandle::releaseInfo() {
if (mInfo) {
delete mInfo;
mInfo = NULL;
}
}
} // namespace android

View file

@ -0,0 +1,205 @@
/*
* Copyright (C) 2011 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_WINDOW_H
#define _UI_INPUT_WINDOW_H
#include "Input.h"
#include "InputTransport.h"
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <utils/String8.h>
#include <SkRegion.h>
#include "InputApplication.h"
namespace android {
/*
* Describes the properties of a window that can receive input.
*/
struct InputWindowInfo {
// Window flags from WindowManager.LayoutParams
enum {
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
FLAG_DIM_BEHIND = 0x00000002,
FLAG_BLUR_BEHIND = 0x00000004,
FLAG_NOT_FOCUSABLE = 0x00000008,
FLAG_NOT_TOUCHABLE = 0x00000010,
FLAG_NOT_TOUCH_MODAL = 0x00000020,
FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
FLAG_KEEP_SCREEN_ON = 0x00000080,
FLAG_LAYOUT_IN_SCREEN = 0x00000100,
FLAG_LAYOUT_NO_LIMITS = 0x00000200,
FLAG_FULLSCREEN = 0x00000400,
FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
FLAG_DITHER = 0x00001000,
FLAG_SECURE = 0x00002000,
FLAG_SCALED = 0x00004000,
FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
FLAG_LAYOUT_INSET_DECOR = 0x00010000,
FLAG_ALT_FOCUSABLE_IM = 0x00020000,
FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
FLAG_SHOW_WHEN_LOCKED = 0x00080000,
FLAG_SHOW_WALLPAPER = 0x00100000,
FLAG_TURN_SCREEN_ON = 0x00200000,
FLAG_DISMISS_KEYGUARD = 0x00400000,
FLAG_SPLIT_TOUCH = 0x00800000,
FLAG_HARDWARE_ACCELERATED = 0x01000000,
FLAG_HARDWARE_ACCELERATED_SYSTEM = 0x02000000,
FLAG_SLIPPERY = 0x04000000,
FLAG_NEEDS_MENU_KEY = 0x08000000,
FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
FLAG_COMPATIBLE_WINDOW = 0x20000000,
FLAG_SYSTEM_ERROR = 0x40000000,
};
// Window types from WindowManager.LayoutParams
enum {
FIRST_APPLICATION_WINDOW = 1,
TYPE_BASE_APPLICATION = 1,
TYPE_APPLICATION = 2,
TYPE_APPLICATION_STARTING = 3,
LAST_APPLICATION_WINDOW = 99,
FIRST_SUB_WINDOW = 1000,
TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW,
TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1,
TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4,
LAST_SUB_WINDOW = 1999,
FIRST_SYSTEM_WINDOW = 2000,
TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW,
TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1,
TYPE_PHONE = FIRST_SYSTEM_WINDOW+2,
TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3,
TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4,
TYPE_TOAST = FIRST_SYSTEM_WINDOW+5,
TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6,
TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7,
TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8,
TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9,
TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10,
TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11,
TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13,
TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14,
TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15,
TYPE_DRAG = FIRST_SYSTEM_WINDOW+16,
TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17,
TYPE_POINTER = FIRST_SYSTEM_WINDOW+18,
TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19,
TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20,
TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21,
LAST_SYSTEM_WINDOW = 2999,
};
enum {
INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
};
sp<InputChannel> inputChannel;
String8 name;
int32_t layoutParamsFlags;
int32_t layoutParamsType;
nsecs_t dispatchingTimeout;
int32_t frameLeft;
int32_t frameTop;
int32_t frameRight;
int32_t frameBottom;
float scaleFactor;
SkRegion touchableRegion;
bool visible;
bool canReceiveKeys;
bool hasFocus;
bool hasWallpaper;
bool paused;
int32_t layer;
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;
/* Returns true if the window is of a trusted type that is allowed to silently
* overlay other windows for the purpose of implementing the secure views feature.
* Trusted overlays, such as IME windows, can partly obscure other windows without causing
* motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
*/
bool isTrustedOverlay() const;
bool supportsSplitTouch() const;
};
/*
* Handle for a window that can receive input.
*
* Used by the native input dispatcher to indirectly refer to the window manager objects
* that describe a window.
*/
class InputWindowHandle : public RefBase {
public:
const sp<InputApplicationHandle> inputApplicationHandle;
inline const InputWindowInfo* getInfo() const {
return mInfo;
}
inline sp<InputChannel> getInputChannel() const {
return mInfo ? mInfo->inputChannel : NULL;
}
inline String8 getName() const {
return mInfo ? mInfo->name : String8("<invalid>");
}
inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
return mInfo ? mInfo->dispatchingTimeout : defaultValue;
}
/**
* Requests that the state of this object be updated to reflect
* the most current available information about the application.
*
* This method should only be called from within the input dispatcher's
* critical section.
*
* Returns true on success, or false if the handle is no longer valid.
*/
virtual bool updateInfo() = 0;
/**
* Releases the storage used by the associated information when it is
* no longer needed.
*/
void releaseInfo();
protected:
InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle);
virtual ~InputWindowHandle();
InputWindowInfo* mInfo;
};
} // namespace android
#endif // _UI_INPUT_WINDOW_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,257 @@
/*
* 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.
*/
#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 <utils/String8.h>
#include <utils/Unicode.h>
#include <utils/RefBase.h>
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 : public RefBase {
public:
enum KeyboardType {
KEYBOARD_TYPE_UNKNOWN = 0,
KEYBOARD_TYPE_NUMERIC = 1,
KEYBOARD_TYPE_PREDICTIVE = 2,
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.
struct FallbackAction {
int32_t keyCode;
int32_t metaState;
};
/* Loads a key character map from a file. */
static status_t load(const String8& filename, Format format, sp<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;
/* Gets the primary character for this key as in the label physically printed on it.
* Returns 0 if none (eg. for non-printing keys). */
char16_t getDisplayLabel(int32_t keyCode) const;
/* Gets the Unicode character for the number or symbol generated by the key
* when the keyboard is used as a dialing pad.
* Returns 0 if no number or symbol is generated.
*/
char16_t getNumber(int32_t keyCode) const;
/* Gets the Unicode character generated by the key and meta key modifiers.
* Returns 0 if no character is generated.
*/
char16_t getCharacter(int32_t keyCode, int32_t metaState) const;
/* Gets the fallback action to use by default if the application does not
* handle the specified key.
* Returns true if an action was available, false if none.
*/
bool getFallbackAction(int32_t keyCode, int32_t metaState,
FallbackAction* outFallbackAction) const;
/* Gets the first matching Unicode character that can be generated by the key,
* preferring the one with the specified meta key modifiers.
* Returns 0 if no matching character is generated.
*/
char16_t getMatch(int32_t keyCode, const char16_t* chars,
size_t numChars, int32_t metaState) const;
/* Gets a sequence of key events that could plausibly generate the specified
* character sequence. Returns false if some of the characters cannot be generated.
*/
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;
/* The meta key modifiers for this behavior. */
int32_t metaState;
/* The character to insert. */
char16_t character;
/* The fallback keycode if the key is not handled. */
int32_t fallbackKeyCode;
};
struct Key {
Key();
Key(const Key& other);
~Key();
/* The single character label printed on the key, or 0 if none. */
char16_t label;
/* The number or symbol character generated by the key, or 0 if none. */
char16_t number;
/* The list of key behaviors sorted from most specific to least specific
* meta key binding. */
Behavior* firstBehavior;
};
class Parser {
enum State {
STATE_TOP = 0,
STATE_KEY = 1,
};
enum {
PROPERTY_LABEL = 1,
PROPERTY_NUMBER = 2,
PROPERTY_META = 3,
};
struct Property {
inline Property(int32_t property = 0, int32_t metaState = 0) :
property(property), metaState(metaState) { }
int32_t property;
int32_t metaState;
};
KeyCharacterMap* mMap;
Tokenizer* mTokenizer;
Format mFormat;
State mState;
int32_t mKeyCode;
public:
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,
int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
int32_t* currentMetaState);
static bool addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
int32_t keyCode, int32_t keyMetaState,
int32_t* currentMetaState);
static void addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
int32_t leftKeyCode, int32_t leftKeyMetaState,
int32_t rightKeyCode, int32_t rightKeyMetaState,
int32_t eitherKeyMetaState,
int32_t* currentMetaState);
static void addLockedMetaKey(Vector<KeyEvent>& outEvents,
int32_t deviceId, int32_t metaState, nsecs_t time,
int32_t keyCode, int32_t keyMetaState,
int32_t* currentMetaState);
};
} // namespace android
#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H

View file

@ -0,0 +1,446 @@
/*
* 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 "KeyLayoutMap"
#include "cutils_log.h"
#include <stdlib.h>
#include "android_keycodes.h"
#include "Keyboard.h"
#include "KeyLayoutMap.h"
#include <utils/Errors.h>
#include "Tokenizer.h"
#include <utils/Timers.h>
// Enables debug output for the parser.
#define DEBUG_PARSER 0
// Enables debug output for parser performance.
#define DEBUG_PARSER_PERFORMANCE 0
// Enables debug output for mapping.
#define DEBUG_MAPPING 0
namespace android {
static const char* WHITESPACE = " \t\r";
// --- KeyLayoutMap ---
KeyLayoutMap::KeyLayoutMap() {
}
KeyLayoutMap::~KeyLayoutMap() {
}
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 {
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.get(), tokenizer);
status = parser.parse();
#if DEBUG_PARSER_PERFORMANCE
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
elapsedTime / 1000000.0);
#endif
if (!status) {
*outMap = map;
}
}
delete tokenizer;
}
return status;
}
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, usageCode=0x%08x ~ Failed.", scanCode, usageCode);
#endif
*outKeyCode = AKEYCODE_UNKNOWN;
*outFlags = 0;
return NAME_NOT_FOUND;
}
*outKeyCode = key->keyCode;
*outFlags = key->flags;
#if DEBUG_MAPPING
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 = mKeysByScanCode.size();
for (size_t i=0; i<N; i++) {
if (mKeysByScanCode.valueAt(i).keyCode == keyCode) {
outScanCodes->add(mKeysByScanCode.keyAt(i));
}
}
return NO_ERROR;
}
status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const {
ssize_t index = mAxes.indexOfKey(scanCode);
if (index < 0) {
#if DEBUG_MAPPING
ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode);
#endif
return NAME_NOT_FOUND;
}
*outAxisInfo = mAxes.valueAt(index);
#if DEBUG_MAPPING
ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, "
"splitValue=%d, flatOverride=%d.",
scanCode,
outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis,
outAxisInfo->splitValue, outAxisInfo->flatOverride);
#endif
return NO_ERROR;
}
status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const {
const size_t N = mLedsByScanCode.size();
for (size_t i = 0; i < N; i++) {
if (mLedsByScanCode.valueAt(i).ledCode == ledCode) {
*outScanCode = mLedsByScanCode.keyAt(i);
#if DEBUG_MAPPING
ALOGD("findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, *outScanCode);
#endif
return NO_ERROR;
}
}
#if DEBUG_MAPPING
ALOGD("findScanCodeForLed: ledCode=%d ~ Not found.", ledCode);
#endif
return NAME_NOT_FOUND;
}
status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const {
const size_t N = mLedsByUsageCode.size();
for (size_t i = 0; i < N; i++) {
if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) {
*outUsageCode = mLedsByUsageCode.keyAt(i);
#if DEBUG_MAPPING
ALOGD("findUsageForLed: ledCode=%d, usage=%x.", ledCode, *outUsageCode);
#endif
return NO_ERROR;
}
}
#if DEBUG_MAPPING
ALOGD("findUsageForLed: ledCode=%d ~ Not found.", ledCode);
#endif
return NAME_NOT_FOUND;
}
// --- KeyLayoutMap::Parser ---
KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) :
mMap(map), mTokenizer(tokenizer) {
}
KeyLayoutMap::Parser::~Parser() {
}
status_t KeyLayoutMap::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 keywordToken = mTokenizer->nextToken(WHITESPACE);
if (keywordToken == "key") {
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseKey();
if (status) return status;
} else if (keywordToken == "axis") {
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseAxis();
if (status) return status;
} else if (keywordToken == "led") {
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseLed();
if (status) return status;
} else {
ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
keywordToken.string());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
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;
}
}
mTokenizer->nextLine();
}
return NO_ERROR;
}
status_t KeyLayoutMap::Parser::parseKey() {
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, 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;
}
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;
}
uint32_t flags = 0;
for (;;) {
mTokenizer->skipDelimiters(WHITESPACE);
if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
String8 flagToken = mTokenizer->nextToken(WHITESPACE);
uint32_t flag = getKeyFlagByLabel(flagToken.string());
if (!flag) {
ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
flagToken.string());
return BAD_VALUE;
}
if (flags & flag) {
ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
flagToken.string());
return BAD_VALUE;
}
flags |= flag;
}
#if DEBUG_PARSER
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;
map.add(code, key);
return NO_ERROR;
}
status_t KeyLayoutMap::Parser::parseAxis() {
String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
char* end;
int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
if (*end) {
ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(),
scanCodeToken.string());
return BAD_VALUE;
}
if (mMap->mAxes.indexOfKey(scanCode) >= 0) {
ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
scanCodeToken.string());
return BAD_VALUE;
}
AxisInfo axisInfo;
mTokenizer->skipDelimiters(WHITESPACE);
String8 token = mTokenizer->nextToken(WHITESPACE);
if (token == "invert") {
axisInfo.mode = AxisInfo::MODE_INVERT;
mTokenizer->skipDelimiters(WHITESPACE);
String8 axisToken = mTokenizer->nextToken(WHITESPACE);
axisInfo.axis = getAxisByLabel(axisToken.string());
if (axisInfo.axis < 0) {
ALOGE("%s: Expected inverted axis label, got '%s'.",
mTokenizer->getLocation().string(), axisToken.string());
return BAD_VALUE;
}
} else if (token == "split") {
axisInfo.mode = AxisInfo::MODE_SPLIT;
mTokenizer->skipDelimiters(WHITESPACE);
String8 splitToken = mTokenizer->nextToken(WHITESPACE);
axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0));
if (*end) {
ALOGE("%s: Expected split value, got '%s'.",
mTokenizer->getLocation().string(), splitToken.string());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
axisInfo.axis = getAxisByLabel(lowAxisToken.string());
if (axisInfo.axis < 0) {
ALOGE("%s: Expected low axis label, got '%s'.",
mTokenizer->getLocation().string(), lowAxisToken.string());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
axisInfo.highAxis = getAxisByLabel(highAxisToken.string());
if (axisInfo.highAxis < 0) {
ALOGE("%s: Expected high axis label, got '%s'.",
mTokenizer->getLocation().string(), highAxisToken.string());
return BAD_VALUE;
}
} else {
axisInfo.axis = getAxisByLabel(token.string());
if (axisInfo.axis < 0) {
ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
mTokenizer->getLocation().string(), token.string());
return BAD_VALUE;
}
}
for (;;) {
mTokenizer->skipDelimiters(WHITESPACE);
if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') {
break;
}
String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
if (keywordToken == "flat") {
mTokenizer->skipDelimiters(WHITESPACE);
String8 flatToken = mTokenizer->nextToken(WHITESPACE);
axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0));
if (*end) {
ALOGE("%s: Expected flat value, got '%s'.",
mTokenizer->getLocation().string(), flatToken.string());
return BAD_VALUE;
}
} else {
ALOGE("%s: Expected keyword 'flat', got '%s'.",
mTokenizer->getLocation().string(), keywordToken.string());
return BAD_VALUE;
}
}
#if DEBUG_PARSER
ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, "
"splitValue=%d, flatOverride=%d.",
scanCode,
axisInfo.mode, axisInfo.axis, axisInfo.highAxis,
axisInfo.splitValue, axisInfo.flatOverride);
#endif
mMap->mAxes.add(scanCode, axisInfo);
return NO_ERROR;
}
status_t KeyLayoutMap::Parser::parseLed() {
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 led %s number, got '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
}
KeyedVector<int32_t, Led>& map = mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode;
if (map.indexOfKey(code) >= 0) {
ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE);
int32_t ledCode = getLedByLabel(ledCodeToken.string());
if (ledCode < 0) {
ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(),
ledCodeToken.string());
return BAD_VALUE;
}
#if DEBUG_PARSER
ALOGD("Parsed led %s: code=%d, ledCode=%d.",
mapUsage ? "usage" : "scan code", code, ledCode);
#endif
Led led;
led.ledCode = ledCode;
map.add(code, led);
return NO_ERROR;
}
};

View file

@ -0,0 +1,117 @@
/*
* 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.
*/
#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 {
struct AxisInfo {
enum Mode {
// Axis value is reported directly.
MODE_NORMAL = 0,
// Axis value should be inverted before reporting.
MODE_INVERT = 1,
// Axis value should be split into two axes
MODE_SPLIT = 2,
};
// Axis mode.
Mode mode;
// Axis id.
// When split, this is the axis used for values smaller than the split position.
int32_t axis;
// When split, this is the axis used for values after higher than the split position.
int32_t highAxis;
// The split value, or 0 if not split.
int32_t splitValue;
// The flat value, or -1 if none.
int32_t flatOverride;
AxisInfo() : mode(MODE_NORMAL), axis(-1), highAxis(-1), splitValue(0), flatOverride(-1) {
}
};
/**
* 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 : public RefBase {
public:
static status_t load(const String8& filename, sp<KeyLayoutMap>* outMap);
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 findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const;
status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const;
status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const;
protected:
virtual ~KeyLayoutMap();
private:
struct Key {
int32_t keyCode;
uint32_t flags;
};
struct Led {
int32_t ledCode;
};
KeyedVector<int32_t, Key> mKeysByScanCode;
KeyedVector<int32_t, Key> mKeysByUsageCode;
KeyedVector<int32_t, AxisInfo> mAxes;
KeyedVector<int32_t, Led> mLedsByScanCode;
KeyedVector<int32_t, Led> mLedsByUsageCode;
KeyLayoutMap();
const Key* getKey(int32_t scanCode, int32_t usageCode) const;
class Parser {
KeyLayoutMap* mMap;
Tokenizer* mTokenizer;
public:
Parser(KeyLayoutMap* map, Tokenizer* tokenizer);
~Parser();
status_t parse();
private:
status_t parseKey();
status_t parseAxis();
status_t parseLed();
};
};
} // namespace android
#endif // _ANDROIDFW_KEY_LAYOUT_MAP_H

View file

@ -0,0 +1,300 @@
/*
* 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 "Keyboard"
#include "cutils_log.h"
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include "Keyboard.h"
#include "KeycodeLabels.h"
#include "KeyLayoutMap.h"
#include "KeyCharacterMap.h"
#include "InputDevice.h"
#include <utils/Errors.h>
#include <cutils/properties.h>
namespace android {
// --- KeyMap ---
KeyMap::KeyMap() {
}
KeyMap::~KeyMap() {
}
status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
const PropertyMap* deviceConfiguration) {
// Use the configured key layout if available.
if (deviceConfiguration) {
String8 keyLayoutName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
keyLayoutName)) {
status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
"it was not found.",
deviceIdenfifier.name.string(), keyLayoutName.string());
}
}
String8 keyCharacterMapName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
keyCharacterMapName)) {
status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard character "
"map '%s' but it was not found.",
deviceIdenfifier.name.string(), keyLayoutName.string());
}
}
if (isComplete()) {
return OK;
}
}
// Try searching by device identifier.
if (probeKeyMap(deviceIdenfifier, String8::empty())) {
return OK;
}
// Fall back on the Generic key map.
// TODO Apply some additional heuristics here to figure out what kind of
// generic key map to use (US English, etc.) for typical external keyboards.
if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
return OK;
}
// Try the Virtual key map as a last resort.
if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {
return OK;
}
// Give up!
ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
deviceIdenfifier.name.string());
return NAME_NOT_FOUND;
}
bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
const String8& keyMapName) {
if (!haveKeyLayout()) {
loadKeyLayout(deviceIdentifier, keyMapName);
}
if (!haveKeyCharacterMap()) {
loadKeyCharacterMap(deviceIdentifier, keyMapName);
}
return isComplete();
}
status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
const String8& name) {
String8 path(getPath(deviceIdentifier, name,
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
if (path.isEmpty()) {
return NAME_NOT_FOUND;
}
status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
if (status) {
return status;
}
keyLayoutFile.setTo(path);
return OK;
}
status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
const String8& name) {
String8 path(getPath(deviceIdentifier, name,
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
if (path.isEmpty()) {
return NAME_NOT_FOUND;
}
status_t status = KeyCharacterMap::load(path,
KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
if (status) {
return status;
}
keyCharacterMapFile.setTo(path);
return OK;
}
String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
const String8& name, InputDeviceConfigurationFileType type) {
return name.isEmpty()
? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
: getInputDeviceConfigurationFilePathByName(name, type);
}
// --- Global functions ---
bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
const PropertyMap* deviceConfiguration, const KeyMap* keyMap) {
if (!keyMap->haveKeyCharacterMap()
|| keyMap->keyCharacterMap->getKeyboardType()
== KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) {
return false;
}
if (deviceConfiguration) {
bool builtIn = false;
if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn)
&& builtIn) {
return true;
}
}
return strstr(deviceIdentifier.name.string(), "-keypad");
}
static int lookupValueByLabel(const char* literal, const KeycodeLabel *list) {
while (list->literal) {
if (strcmp(literal, list->literal) == 0) {
return list->value;
}
list++;
}
return list->value;
}
static const char* lookupLabelByValue(int value, const KeycodeLabel *list) {
while (list->literal) {
if (list->value == value) {
return list->literal;
}
list++;
}
return NULL;
}
int32_t getKeyCodeByLabel(const char* label) {
return int32_t(lookupValueByLabel(label, KEYCODES));
}
uint32_t getKeyFlagByLabel(const char* label) {
return uint32_t(lookupValueByLabel(label, FLAGS));
}
int32_t getAxisByLabel(const char* label) {
return int32_t(lookupValueByLabel(label, AXES));
}
const char* getAxisLabel(int32_t axisId) {
return lookupLabelByValue(axisId, AXES);
}
int32_t getLedByLabel(const char* label) {
return int32_t(lookupValueByLabel(label, LEDS));
}
static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
int32_t newMetaState;
if (down) {
newMetaState = oldMetaState | mask;
} else {
newMetaState = oldMetaState &
~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
}
if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
newMetaState |= AMETA_ALT_ON;
}
if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
newMetaState |= AMETA_SHIFT_ON;
}
if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
newMetaState |= AMETA_CTRL_ON;
}
if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
newMetaState |= AMETA_META_ON;
}
return newMetaState;
}
static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
if (down) {
return oldMetaState;
} else {
return oldMetaState ^ mask;
}
}
int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
switch (keyCode) {
case AKEYCODE_ALT_LEFT:
return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
case AKEYCODE_ALT_RIGHT:
return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
case AKEYCODE_SHIFT_LEFT:
return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
case AKEYCODE_SHIFT_RIGHT:
return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
case AKEYCODE_SYM:
return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
case AKEYCODE_FUNCTION:
return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
case AKEYCODE_CTRL_LEFT:
return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
case AKEYCODE_CTRL_RIGHT:
return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
case AKEYCODE_META_LEFT:
return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
case AKEYCODE_META_RIGHT:
return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
case AKEYCODE_CAPS_LOCK:
return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState);
case AKEYCODE_NUM_LOCK:
return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState);
case AKEYCODE_SCROLL_LOCK:
return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState);
default:
return oldMetaState;
}
}
bool isMetaKey(int32_t keyCode) {
switch (keyCode) {
case AKEYCODE_ALT_LEFT:
case AKEYCODE_ALT_RIGHT:
case AKEYCODE_SHIFT_LEFT:
case AKEYCODE_SHIFT_RIGHT:
case AKEYCODE_SYM:
case AKEYCODE_FUNCTION:
case AKEYCODE_CTRL_LEFT:
case AKEYCODE_CTRL_RIGHT:
case AKEYCODE_META_LEFT:
case AKEYCODE_META_RIGHT:
case AKEYCODE_CAPS_LOCK:
case AKEYCODE_NUM_LOCK:
case AKEYCODE_SCROLL_LOCK:
return true;
default:
return false;
}
}
} // namespace android

View file

@ -0,0 +1,122 @@
/*
* 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 _ANDROIDFW_KEYBOARD_H
#define _ANDROIDFW_KEYBOARD_H
#include "Input.h"
#include "InputDevice.h"
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/PropertyMap.h>
namespace android {
enum {
/* Device id of the built in keyboard. */
DEVICE_ID_BUILT_IN_KEYBOARD = 0,
/* Device id of a generic virtual keyboard with a full layout that can be used
* to synthesize key events. */
DEVICE_ID_VIRTUAL_KEYBOARD = -1,
};
class KeyLayoutMap;
class KeyCharacterMap;
/**
* Loads the key layout map and key character map for a keyboard device.
*/
class KeyMap {
public:
String8 keyLayoutFile;
sp<KeyLayoutMap> keyLayoutMap;
String8 keyCharacterMapFile;
sp<KeyCharacterMap> keyCharacterMap;
KeyMap();
~KeyMap();
status_t load(const InputDeviceIdentifier& deviceIdenfier,
const PropertyMap* deviceConfiguration);
inline bool haveKeyLayout() const {
return !keyLayoutFile.isEmpty();
}
inline bool haveKeyCharacterMap() const {
return !keyCharacterMapFile.isEmpty();
}
inline bool isComplete() const {
return haveKeyLayout() && haveKeyCharacterMap();
}
private:
bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
const String8& name);
String8 getPath(const InputDeviceIdentifier& deviceIdentifier,
const String8& name, InputDeviceConfigurationFileType type);
};
/**
* Returns true if the keyboard is eligible for use as a built-in keyboard.
*/
extern bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
const PropertyMap* deviceConfiguration, const KeyMap* keyMap);
/**
* Gets a key code by its short form label, eg. "HOME".
* Returns 0 if unknown.
*/
extern int32_t getKeyCodeByLabel(const char* label);
/**
* Gets a key flag by its short form label, eg. "WAKE".
* Returns 0 if unknown.
*/
extern uint32_t getKeyFlagByLabel(const char* label);
/**
* Gets a axis by its short form label, eg. "X".
* Returns -1 if unknown.
*/
extern int32_t getAxisByLabel(const char* label);
/**
* Gets a axis label by its id.
* Returns NULL if unknown.
*/
extern const char* getAxisLabel(int32_t axisId);
extern int32_t getLedByLabel(const char* label);
/**
* Updates a meta state field when a key is pressed or released.
*/
extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
/**
* Returns true if a key is a meta key like ALT or CAPS_LOCK.
*/
extern bool isMetaKey(int32_t keyCode);
} // namespace android
#endif // _ANDROIDFW_KEYBOARD_H

View file

@ -0,0 +1,380 @@
/*
* 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.
*/
#ifndef _ANDROIDFW_KEYCODE_LABELS_H
#define _ANDROIDFW_KEYCODE_LABELS_H
#include "android_keycodes.h"
struct KeycodeLabel {
const char *literal;
int value;
};
static const KeycodeLabel KEYCODES[] = {
{ "SOFT_LEFT", 1 },
{ "SOFT_RIGHT", 2 },
{ "HOME", 3 },
{ "BACK", 4 },
{ "CALL", 5 },
{ "ENDCALL", 6 },
{ "0", 7 },
{ "1", 8 },
{ "2", 9 },
{ "3", 10 },
{ "4", 11 },
{ "5", 12 },
{ "6", 13 },
{ "7", 14 },
{ "8", 15 },
{ "9", 16 },
{ "STAR", 17 },
{ "POUND", 18 },
{ "DPAD_UP", 19 },
{ "DPAD_DOWN", 20 },
{ "DPAD_LEFT", 21 },
{ "DPAD_RIGHT", 22 },
{ "DPAD_CENTER", 23 },
{ "VOLUME_UP", 24 },
{ "VOLUME_DOWN", 25 },
{ "POWER", 26 },
{ "CAMERA", 27 },
{ "CLEAR", 28 },
{ "A", 29 },
{ "B", 30 },
{ "C", 31 },
{ "D", 32 },
{ "E", 33 },
{ "F", 34 },
{ "G", 35 },
{ "H", 36 },
{ "I", 37 },
{ "J", 38 },
{ "K", 39 },
{ "L", 40 },
{ "M", 41 },
{ "N", 42 },
{ "O", 43 },
{ "P", 44 },
{ "Q", 45 },
{ "R", 46 },
{ "S", 47 },
{ "T", 48 },
{ "U", 49 },
{ "V", 50 },
{ "W", 51 },
{ "X", 52 },
{ "Y", 53 },
{ "Z", 54 },
{ "COMMA", 55 },
{ "PERIOD", 56 },
{ "ALT_LEFT", 57 },
{ "ALT_RIGHT", 58 },
{ "SHIFT_LEFT", 59 },
{ "SHIFT_RIGHT", 60 },
{ "TAB", 61 },
{ "SPACE", 62 },
{ "SYM", 63 },
{ "EXPLORER", 64 },
{ "ENVELOPE", 65 },
{ "ENTER", 66 },
{ "DEL", 67 },
{ "GRAVE", 68 },
{ "MINUS", 69 },
{ "EQUALS", 70 },
{ "LEFT_BRACKET", 71 },
{ "RIGHT_BRACKET", 72 },
{ "BACKSLASH", 73 },
{ "SEMICOLON", 74 },
{ "APOSTROPHE", 75 },
{ "SLASH", 76 },
{ "AT", 77 },
{ "NUM", 78 },
{ "HEADSETHOOK", 79 },
{ "FOCUS", 80 },
{ "PLUS", 81 },
{ "MENU", 82 },
{ "NOTIFICATION", 83 },
{ "SEARCH", 84 },
{ "MEDIA_PLAY_PAUSE", 85 },
{ "MEDIA_STOP", 86 },
{ "MEDIA_NEXT", 87 },
{ "MEDIA_PREVIOUS", 88 },
{ "MEDIA_REWIND", 89 },
{ "MEDIA_FAST_FORWARD", 90 },
{ "MUTE", 91 },
{ "PAGE_UP", 92 },
{ "PAGE_DOWN", 93 },
{ "PICTSYMBOLS", 94 },
{ "SWITCH_CHARSET", 95 },
{ "BUTTON_A", 96 },
{ "BUTTON_B", 97 },
{ "BUTTON_C", 98 },
{ "BUTTON_X", 99 },
{ "BUTTON_Y", 100 },
{ "BUTTON_Z", 101 },
{ "BUTTON_L1", 102 },
{ "BUTTON_R1", 103 },
{ "BUTTON_L2", 104 },
{ "BUTTON_R2", 105 },
{ "BUTTON_THUMBL", 106 },
{ "BUTTON_THUMBR", 107 },
{ "BUTTON_START", 108 },
{ "BUTTON_SELECT", 109 },
{ "BUTTON_MODE", 110 },
{ "ESCAPE", 111 },
{ "FORWARD_DEL", 112 },
{ "CTRL_LEFT", 113 },
{ "CTRL_RIGHT", 114 },
{ "CAPS_LOCK", 115 },
{ "SCROLL_LOCK", 116 },
{ "META_LEFT", 117 },
{ "META_RIGHT", 118 },
{ "FUNCTION", 119 },
{ "SYSRQ", 120 },
{ "BREAK", 121 },
{ "MOVE_HOME", 122 },
{ "MOVE_END", 123 },
{ "INSERT", 124 },
{ "FORWARD", 125 },
{ "MEDIA_PLAY", 126 },
{ "MEDIA_PAUSE", 127 },
{ "MEDIA_CLOSE", 128 },
{ "MEDIA_EJECT", 129 },
{ "MEDIA_RECORD", 130 },
{ "F1", 131 },
{ "F2", 132 },
{ "F3", 133 },
{ "F4", 134 },
{ "F5", 135 },
{ "F6", 136 },
{ "F7", 137 },
{ "F8", 138 },
{ "F9", 139 },
{ "F10", 140 },
{ "F11", 141 },
{ "F12", 142 },
{ "NUM_LOCK", 143 },
{ "NUMPAD_0", 144 },
{ "NUMPAD_1", 145 },
{ "NUMPAD_2", 146 },
{ "NUMPAD_3", 147 },
{ "NUMPAD_4", 148 },
{ "NUMPAD_5", 149 },
{ "NUMPAD_6", 150 },
{ "NUMPAD_7", 151 },
{ "NUMPAD_8", 152 },
{ "NUMPAD_9", 153 },
{ "NUMPAD_DIVIDE", 154 },
{ "NUMPAD_MULTIPLY", 155 },
{ "NUMPAD_SUBTRACT", 156 },
{ "NUMPAD_ADD", 157 },
{ "NUMPAD_DOT", 158 },
{ "NUMPAD_COMMA", 159 },
{ "NUMPAD_ENTER", 160 },
{ "NUMPAD_EQUALS", 161 },
{ "NUMPAD_LEFT_PAREN", 162 },
{ "NUMPAD_RIGHT_PAREN", 163 },
{ "VOLUME_MUTE", 164 },
{ "INFO", 165 },
{ "CHANNEL_UP", 166 },
{ "CHANNEL_DOWN", 167 },
{ "ZOOM_IN", 168 },
{ "ZOOM_OUT", 169 },
{ "TV", 170 },
{ "WINDOW", 171 },
{ "GUIDE", 172 },
{ "DVR", 173 },
{ "BOOKMARK", 174 },
{ "CAPTIONS", 175 },
{ "SETTINGS", 176 },
{ "TV_POWER", 177 },
{ "TV_INPUT", 178 },
{ "STB_POWER", 179 },
{ "STB_INPUT", 180 },
{ "AVR_POWER", 181 },
{ "AVR_INPUT", 182 },
{ "PROG_RED", 183 },
{ "PROG_GREEN", 184 },
{ "PROG_YELLOW", 185 },
{ "PROG_BLUE", 186 },
{ "APP_SWITCH", 187 },
{ "BUTTON_1", 188 },
{ "BUTTON_2", 189 },
{ "BUTTON_3", 190 },
{ "BUTTON_4", 191 },
{ "BUTTON_5", 192 },
{ "BUTTON_6", 193 },
{ "BUTTON_7", 194 },
{ "BUTTON_8", 195 },
{ "BUTTON_9", 196 },
{ "BUTTON_10", 197 },
{ "BUTTON_11", 198 },
{ "BUTTON_12", 199 },
{ "BUTTON_13", 200 },
{ "BUTTON_14", 201 },
{ "BUTTON_15", 202 },
{ "BUTTON_16", 203 },
{ "LANGUAGE_SWITCH", 204 },
{ "MANNER_MODE", 205 },
{ "3D_MODE", 206 },
{ "CONTACTS", 207 },
{ "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 },
{ "MEDIA_AUDIO_TRACK", 222 },
{ "SLEEP", 223 },
{ "WAKEUP", 224 },
{ "PAIRING", 225 },
{ "MEDIA_TOP_MENU", 226 },
{ "11", 227 },
{ "12", 228 },
{ "LAST_CHANNEL", 229 },
{ "TV_DATA_SERVICE", 230 },
{ "VOICE_ASSIST", 231 },
{ "TV_RADIO_SERVICE", 232 },
{ "TV_TELETEXT", 233 },
{ "TV_NUMBER_ENTRY", 234 },
{ "TV_TERRESTRIAL_ANALOG", 235 },
{ "TV_TERRESTRIAL_DIGITAL", 236 },
{ "TV_SATELLITE", 237 },
{ "TV_SATELLITE_BS", 238 },
{ "TV_SATELLITE_CS", 239 },
{ "TV_SATELLITE_SERVICE", 240 },
{ "TV_NETWORK", 241 },
{ "TV_ANTENNA_CABLE", 242 },
{ "TV_INPUT_HDMI_1", 243 },
{ "TV_INPUT_HDMI_2", 244 },
{ "TV_INPUT_HDMI_3", 245 },
{ "TV_INPUT_HDMI_4", 246 },
{ "TV_INPUT_COMPOSITE_1", 247 },
{ "TV_INPUT_COMPOSITE_2", 248 },
{ "TV_INPUT_COMPONENT_1", 249 },
{ "TV_INPUT_COMPONENT_2", 250 },
{ "TV_INPUT_VGA_1", 251 },
{ "TV_AUDIO_DESCRIPTION", 252 },
{ "TV_AUDIO_DESCRIPTION_MIX_UP", 253 },
{ "TV_AUDIO_DESCRIPTION_MIX_DOWN", 254 },
{ "TV_ZOOM_MODE", 255 },
{ "TV_CONTENTS_MENU", 256 },
{ "TV_MEDIA_CONTEXT_MENU", 257 },
{ "TV_TIMER_PROGRAMMING", 258 },
{ "HELP", 259 },
// 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.
{ NULL, 0 }
};
// NOTE: If you edit these flags, also edit policy flags in Input.h.
static const KeycodeLabel FLAGS[] = {
{ "WAKE", 0x00000001 },
{ "WAKE_DROPPED", 0x00000002 },
{ "SHIFT", 0x00000004 },
{ "CAPS_LOCK", 0x00000008 },
{ "ALT", 0x00000010 },
{ "ALT_GR", 0x00000020 },
{ "MENU", 0x00000040 },
{ "LAUNCHER", 0x00000080 },
{ "VIRTUAL", 0x00000100 },
{ "FUNCTION", 0x00000200 },
{ NULL, 0 }
};
static const KeycodeLabel AXES[] = {
{ "X", 0 },
{ "Y", 1 },
{ "PRESSURE", 2 },
{ "SIZE", 3 },
{ "TOUCH_MAJOR", 4 },
{ "TOUCH_MINOR", 5 },
{ "TOOL_MAJOR", 6 },
{ "TOOL_MINOR", 7 },
{ "ORIENTATION", 8 },
{ "VSCROLL", 9 },
{ "HSCROLL", 10 },
{ "Z", 11 },
{ "RX", 12 },
{ "RY", 13 },
{ "RZ", 14 },
{ "HAT_X", 15 },
{ "HAT_Y", 16 },
{ "LTRIGGER", 17 },
{ "RTRIGGER", 18 },
{ "THROTTLE", 19 },
{ "RUDDER", 20 },
{ "WHEEL", 21 },
{ "GAS", 22 },
{ "BRAKE", 23 },
{ "DISTANCE", 24 },
{ "TILT", 25 },
{ "GENERIC_1", 32 },
{ "GENERIC_2", 33 },
{ "GENERIC_3", 34 },
{ "GENERIC_4", 35 },
{ "GENERIC_5", 36 },
{ "GENERIC_6", 37 },
{ "GENERIC_7", 38 },
{ "GENERIC_8", 39 },
{ "GENERIC_9", 40 },
{ "GENERIC_10", 41 },
{ "GENERIC_11", 42 },
{ "GENERIC_12", 43 },
{ "GENERIC_13", 44 },
{ "GENERIC_14", 45 },
{ "GENERIC_15", 46 },
{ "GENERIC_16", 47 },
// NOTE: If you add a new axis here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
{ NULL, -1 }
};
static const KeycodeLabel LEDS[] = {
{ "NUM_LOCK", 1},
{ "CAPS_LOCK", 2},
{ "SCROLL_LOCK", 3},
{ "COMPOSE", 4},
{ "KANA", 5},
{ "SLEEP", 6},
{ "SUSPEND", 7},
{ "MUTE", 8},
{ "MISC", 9},
{ "MAIL", 10},
{ "CHARGING", 11},
{ "CONTROLLER_1", 12},
{ "CONTROLLER_2", 13},
{ "CONTROLLER_3", 14},
{ "CONTROLLER_4", 15},
// NOTE: If you add new LEDs here, you must also add them to Input.h
{ NULL, 0 }
};
#endif // _ANDROIDFW_KEYCODE_LABELS_H

View file

@ -0,0 +1,604 @@
/*
* 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 "PointerController"
//#define LOG_NDEBUG 0
// Log debug messages about pointer updates
#define DEBUG_POINTER_UPDATES 0
#include "PointerController.h"
#include "cutils_log.h"
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkPaint.h>
#include <SkXfermode.h>
namespace android {
// --- PointerController ---
// Time to wait before starting the fade when the pointer is inactive.
static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds
// Time to wait between animation frames.
static const nsecs_t ANIMATION_FRAME_INTERVAL = 1000000000LL / 60;
// Time to spend fading out the spot completely.
static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
// Time to spend fading out the pointer completely.
static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
// --- PointerController ---
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
mHandler = new WeakMessageHandler(this);
AutoMutex _l(mLock);
mLocked.animationPending = false;
mLocked.displayWidth = -1;
mLocked.displayHeight = -1;
mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
mLocked.presentation = PRESENTATION_POINTER;
mLocked.presentationChanged = false;
mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL;
mLocked.pointerFadeDirection = 0;
mLocked.pointerX = 0;
mLocked.pointerY = 0;
mLocked.pointerAlpha = 0.0f; // pointer is initially faded
mLocked.pointerSprite = mSpriteController->createSprite();
mLocked.pointerIconChanged = false;
mLocked.buttonState = 0;
loadResources();
}
PointerController::~PointerController() {
mLooper->removeMessages(mHandler);
AutoMutex _l(mLock);
mLocked.pointerSprite.clear();
for (size_t i = 0; i < mLocked.spots.size(); i++) {
delete mLocked.spots.itemAt(i);
}
mLocked.spots.clear();
mLocked.recycledSprites.clear();
}
bool PointerController::getBounds(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const {
AutoMutex _l(mLock);
return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY);
}
bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const {
if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
return false;
}
*outMinX = 0;
*outMinY = 0;
switch (mLocked.displayOrientation) {
case DISPLAY_ORIENTATION_90:
case DISPLAY_ORIENTATION_270:
*outMaxX = mLocked.displayHeight - 1;
*outMaxY = mLocked.displayWidth - 1;
break;
default:
*outMaxX = mLocked.displayWidth - 1;
*outMaxY = mLocked.displayHeight - 1;
break;
}
return true;
}
void PointerController::move(float deltaX, float deltaY) {
#if DEBUG_POINTER_UPDATES
ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
#endif
if (deltaX == 0.0f && deltaY == 0.0f) {
return;
}
AutoMutex _l(mLock);
setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
}
void PointerController::setButtonState(int32_t buttonState) {
#if DEBUG_POINTER_UPDATES
ALOGD("Set button state 0x%08x", buttonState);
#endif
AutoMutex _l(mLock);
if (mLocked.buttonState != buttonState) {
mLocked.buttonState = buttonState;
}
}
int32_t PointerController::getButtonState() const {
AutoMutex _l(mLock);
return mLocked.buttonState;
}
void PointerController::setPosition(float x, float y) {
#if DEBUG_POINTER_UPDATES
ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
#endif
AutoMutex _l(mLock);
setPositionLocked(x, y);
}
void PointerController::setPositionLocked(float x, float y) {
float minX, minY, maxX, maxY;
if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
if (x <= minX) {
mLocked.pointerX = minX;
} else if (x >= maxX) {
mLocked.pointerX = maxX;
} else {
mLocked.pointerX = x;
}
if (y <= minY) {
mLocked.pointerY = minY;
} else if (y >= maxY) {
mLocked.pointerY = maxY;
} else {
mLocked.pointerY = y;
}
updatePointerLocked();
}
}
void PointerController::getPosition(float* outX, float* outY) const {
AutoMutex _l(mLock);
*outX = mLocked.pointerX;
*outY = mLocked.pointerY;
}
void PointerController::fade(Transition transition) {
AutoMutex _l(mLock);
// Remove the inactivity timeout, since we are fading now.
removeInactivityTimeoutLocked();
// Start fading.
if (transition == TRANSITION_IMMEDIATE) {
mLocked.pointerFadeDirection = 0;
mLocked.pointerAlpha = 0.0f;
updatePointerLocked();
} else {
mLocked.pointerFadeDirection = -1;
startAnimationLocked();
}
}
void PointerController::unfade(Transition transition) {
AutoMutex _l(mLock);
// Always reset the inactivity timer.
resetInactivityTimeoutLocked();
// Start unfading.
if (transition == TRANSITION_IMMEDIATE) {
mLocked.pointerFadeDirection = 0;
mLocked.pointerAlpha = 1.0f;
updatePointerLocked();
} else {
mLocked.pointerFadeDirection = 1;
startAnimationLocked();
}
}
void PointerController::setPresentation(Presentation presentation) {
AutoMutex _l(mLock);
if (mLocked.presentation != presentation) {
mLocked.presentation = presentation;
mLocked.presentationChanged = true;
if (presentation != PRESENTATION_SPOT) {
fadeOutAndReleaseAllSpotsLocked();
}
updatePointerLocked();
}
}
void PointerController::setSpots(const PointerCoords* spotCoords,
const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
#if DEBUG_POINTER_UPDATES
ALOGD("setSpots: idBits=%08x", spotIdBits.value);
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
uint32_t id = idBits.firstMarkedBit();
idBits.clearBit(id);
const PointerCoords& c = spotCoords[spotIdToIndex[id]];
ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
c.getAxisValue(AMOTION_EVENT_AXIS_X),
c.getAxisValue(AMOTION_EVENT_AXIS_Y),
c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
}
#endif
AutoMutex _l(mLock);
mSpriteController->openTransaction();
// Add or move spots for fingers that are down.
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
uint32_t id = idBits.clearFirstMarkedBit();
const PointerCoords& c = spotCoords[spotIdToIndex[id]];
const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
? mResources.spotTouch : mResources.spotHover;
float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
Spot* spot = getSpotLocked(id);
if (!spot) {
spot = createAndAddSpotLocked(id);
}
spot->updateSprite(&icon, x, y);
}
// Remove spots for fingers that went up.
for (size_t i = 0; i < mLocked.spots.size(); i++) {
Spot* spot = mLocked.spots.itemAt(i);
if (spot->id != Spot::INVALID_ID
&& !spotIdBits.hasBit(spot->id)) {
fadeOutAndReleaseSpotLocked(spot);
}
}
mSpriteController->closeTransaction();
}
void PointerController::clearSpots() {
#if DEBUG_POINTER_UPDATES
ALOGD("clearSpots");
#endif
AutoMutex _l(mLock);
fadeOutAndReleaseAllSpotsLocked();
}
void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) {
AutoMutex _l(mLock);
if (mLocked.inactivityTimeout != inactivityTimeout) {
mLocked.inactivityTimeout = inactivityTimeout;
resetInactivityTimeoutLocked();
}
}
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;
float minX, minY, maxX, maxY;
if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
mLocked.pointerX = (minX + maxX) * 0.5f;
mLocked.pointerY = (minY + maxY) * 0.5f;
} else {
mLocked.pointerX = 0;
mLocked.pointerY = 0;
}
fadeOutAndReleaseAllSpotsLocked();
}
if (mLocked.displayOrientation != orientation) {
// Apply offsets to convert from the pixel top-left corner position to the pixel center.
// This creates an invariant frame of reference that we can easily rotate when
// taking into account that the pointer may be located at fractional pixel offsets.
float x = mLocked.pointerX + 0.5f;
float y = mLocked.pointerY + 0.5f;
float temp;
// Undo the previous rotation.
switch (mLocked.displayOrientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
x = mLocked.displayWidth - y;
y = temp;
break;
case DISPLAY_ORIENTATION_180:
x = mLocked.displayWidth - x;
y = mLocked.displayHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
x = y;
y = mLocked.displayHeight - temp;
break;
}
// Perform the new rotation.
switch (orientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
x = y;
y = mLocked.displayWidth - temp;
break;
case DISPLAY_ORIENTATION_180:
x = mLocked.displayWidth - x;
y = mLocked.displayHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
x = mLocked.displayHeight - y;
y = temp;
break;
}
// Apply offsets to convert from the pixel center to the pixel top-left corner position
// and save the results.
mLocked.pointerX = x - 0.5f;
mLocked.pointerY = y - 0.5f;
mLocked.displayOrientation = orientation;
}
updatePointerLocked();
}
void PointerController::setPointerIcon(const SpriteIcon& icon) {
AutoMutex _l(mLock);
mLocked.pointerIcon = icon.copy();
mLocked.pointerIconChanged = true;
updatePointerLocked();
}
void PointerController::handleMessage(const Message& message) {
switch (message.what) {
case MSG_ANIMATE:
doAnimate();
break;
case MSG_INACTIVITY_TIMEOUT:
doInactivityTimeout();
break;
}
}
void PointerController::doAnimate() {
AutoMutex _l(mLock);
bool keepAnimating = false;
mLocked.animationPending = false;
nsecs_t frameDelay = systemTime(SYSTEM_TIME_MONOTONIC) - mLocked.animationTime;
// Animate pointer fade.
if (mLocked.pointerFadeDirection < 0) {
mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
if (mLocked.pointerAlpha <= 0.0f) {
mLocked.pointerAlpha = 0.0f;
mLocked.pointerFadeDirection = 0;
} else {
keepAnimating = true;
}
updatePointerLocked();
} else if (mLocked.pointerFadeDirection > 0) {
mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
if (mLocked.pointerAlpha >= 1.0f) {
mLocked.pointerAlpha = 1.0f;
mLocked.pointerFadeDirection = 0;
} else {
keepAnimating = true;
}
updatePointerLocked();
}
// Animate spots that are fading out and being removed.
for (size_t i = 0; i < mLocked.spots.size(); i++) {
Spot* spot = mLocked.spots.itemAt(i);
if (spot->id == Spot::INVALID_ID) {
spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
if (spot->alpha <= 0) {
mLocked.spots.removeAt(i--);
releaseSpotLocked(spot);
} else {
spot->sprite->setAlpha(spot->alpha);
keepAnimating = true;
}
}
}
if (keepAnimating) {
startAnimationLocked();
}
}
void PointerController::doInactivityTimeout() {
fade(TRANSITION_GRADUAL);
}
void PointerController::startAnimationLocked() {
if (!mLocked.animationPending) {
mLocked.animationPending = true;
mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
mLooper->sendMessageDelayed(ANIMATION_FRAME_INTERVAL, mHandler, Message(MSG_ANIMATE));
}
}
void PointerController::resetInactivityTimeoutLocked() {
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT
? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
}
void PointerController::removeInactivityTimeoutLocked() {
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
}
void PointerController::updatePointerLocked() {
mSpriteController->openTransaction();
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
if (mLocked.pointerAlpha > 0) {
mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
mLocked.pointerSprite->setVisible(true);
} else {
mLocked.pointerSprite->setVisible(false);
}
if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
mLocked.pointerSprite->setIcon(mLocked.presentation == PRESENTATION_POINTER
? mLocked.pointerIcon : mResources.spotAnchor);
mLocked.pointerIconChanged = false;
mLocked.presentationChanged = false;
}
mSpriteController->closeTransaction();
}
PointerController::Spot* PointerController::getSpotLocked(uint32_t id) {
for (size_t i = 0; i < mLocked.spots.size(); i++) {
Spot* spot = mLocked.spots.itemAt(i);
if (spot->id == id) {
return spot;
}
}
return NULL;
}
PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) {
// Remove spots until we have fewer than MAX_SPOTS remaining.
while (mLocked.spots.size() >= MAX_SPOTS) {
Spot* spot = removeFirstFadingSpotLocked();
if (!spot) {
spot = mLocked.spots.itemAt(0);
mLocked.spots.removeAt(0);
}
releaseSpotLocked(spot);
}
// Obtain a sprite from the recycled pool.
sp<Sprite> sprite;
if (! mLocked.recycledSprites.isEmpty()) {
sprite = mLocked.recycledSprites.top();
mLocked.recycledSprites.pop();
} else {
sprite = mSpriteController->createSprite();
}
// Return the new spot.
Spot* spot = new Spot(id, sprite);
mLocked.spots.push(spot);
return spot;
}
PointerController::Spot* PointerController::removeFirstFadingSpotLocked() {
for (size_t i = 0; i < mLocked.spots.size(); i++) {
Spot* spot = mLocked.spots.itemAt(i);
if (spot->id == Spot::INVALID_ID) {
mLocked.spots.removeAt(i);
return spot;
}
}
return NULL;
}
void PointerController::releaseSpotLocked(Spot* spot) {
spot->sprite->clearIcon();
if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
mLocked.recycledSprites.push(spot->sprite);
}
delete spot;
}
void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) {
if (spot->id != Spot::INVALID_ID) {
spot->id = Spot::INVALID_ID;
startAnimationLocked();
}
}
void PointerController::fadeOutAndReleaseAllSpotsLocked() {
for (size_t i = 0; i < mLocked.spots.size(); i++) {
Spot* spot = mLocked.spots.itemAt(i);
fadeOutAndReleaseSpotLocked(spot);
}
}
void PointerController::loadResources() {
mPolicy->loadPointerResources(&mResources);
}
// --- PointerController::Spot ---
void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) {
sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
sprite->setAlpha(alpha);
sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
sprite->setPosition(x, y);
this->x = x;
this->y = y;
if (icon != lastIcon) {
lastIcon = icon;
if (icon) {
sprite->setIcon(*icon);
sprite->setVisible(true);
} else {
sprite->setVisible(false);
}
}
}
} // namespace android

View file

@ -0,0 +1,266 @@
/*
* 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_POINTER_CONTROLLER_H
#define _UI_POINTER_CONTROLLER_H
#include "SpriteController.h"
#include <ui/DisplayInfo.h>
#include "Input.h"
#include <utils/BitSet.h>
#include <utils/RefBase.h>
#include <utils/Looper.h>
#include <utils/String8.h>
#include <SkBitmap.h>
namespace android {
/**
* Interface for tracking a mouse / touch pad pointer and touch pad spots.
*
* The spots are sprites on screen that visually represent the positions of
* fingers
*
* The pointer controller is responsible for providing synchronization and for tracking
* display orientation changes if needed.
*/
class PointerControllerInterface : public virtual RefBase {
protected:
PointerControllerInterface() { }
virtual ~PointerControllerInterface() { }
public:
/* Gets the bounds of the region that the pointer can traverse.
* Returns true if the bounds are available. */
virtual bool getBounds(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const = 0;
/* Move the pointer. */
virtual void move(float deltaX, float deltaY) = 0;
/* Sets a mask that indicates which buttons are pressed. */
virtual void setButtonState(int32_t buttonState) = 0;
/* Gets a mask that indicates which buttons are pressed. */
virtual int32_t getButtonState() const = 0;
/* Sets the absolute location of the pointer. */
virtual void setPosition(float x, float y) = 0;
/* Gets the absolute location of the pointer. */
virtual void getPosition(float* outX, float* outY) const = 0;
enum Transition {
// Fade/unfade immediately.
TRANSITION_IMMEDIATE,
// Fade/unfade gradually.
TRANSITION_GRADUAL,
};
/* Fades the pointer out now. */
virtual void fade(Transition transition) = 0;
/* Makes the pointer visible if it has faded out.
* The pointer never unfades itself automatically. This method must be called
* by the client whenever the pointer is moved or a button is pressed and it
* wants to ensure that the pointer becomes visible again. */
virtual void unfade(Transition transition) = 0;
enum Presentation {
// Show the mouse pointer.
PRESENTATION_POINTER,
// Show spots and a spot anchor in place of the mouse pointer.
PRESENTATION_SPOT,
};
/* Sets the mode of the pointer controller. */
virtual void setPresentation(Presentation presentation) = 0;
/* Sets the spots for the current gesture.
* The spots are not subject to the inactivity timeout like the pointer
* itself it since they are expected to remain visible for so long as
* the fingers are on the touch pad.
*
* The values of the AMOTION_EVENT_AXIS_PRESSURE axis is significant.
* For spotCoords, pressure != 0 indicates that the spot's location is being
* pressed (not hovering).
*/
virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
BitSet32 spotIdBits) = 0;
/* Removes all spots. */
virtual void clearSpots() = 0;
};
/*
* Pointer resources.
*/
struct PointerResources {
SpriteIcon spotHover;
SpriteIcon spotTouch;
SpriteIcon spotAnchor;
};
/*
* Pointer controller policy interface.
*
* The pointer controller policy is used by the pointer controller to interact with
* the Window Manager and other system components.
*
* The actual implementation is partially supported by callbacks into the DVM
* via JNI. This interface is also mocked in the unit tests.
*/
class PointerControllerPolicyInterface : public virtual RefBase {
protected:
PointerControllerPolicyInterface() { }
virtual ~PointerControllerPolicyInterface() { }
public:
virtual void loadPointerResources(PointerResources* outResources) = 0;
};
/*
* Tracks pointer movements and draws the pointer sprite to a surface.
*
* Handles pointer acceleration and animation.
*/
class PointerController : public PointerControllerInterface, public MessageHandler {
protected:
virtual ~PointerController();
public:
enum InactivityTimeout {
INACTIVITY_TIMEOUT_NORMAL = 0,
INACTIVITY_TIMEOUT_SHORT = 1,
};
PointerController(const sp<PointerControllerPolicyInterface>& policy,
const sp<Looper>& looper, const sp<SpriteController>& spriteController);
virtual bool getBounds(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const;
virtual void move(float deltaX, float deltaY);
virtual void setButtonState(int32_t buttonState);
virtual int32_t getButtonState() const;
virtual void setPosition(float x, float y);
virtual void getPosition(float* outX, float* outY) const;
virtual void fade(Transition transition);
virtual void unfade(Transition transition);
virtual void setPresentation(Presentation presentation);
virtual void setSpots(const PointerCoords* spotCoords,
const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
virtual void clearSpots();
void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
void setPointerIcon(const SpriteIcon& icon);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
private:
static const size_t MAX_RECYCLED_SPRITES = 12;
static const size_t MAX_SPOTS = 12;
enum {
MSG_ANIMATE,
MSG_INACTIVITY_TIMEOUT,
};
struct Spot {
static const uint32_t INVALID_ID = 0xffffffff;
uint32_t id;
sp<Sprite> sprite;
float alpha;
float scale;
float x, y;
inline Spot(uint32_t id, const sp<Sprite>& sprite)
: id(id), sprite(sprite), alpha(1.0f), scale(1.0f),
x(0.0f), y(0.0f), lastIcon(NULL) { }
void updateSprite(const SpriteIcon* icon, float x, float y);
private:
const SpriteIcon* lastIcon;
};
mutable Mutex mLock;
sp<PointerControllerPolicyInterface> mPolicy;
sp<Looper> mLooper;
sp<SpriteController> mSpriteController;
sp<WeakMessageHandler> mHandler;
PointerResources mResources;
struct Locked {
bool animationPending;
nsecs_t animationTime;
int32_t displayWidth;
int32_t displayHeight;
int32_t displayOrientation;
InactivityTimeout inactivityTimeout;
Presentation presentation;
bool presentationChanged;
int32_t pointerFadeDirection;
float pointerX;
float pointerY;
float pointerAlpha;
sp<Sprite> pointerSprite;
SpriteIcon pointerIcon;
bool pointerIconChanged;
int32_t buttonState;
Vector<Spot*> spots;
Vector<sp<Sprite> > recycledSprites;
} mLocked;
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
void setPositionLocked(float x, float y);
void handleMessage(const Message& message);
void doAnimate();
void doInactivityTimeout();
void startAnimationLocked();
void resetInactivityTimeoutLocked();
void removeInactivityTimeoutLocked();
void updatePointerLocked();
Spot* getSpotLocked(uint32_t id);
Spot* createAndAddSpotLocked(uint32_t id);
Spot* removeFirstFadingSpotLocked();
void releaseSpotLocked(Spot* spot);
void fadeOutAndReleaseSpotLocked(Spot* spot);
void fadeOutAndReleaseAllSpotsLocked();
void loadResources();
};
} // namespace android
#endif // _UI_POINTER_CONTROLLER_H

View file

@ -0,0 +1,33 @@
/*
* 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 _ANDROIDFW_POWER_MANAGER_H
#define _ANDROIDFW_POWER_MANAGER_H
namespace android {
enum {
USER_ACTIVITY_EVENT_OTHER = 0,
USER_ACTIVITY_EVENT_BUTTON = 1,
USER_ACTIVITY_EVENT_TOUCH = 2,
USER_ACTIVITY_EVENT_LAST = USER_ACTIVITY_EVENT_TOUCH, // Last valid event code.
};
} // namespace android
#endif // _ANDROIDFW_POWER_MANAGER_H

View file

@ -0,0 +1,515 @@
/*
* Copyright (C) 2011 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 "Sprites"
//#define LOG_NDEBUG 0
#include "SpriteController.h"
#include "cutils_log.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 {
// --- SpriteController ---
SpriteController::SpriteController(const sp<Looper>& looper, int32_t overlayLayer) :
mLooper(looper), mOverlayLayer(overlayLayer) {
#ifdef HAVE_ANDROID_OS
mHandler = new WeakMessageHandler(this);
#endif
mLocked.transactionNestingCount = 0;
mLocked.deferredSpriteUpdate = false;
}
SpriteController::~SpriteController() {
#ifdef HAVE_ANDROID_OS
mLooper->removeMessages(mHandler);
if (mSurfaceComposerClient != NULL) {
mSurfaceComposerClient->dispose();
mSurfaceComposerClient.clear();
}
#endif
}
sp<Sprite> SpriteController::createSprite() {
return new SpriteImpl(this);
}
void SpriteController::openTransaction() {
AutoMutex _l(mLock);
mLocked.transactionNestingCount += 1;
}
void SpriteController::closeTransaction() {
AutoMutex _l(mLock);
LOG_ALWAYS_FATAL_IF(mLocked.transactionNestingCount == 0,
"Sprite closeTransaction() called but there is no open sprite transaction");
mLocked.transactionNestingCount -= 1;
if (mLocked.transactionNestingCount == 0 && mLocked.deferredSpriteUpdate) {
mLocked.deferredSpriteUpdate = false;
#ifdef HAVE_ANDROID_OS
mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
#endif
}
}
void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) {
bool wasEmpty = mLocked.invalidatedSprites.isEmpty();
mLocked.invalidatedSprites.push(sprite);
if (wasEmpty) {
if (mLocked.transactionNestingCount != 0) {
mLocked.deferredSpriteUpdate = true;
} else {
#ifdef HAVE_ANDROID_OS
mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
#endif
}
}
}
#ifdef HAVE_ANDROID_OS
void SpriteController::disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl) {
bool wasEmpty = mLocked.disposedSurfaces.isEmpty();
mLocked.disposedSurfaces.push(surfaceControl);
if (wasEmpty) {
mLooper->sendMessage(mHandler, Message(MSG_DISPOSE_SURFACES));
}
}
void SpriteController::handleMessage(const Message& message) {
switch (message.what) {
case MSG_UPDATE_SPRITES:
doUpdateSprites();
break;
case MSG_DISPOSE_SURFACES:
doDisposeSurfaces();
break;
}
}
#endif
void SpriteController::doUpdateSprites() {
// Collect information about sprite updates.
// Each sprite update record includes a reference to its associated sprite so we can
// be certain the sprites will not be deleted while this function runs. Sprites
// may invalidate themselves again during this time but we will handle those changes
// in the next iteration.
Vector<SpriteUpdate> updates;
size_t numSprites;
{ // acquire lock
AutoMutex _l(mLock);
numSprites = mLocked.invalidatedSprites.size();
for (size_t i = 0; i < numSprites; i++) {
const sp<SpriteImpl>& sprite = mLocked.invalidatedSprites.itemAt(i);
updates.push(SpriteUpdate(sprite, sprite->getStateLocked()));
sprite->resetDirtyLocked();
}
mLocked.invalidatedSprites.clear();
} // release lock
// Create missing surfaces.
bool surfaceChanged = false;
#ifdef HAVE_ANDROID_OS
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) {
update.state.surfaceWidth = update.state.icon.bitmap.width();
update.state.surfaceHeight = update.state.icon.bitmap.height();
update.state.surfaceDrawn = false;
update.state.surfaceVisible = false;
update.state.surfaceControl = obtainSurface(
update.state.surfaceWidth, update.state.surfaceHeight);
if (update.state.surfaceControl != NULL) {
update.surfaceChanged = surfaceChanged = true;
}
}
}
#endif
// Resize sprites if needed, inside a global transaction.
#ifdef HAVE_ANDROID_OS
bool haveGlobalTransaction = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
int32_t desiredWidth = update.state.icon.bitmap.width();
int32_t desiredHeight = update.state.icon.bitmap.height();
if (update.state.surfaceWidth < desiredWidth
|| update.state.surfaceHeight < desiredHeight) {
if (!haveGlobalTransaction) {
SurfaceComposerClient::openGlobalTransaction();
haveGlobalTransaction = true;
}
status_t status = update.state.surfaceControl->setSize(desiredWidth, desiredHeight);
if (status) {
ALOGE("Error %d resizing sprite surface from %dx%d to %dx%d",
status, update.state.surfaceWidth, update.state.surfaceHeight,
desiredWidth, desiredHeight);
} else {
update.state.surfaceWidth = desiredWidth;
update.state.surfaceHeight = desiredHeight;
update.state.surfaceDrawn = false;
update.surfaceChanged = surfaceChanged = true;
if (update.state.surfaceVisible) {
status = update.state.surfaceControl->hide();
if (status) {
ALOGE("Error %d hiding sprite surface after resize.", status);
} else {
update.state.surfaceVisible = false;
}
}
}
}
}
}
#endif
#ifdef HAVE_ANDROID_OS
if (haveGlobalTransaction) {
SurfaceComposerClient::closeGlobalTransaction();
}
#endif
// Redraw sprites if needed.
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
if ((update.state.dirty & DIRTY_BITMAP) && update.state.surfaceDrawn) {
update.state.surfaceDrawn = false;
update.surfaceChanged = surfaceChanged = true;
}
#ifdef HAVE_ANDROID_OS
if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn
&& update.state.wantSurfaceVisible()) {
sp<Surface> surface = update.state.surfaceControl->getSurface();
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 = outBuffer.stride * bytesPerPixel(outBuffer.format);
surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config,
outBuffer.width, outBuffer.height, bpr);
surfaceBitmap.setPixels(outBuffer.bits);
SkCanvas surfaceCanvas(surfaceBitmap);
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
if (outBuffer.width > uint32_t(update.state.icon.bitmap.width())) {
paint.setColor(0); // transparent fill color
surfaceCanvas.drawRectCoords(update.state.icon.bitmap.width(), 0,
outBuffer.width, update.state.icon.bitmap.height(), paint);
}
if (outBuffer.height > uint32_t(update.state.icon.bitmap.height())) {
paint.setColor(0); // transparent fill color
surfaceCanvas.drawRectCoords(0, update.state.icon.bitmap.height(),
outBuffer.width, outBuffer.height, paint);
}
status = surface->unlockAndPost();
if (status) {
ALOGE("Error %d unlocking and posting sprite surface after drawing.", status);
} else {
update.state.surfaceDrawn = true;
update.surfaceChanged = surfaceChanged = true;
}
}
}
#endif
}
#ifdef HAVE_ANDROID_OS
// Set sprite surface properties and make them visible.
bool haveTransaction = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
bool wantSurfaceVisibleAndDrawn = update.state.wantSurfaceVisible()
&& update.state.surfaceDrawn;
bool becomingVisible = wantSurfaceVisibleAndDrawn && !update.state.surfaceVisible;
bool becomingHidden = !wantSurfaceVisibleAndDrawn && update.state.surfaceVisible;
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
| DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
status_t status;
if (!haveTransaction) {
SurfaceComposerClient::openGlobalTransaction();
haveTransaction = true;
}
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible || (update.state.dirty & DIRTY_ALPHA))) {
status = update.state.surfaceControl->setAlpha(update.state.alpha);
if (status) {
ALOGE("Error %d setting sprite surface alpha.", status);
}
}
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible || (update.state.dirty & (DIRTY_POSITION
| DIRTY_HOTSPOT)))) {
status = update.state.surfaceControl->setPosition(
update.state.positionX - update.state.icon.hotSpotX,
update.state.positionY - update.state.icon.hotSpotY);
if (status) {
ALOGE("Error %d setting sprite surface position.", status);
}
}
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible
|| (update.state.dirty & DIRTY_TRANSFORMATION_MATRIX))) {
status = update.state.surfaceControl->setMatrix(
update.state.transformationMatrix.dsdx,
update.state.transformationMatrix.dtdx,
update.state.transformationMatrix.dsdy,
update.state.transformationMatrix.dtdy);
if (status) {
ALOGE("Error %d setting sprite surface transformation matrix.", status);
}
}
int32_t surfaceLayer = mOverlayLayer + update.state.layer;
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
status = update.state.surfaceControl->setLayer(surfaceLayer);
if (status) {
ALOGE("Error %d setting sprite surface layer.", status);
}
}
if (becomingVisible) {
status = update.state.surfaceControl->show();
if (status) {
ALOGE("Error %d showing sprite surface.", status);
} else {
update.state.surfaceVisible = true;
update.surfaceChanged = surfaceChanged = true;
}
} else if (becomingHidden) {
status = update.state.surfaceControl->hide();
if (status) {
ALOGE("Error %d hiding sprite surface.", status);
} else {
update.state.surfaceVisible = false;
update.surfaceChanged = surfaceChanged = true;
}
}
}
}
#endif
#ifdef HAVE_ANDROID_OS
if (haveTransaction) {
SurfaceComposerClient::closeGlobalTransaction();
}
#endif
#ifdef HAVE_ANDROID_OS
// If any surfaces were changed, write back the new surface properties to the sprites.
if (surfaceChanged) { // acquire lock
AutoMutex _l(mLock);
for (size_t i = 0; i < numSprites; i++) {
const SpriteUpdate& update = updates.itemAt(i);
if (update.surfaceChanged) {
update.sprite->setSurfaceLocked(update.state.surfaceControl,
update.state.surfaceWidth, update.state.surfaceHeight,
update.state.surfaceDrawn, update.state.surfaceVisible);
}
}
} // release lock
#endif
// Clear the sprite update vector outside the lock. It is very important that
// we do not clear sprite references inside the lock since we could be releasing
// the last remaining reference to the sprite here which would result in the
// sprite being deleted and the lock being reacquired by the sprite destructor
// while already held.
updates.clear();
}
void SpriteController::doDisposeSurfaces() {
#ifdef HAVE_ANDROID_OS
// Collect disposed surfaces.
Vector<sp<SurfaceControl> > disposedSurfaces;
{ // acquire lock
AutoMutex _l(mLock);
disposedSurfaces = mLocked.disposedSurfaces;
mLocked.disposedSurfaces.clear();
} // release lock
// Release the last reference to each surface outside of the lock.
// We don't want the surfaces to be deleted while we are holding our lock.
disposedSurfaces.clear();
#endif
}
void SpriteController::ensureSurfaceComposerClient() {
#ifdef HAVE_ANDROID_OS
if (mSurfaceComposerClient == NULL) {
mSurfaceComposerClient = new SurfaceComposerClient();
}
#endif
}
#ifdef HAVE_ANDROID_OS
sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height) {
ensureSurfaceComposerClient();
sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface(
String8("Sprite"), width, height, PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eHidden);
if (surfaceControl == NULL || !surfaceControl->isValid()) {
ALOGE("Error creating sprite surface.");
return NULL;
}
return surfaceControl;
}
#endif
// --- SpriteController::SpriteImpl ---
SpriteController::SpriteImpl::SpriteImpl(const sp<SpriteController> controller) :
mController(controller) {
}
SpriteController::SpriteImpl::~SpriteImpl() {
AutoMutex _m(mController->mLock);
#ifdef HAVE_ANDROID_OS
// Let the controller take care of deleting the last reference to sprite
// surfaces so that we do not block the caller on an IPC here.
if (mLocked.state.surfaceControl != NULL) {
mController->disposeSurfaceLocked(mLocked.state.surfaceControl);
mLocked.state.surfaceControl.clear();
}
#endif
}
void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
AutoMutex _l(mController->mLock);
#ifdef HAVE_ANDROID_OS
uint32_t dirty;
if (icon.isValid()) {
icon.bitmap.copyTo(&mLocked.state.icon.bitmap, SkBitmap::kARGB_8888_Config);
if (!mLocked.state.icon.isValid()
|| mLocked.state.icon.hotSpotX != icon.hotSpotX
|| mLocked.state.icon.hotSpotY != icon.hotSpotY) {
mLocked.state.icon.hotSpotX = icon.hotSpotX;
mLocked.state.icon.hotSpotY = icon.hotSpotY;
dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
} else {
dirty = DIRTY_BITMAP;
}
} else if (mLocked.state.icon.isValid()) {
mLocked.state.icon.bitmap.reset();
dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
} else {
return; // setting to invalid icon and already invalid so nothing to do
}
invalidateLocked(dirty);
#endif
}
void SpriteController::SpriteImpl::setVisible(bool visible) {
AutoMutex _l(mController->mLock);
if (mLocked.state.visible != visible) {
mLocked.state.visible = visible;
invalidateLocked(DIRTY_VISIBILITY);
}
}
void SpriteController::SpriteImpl::setPosition(float x, float y) {
AutoMutex _l(mController->mLock);
if (mLocked.state.positionX != x || mLocked.state.positionY != y) {
mLocked.state.positionX = x;
mLocked.state.positionY = y;
invalidateLocked(DIRTY_POSITION);
}
}
void SpriteController::SpriteImpl::setLayer(int32_t layer) {
AutoMutex _l(mController->mLock);
if (mLocked.state.layer != layer) {
mLocked.state.layer = layer;
invalidateLocked(DIRTY_LAYER);
}
}
void SpriteController::SpriteImpl::setAlpha(float alpha) {
AutoMutex _l(mController->mLock);
if (mLocked.state.alpha != alpha) {
mLocked.state.alpha = alpha;
invalidateLocked(DIRTY_ALPHA);
}
}
void SpriteController::SpriteImpl::setTransformationMatrix(
const SpriteTransformationMatrix& matrix) {
AutoMutex _l(mController->mLock);
if (mLocked.state.transformationMatrix != matrix) {
mLocked.state.transformationMatrix = matrix;
invalidateLocked(DIRTY_TRANSFORMATION_MATRIX);
}
}
void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
bool wasDirty = mLocked.state.dirty;
mLocked.state.dirty |= dirty;
if (!wasDirty) {
mController->invalidateSpriteLocked(this);
}
}
} // namespace android

View file

@ -0,0 +1,319 @@
/*
* Copyright (C) 2011 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_SPRITES_H
#define _UI_SPRITES_H
#include <utils/RefBase.h>
#include <utils/Looper.h>
#ifdef HAVE_ANDROID_OS
#include <gui/SurfaceComposerClient.h>
#endif
#include <SkBitmap.h>
namespace android {
/*
* Transformation matrix for a sprite.
*/
struct SpriteTransformationMatrix {
inline SpriteTransformationMatrix() : dsdx(1.0f), dtdx(0.0f), dsdy(0.0f), dtdy(1.0f) { }
inline SpriteTransformationMatrix(float dsdx, float dtdx, float dsdy, float dtdy) :
dsdx(dsdx), dtdx(dtdx), dsdy(dsdy), dtdy(dtdy) { }
float dsdx;
float dtdx;
float dsdy;
float dtdy;
inline bool operator== (const SpriteTransformationMatrix& other) {
return dsdx == other.dsdx
&& dtdx == other.dtdx
&& dsdy == other.dsdy
&& dtdy == other.dtdy;
}
inline bool operator!= (const SpriteTransformationMatrix& other) {
return !(*this == other);
}
};
/*
* Icon that a sprite displays, including its hotspot.
*/
struct SpriteIcon {
inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { }
#ifdef HAVE_ANDROID_OS
inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) :
bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
SkBitmap bitmap;
#endif
float hotSpotX;
float hotSpotY;
inline SpriteIcon copy() const {
#ifdef HAVE_ANDROID_OS
SkBitmap bitmapCopy;
bitmap.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config);
return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
#else
return SpriteIcon();
#endif
}
inline void reset() {
#ifdef HAVE_ANDROID_OS
bitmap.reset();
hotSpotX = 0;
hotSpotY = 0;
#endif
}
inline bool isValid() const {
#ifdef HAVE_ANDROID_OS
return !bitmap.isNull() && !bitmap.empty();
#else
return false;
#endif
}
};
/*
* A sprite is a simple graphical object that is displayed on-screen above other layers.
* The basic sprite class is an interface.
* The implementation is provided by the sprite controller.
*/
class Sprite : public RefBase {
protected:
Sprite() { }
virtual ~Sprite() { }
public:
enum {
// The base layer for pointer sprites.
BASE_LAYER_POINTER = 0, // reserve space for 1 pointer
// The base layer for spot sprites.
BASE_LAYER_SPOT = 1, // reserve space for MAX_POINTER_ID spots
};
/* Sets the bitmap that is drawn by the sprite.
* The sprite retains a copy of the bitmap for subsequent rendering. */
virtual void setIcon(const SpriteIcon& icon) = 0;
inline void clearIcon() {
setIcon(SpriteIcon());
}
/* Sets whether the sprite is visible. */
virtual void setVisible(bool visible) = 0;
/* Sets the sprite position on screen, relative to the sprite's hot spot. */
virtual void setPosition(float x, float y) = 0;
/* Sets the layer of the sprite, relative to the system sprite overlay layer.
* Layer 0 is the overlay layer, > 0 appear above this layer. */
virtual void setLayer(int32_t layer) = 0;
/* Sets the sprite alpha blend ratio between 0.0 and 1.0. */
virtual void setAlpha(float alpha) = 0;
/* Sets the sprite transformation matrix. */
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
};
/*
* Displays sprites on the screen.
*
* This interface is used by PointerController and SpotController to draw pointers or
* spot representations of fingers. It is not intended for general purpose use
* by other components.
*
* All sprite position updates and rendering is performed asynchronously.
*
* Clients are responsible for animating sprites by periodically updating their properties.
*/
class SpriteController : public MessageHandler {
protected:
virtual ~SpriteController();
public:
SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
/* Creates a new sprite, initially invisible. */
sp<Sprite> createSprite();
/* Opens or closes a transaction to perform a batch of sprite updates as part of
* a single operation such as setPosition and setAlpha. It is not necessary to
* open a transaction when updating a single property.
* Calls to openTransaction() nest and must be matched by an equal number
* of calls to closeTransaction(). */
void openTransaction();
void closeTransaction();
private:
enum {
MSG_UPDATE_SPRITES,
MSG_DISPOSE_SURFACES,
};
enum {
DIRTY_BITMAP = 1 << 0,
DIRTY_ALPHA = 1 << 1,
DIRTY_POSITION = 1 << 2,
DIRTY_TRANSFORMATION_MATRIX = 1 << 3,
DIRTY_LAYER = 1 << 4,
DIRTY_VISIBILITY = 1 << 5,
DIRTY_HOTSPOT = 1 << 6,
};
/* Describes the state of a sprite.
* This structure is designed so that it can be copied during updates so that
* surfaces can be resized and redrawn without blocking the client by holding a lock
* on the sprites for a long time.
* Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */
struct SpriteState {
inline SpriteState() :
dirty(0), visible(false),
positionX(0), positionY(0), layer(0), alpha(1.0f),
surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
}
uint32_t dirty;
SpriteIcon icon;
bool visible;
float positionX;
float positionY;
int32_t layer;
float alpha;
SpriteTransformationMatrix transformationMatrix;
#ifdef HAVE_ANDROID_OS
sp<SurfaceControl> surfaceControl;
#endif
int32_t surfaceWidth;
int32_t surfaceHeight;
bool surfaceDrawn;
bool surfaceVisible;
inline bool wantSurfaceVisible() const {
return visible && alpha > 0.0f && icon.isValid();
}
};
/* Client interface for a sprite.
* Requests acquire a lock on the controller, update local state and request the
* controller to invalidate the sprite.
* The real heavy lifting of creating, resizing and redrawing surfaces happens
* asynchronously with no locks held except in short critical section to copy
* the sprite state before the work and update the sprite surface control afterwards.
*/
class SpriteImpl : public Sprite {
protected:
virtual ~SpriteImpl();
public:
SpriteImpl(const sp<SpriteController> controller);
virtual void setIcon(const SpriteIcon& icon);
virtual void setVisible(bool visible);
virtual void setPosition(float x, float y);
virtual void setLayer(int32_t layer);
virtual void setAlpha(float alpha);
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
inline const SpriteState& getStateLocked() const {
return mLocked.state;
}
inline void resetDirtyLocked() {
mLocked.state.dirty = 0;
}
#ifdef HAVE_ANDROID_OS
inline void setSurfaceLocked(const sp<SurfaceControl>& surfaceControl,
int32_t width, int32_t height, bool drawn, bool visible) {
mLocked.state.surfaceControl = surfaceControl;
mLocked.state.surfaceWidth = width;
mLocked.state.surfaceHeight = height;
mLocked.state.surfaceDrawn = drawn;
mLocked.state.surfaceVisible = visible;
}
#endif
private:
sp<SpriteController> mController;
struct Locked {
SpriteState state;
} mLocked; // guarded by mController->mLock
void invalidateLocked(uint32_t dirty);
};
/* Stores temporary information collected during the sprite update cycle. */
struct SpriteUpdate {
inline SpriteUpdate() : surfaceChanged(false) { }
inline SpriteUpdate(const sp<SpriteImpl> sprite, const SpriteState& state) :
sprite(sprite), state(state), surfaceChanged(false) {
}
sp<SpriteImpl> sprite;
SpriteState state;
bool surfaceChanged;
};
mutable Mutex mLock;
sp<Looper> mLooper;
const int32_t mOverlayLayer;
#ifdef HAVE_ANDROID_OS
sp<WeakMessageHandler> mHandler;
sp<SurfaceComposerClient> mSurfaceComposerClient;
#endif
struct Locked {
Vector<sp<SpriteImpl> > invalidatedSprites;
#ifdef HAVE_ANDROID_OS
Vector<sp<SurfaceControl> > disposedSurfaces;
#endif
uint32_t transactionNestingCount;
bool deferredSpriteUpdate;
} mLocked; // guarded by mLock
void invalidateSpriteLocked(const sp<SpriteImpl>& sprite);
#ifdef HAVE_ANDROID_OS
void disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl);
void handleMessage(const Message& message);
#endif
void doUpdateSprites();
void doDisposeSurfaces();
void ensureSurfaceComposerClient();
#ifdef HAVE_ANDROID_OS
sp<SurfaceControl> obtainSurface(int32_t width, int32_t height);
#endif
};
} // namespace android
#endif // _UI_SPRITES_H

View file

@ -0,0 +1,175 @@
/*
* 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 "Tokenizer"
#include "cutils_log.h"
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "Tokenizer.h"
// Enables debug output for the tokenizer.
#define DEBUG_TOKENIZER 0
namespace android {
static inline bool isDelimiter(char ch, const char* delimiters) {
return strchr(delimiters, ch) != NULL;
}
Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,
bool ownBuffer, size_t length) :
mFilename(filename), mFileMap(fileMap),
mBuffer(buffer), mOwnBuffer(ownBuffer), mLength(length),
mCurrent(buffer), mLineNumber(1) {
}
Tokenizer::~Tokenizer() {
if (mFileMap) {
mFileMap->release();
}
if (mOwnBuffer) {
delete[] mBuffer;
}
}
status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
*outTokenizer = NULL;
int result = NO_ERROR;
int fd = ::open(filename.string(), O_RDONLY);
if (fd < 0) {
result = -errno;
ALOGE("Error opening file '%s', %s.", filename.string(), strerror(errno));
} else {
struct stat stat;
if (fstat(fd, &stat)) {
result = -errno;
ALOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno));
} else {
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);
buffer = static_cast<char*>(fileMap->getDataPtr());
} else {
fileMap->release();
fileMap = NULL;
// Fall back to reading into a buffer since we can't mmap files in sysfs.
// 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;
ALOGE("Error reading file '%s', %s.", filename.string(), strerror(errno));
delete[] buffer;
buffer = NULL;
} else {
length = size_t(nrd);
}
}
if (!result) {
*outTokenizer = new Tokenizer(filename, fileMap, buffer, ownBuffer, length);
}
}
close(fd);
}
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);
return result;
}
String8 Tokenizer::peekRemainderOfLine() const {
const char* end = getEnd();
const char* eol = mCurrent;
while (eol != end) {
char ch = *eol;
if (ch == '\n') {
break;
}
eol += 1;
}
return String8(mCurrent, eol - mCurrent);
}
String8 Tokenizer::nextToken(const char* delimiters) {
#if DEBUG_TOKENIZER
ALOGD("nextToken");
#endif
const char* end = getEnd();
const char* tokenStart = mCurrent;
while (mCurrent != end) {
char ch = *mCurrent;
if (ch == '\n' || isDelimiter(ch, delimiters)) {
break;
}
mCurrent += 1;
}
return String8(tokenStart, mCurrent - tokenStart);
}
void Tokenizer::nextLine() {
#if DEBUG_TOKENIZER
ALOGD("nextLine");
#endif
const char* end = getEnd();
while (mCurrent != end) {
char ch = *(mCurrent++);
if (ch == '\n') {
mLineNumber += 1;
break;
}
}
}
void Tokenizer::skipDelimiters(const char* delimiters) {
#if DEBUG_TOKENIZER
ALOGD("skipDelimiters");
#endif
const char* end = getEnd();
while (mCurrent != end) {
char ch = *mCurrent;
if (ch == '\n' || !isDelimiter(ch, delimiters)) {
break;
}
mCurrent += 1;
}
}
} // namespace android

View file

@ -0,0 +1,136 @@
/*
* 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_TOKENIZER_H
#define _UTILS_TOKENIZER_H
#include <assert.h>
#include <utils/Errors.h>
#include <utils/FileMap.h>
#include <utils/String8.h>
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,
bool ownBuffer, size_t length);
public:
~Tokenizer();
/**
* Opens a file and maps it into memory.
*
* Returns NO_ERROR and a tokenizer for the file, if successful.
* Otherwise returns an error and sets outTokenizer to NULL.
*/
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.
*/
inline bool isEof() const { return mCurrent == getEnd(); }
/**
* Returns true if at the end of the line or end of the file.
*/
inline bool isEol() const { return isEof() || *mCurrent == '\n'; }
/**
* Gets the name of the file.
*/
inline String8 getFilename() const { return mFilename; }
/**
* Gets a 1-based line number index for the current position.
*/
inline int32_t getLineNumber() const { return mLineNumber; }
/**
* Formats a location string consisting of the filename and current line number.
* Returns a string like "MyFile.txt:33".
*/
String8 getLocation() const;
/**
* Gets the character at the current position.
* Returns null at end of file.
*/
inline char peekChar() const { return isEof() ? '\0' : *mCurrent; }
/**
* Gets the remainder of the current line as a string, excluding the newline character.
*/
String8 peekRemainderOfLine() const;
/**
* Gets the character at the current position and advances past it.
* Returns null at end of file.
*/
inline char nextChar() { return isEof() ? '\0' : *(mCurrent++); }
/**
* Gets the next token on this line stopping at the specified delimiters
* or the end of the line whichever comes first and advances past it.
* Also stops at embedded nulls.
* Returns the token or an empty string if the current character is a delimiter
* or is at the end of the line.
*/
String8 nextToken(const char* delimiters);
/**
* Advances to the next line.
* Does nothing if already at the end of the file.
*/
void nextLine();
/**
* Skips over the specified delimiters in the line.
* Also skips embedded nulls.
*/
void skipDelimiters(const char* delimiters);
private:
Tokenizer(const Tokenizer& other); // not copyable
String8 mFilename;
FileMap* mFileMap;
char* mBuffer;
bool mOwnBuffer;
size_t mLength;
const char* mCurrent;
int32_t mLineNumber;
inline const char* getEnd() const { return mBuffer + mLength; }
};
} // namespace android
#endif // _UTILS_TOKENIZER_H

64
widget/gonk/libui/Trace.h Normal file
View 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

View 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

View 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

View 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

View 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

View file

@ -0,0 +1,171 @@
/*
* 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 "VirtualKeyMap"
#include "cutils_log.h"
#include <stdlib.h>
#include <string.h>
#include "VirtualKeyMap.h"
#include <utils/Errors.h>
#include "Tokenizer.h"
#include <utils/Timers.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_FIELD_DELIMITER = " \t\r:";
// --- VirtualKeyMap ---
VirtualKeyMap::VirtualKeyMap() {
}
VirtualKeyMap::~VirtualKeyMap() {
}
status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) {
*outMap = NULL;
Tokenizer* tokenizer;
status_t status = Tokenizer::open(filename, &tokenizer);
if (status) {
ALOGE("Error %d opening virtual key map file %s.", status, filename.string());
} else {
VirtualKeyMap* map = new VirtualKeyMap();
if (!map) {
ALOGE("Error allocating virtual key 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;
}
}
delete tokenizer;
}
return status;
}
// --- VirtualKeyMap::Parser ---
VirtualKeyMap::Parser::Parser(VirtualKeyMap* map, Tokenizer* tokenizer) :
mMap(map), mTokenizer(tokenizer) {
}
VirtualKeyMap::Parser::~Parser() {
}
status_t VirtualKeyMap::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() != '#') {
// Multiple keys can appear on one line or they can be broken up across multiple lines.
do {
String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
if (token != "0x01") {
ALOGE("%s: Unknown virtual key type, expected 0x01.",
mTokenizer->getLocation().string());
return BAD_VALUE;
}
VirtualKeyDefinition defn;
bool success = parseNextIntField(&defn.scanCode)
&& parseNextIntField(&defn.centerX)
&& parseNextIntField(&defn.centerY)
&& parseNextIntField(&defn.width)
&& parseNextIntField(&defn.height);
if (!success) {
ALOGE("%s: Expected 5 colon-delimited integers in virtual key definition.",
mTokenizer->getLocation().string());
return BAD_VALUE;
}
#if DEBUG_PARSER
ALOGD("Parsed virtual key: scanCode=%d, centerX=%d, centerY=%d, "
"width=%d, height=%d",
defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height);
#endif
mMap->mVirtualKeys.push(defn);
} while (consumeFieldDelimiterAndSkipWhitespace());
if (!mTokenizer->isEol()) {
ALOGE("%s: Expected end of line, got '%s'.",
mTokenizer->getLocation().string(),
mTokenizer->peekRemainderOfLine().string());
return BAD_VALUE;
}
}
mTokenizer->nextLine();
}
return NO_ERROR;
}
bool VirtualKeyMap::Parser::consumeFieldDelimiterAndSkipWhitespace() {
mTokenizer->skipDelimiters(WHITESPACE);
if (mTokenizer->peekChar() == ':') {
mTokenizer->nextChar();
mTokenizer->skipDelimiters(WHITESPACE);
return true;
}
return false;
}
bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) {
if (!consumeFieldDelimiterAndSkipWhitespace()) {
return false;
}
String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
char* end;
*outValue = strtol(token.string(), &end, 0);
if (token.isEmpty() || *end != '\0') {
ALOGE("Expected an integer, got '%s'.", token.string());
return false;
}
return true;
}
} // namespace android

View file

@ -0,0 +1,81 @@
/*
* 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 _ANDROIDFW_VIRTUAL_KEY_MAP_H
#define _ANDROIDFW_VIRTUAL_KEY_MAP_H
#include <stdint.h>
#include "Input.h"
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include "Tokenizer.h"
#include <utils/String8.h>
#include <utils/Unicode.h>
namespace android {
/* Describes a virtual key. */
struct VirtualKeyDefinition {
int32_t scanCode;
// configured position data, specified in display coords
int32_t centerX;
int32_t centerY;
int32_t width;
int32_t height;
};
/**
* 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:
~VirtualKeyMap();
static status_t load(const String8& filename, VirtualKeyMap** outMap);
inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const {
return mVirtualKeys;
}
private:
class Parser {
VirtualKeyMap* mMap;
Tokenizer* mTokenizer;
public:
Parser(VirtualKeyMap* map, Tokenizer* tokenizer);
~Parser();
status_t parse();
private:
bool consumeFieldDelimiterAndSkipWhitespace();
bool parseNextIntField(int32_t* outValue);
};
Vector<VirtualKeyDefinition> mVirtualKeys;
VirtualKeyMap();
};
} // namespace android
#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H

View file

@ -0,0 +1,850 @@
/*
* 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 _ANDROID_INPUT_H
#define _ANDROID_INPUT_H
/******************************************************************
*
* IMPORTANT NOTICE:
*
* This file is part of Android's set of stable system headers
* exposed by the Android NDK (Native Development Kit).
*
* Third-party source AND binary code relies on the definitions
* here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
*
* - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
* - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
* - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
* - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
*/
/*
* Structures and functions to receive and process input events in
* native code.
*
* NOTE: These functions MUST be implemented by /system/lib/libui.so
*/
#include <stdint.h>
#include <sys/types.h>
#include "android_keycodes.h"
#include <android/looper.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Key states (may be returned by queries about the current state of a
* particular key code, scan code or switch).
*/
enum {
/* The key state is unknown or the requested key itself is not supported. */
AKEY_STATE_UNKNOWN = -1,
/* The key is up. */
AKEY_STATE_UP = 0,
/* The key is down. */
AKEY_STATE_DOWN = 1,
/* The key is down but is a virtual key press that is being emulated by the system. */
AKEY_STATE_VIRTUAL = 2
};
/*
* Meta key / modifer state.
*/
enum {
/* No meta keys are pressed. */
AMETA_NONE = 0,
/* This mask is used to check whether one of the ALT meta keys is pressed. */
AMETA_ALT_ON = 0x02,
/* This mask is used to check whether the left ALT meta key is pressed. */
AMETA_ALT_LEFT_ON = 0x10,
/* This mask is used to check whether the right ALT meta key is pressed. */
AMETA_ALT_RIGHT_ON = 0x20,
/* This mask is used to check whether one of the SHIFT meta keys is pressed. */
AMETA_SHIFT_ON = 0x01,
/* This mask is used to check whether the left SHIFT meta key is pressed. */
AMETA_SHIFT_LEFT_ON = 0x40,
/* This mask is used to check whether the right SHIFT meta key is pressed. */
AMETA_SHIFT_RIGHT_ON = 0x80,
/* This mask is used to check whether the SYM meta key is pressed. */
AMETA_SYM_ON = 0x04,
/* This mask is used to check whether the FUNCTION meta key is pressed. */
AMETA_FUNCTION_ON = 0x08,
/* This mask is used to check whether one of the CTRL meta keys is pressed. */
AMETA_CTRL_ON = 0x1000,
/* This mask is used to check whether the left CTRL meta key is pressed. */
AMETA_CTRL_LEFT_ON = 0x2000,
/* This mask is used to check whether the right CTRL meta key is pressed. */
AMETA_CTRL_RIGHT_ON = 0x4000,
/* This mask is used to check whether one of the META meta keys is pressed. */
AMETA_META_ON = 0x10000,
/* This mask is used to check whether the left META meta key is pressed. */
AMETA_META_LEFT_ON = 0x20000,
/* This mask is used to check whether the right META meta key is pressed. */
AMETA_META_RIGHT_ON = 0x40000,
/* This mask is used to check whether the CAPS LOCK meta key is on. */
AMETA_CAPS_LOCK_ON = 0x100000,
/* This mask is used to check whether the NUM LOCK meta key is on. */
AMETA_NUM_LOCK_ON = 0x200000,
/* This mask is used to check whether the SCROLL LOCK meta key is on. */
AMETA_SCROLL_LOCK_ON = 0x400000,
};
/*
* Input events.
*
* Input events are opaque structures. Use the provided accessors functions to
* read their properties.
*/
struct AInputEvent;
typedef struct AInputEvent AInputEvent;
/*
* Input event types.
*/
enum {
/* Indicates that the input event is a key event. */
AINPUT_EVENT_TYPE_KEY = 1,
/* Indicates that the input event is a motion event. */
AINPUT_EVENT_TYPE_MOTION = 2
};
/*
* Key event actions.
*/
enum {
/* The key has been pressed down. */
AKEY_EVENT_ACTION_DOWN = 0,
/* The key has been released. */
AKEY_EVENT_ACTION_UP = 1,
/* Multiple duplicate key events have occurred in a row, or a complex string is
* being delivered. The repeat_count property of the key event contains the number
* of times the given key code should be executed.
*/
AKEY_EVENT_ACTION_MULTIPLE = 2
};
/*
* Key event flags.
*/
enum {
/* This mask is set if the device woke because of this key event. */
AKEY_EVENT_FLAG_WOKE_HERE = 0x1,
/* This mask is set if the key event was generated by a software keyboard. */
AKEY_EVENT_FLAG_SOFT_KEYBOARD = 0x2,
/* This mask is set if we don't want the key event to cause us to leave touch mode. */
AKEY_EVENT_FLAG_KEEP_TOUCH_MODE = 0x4,
/* This mask is set if an event was known to come from a trusted part
* of the system. That is, the event is known to come from the user,
* and could not have been spoofed by a third party component. */
AKEY_EVENT_FLAG_FROM_SYSTEM = 0x8,
/* This mask is used for compatibility, to identify enter keys that are
* coming from an IME whose enter key has been auto-labelled "next" or
* "done". This allows TextView to dispatch these as normal enter keys
* for old applications, but still do the appropriate action when
* receiving them. */
AKEY_EVENT_FLAG_EDITOR_ACTION = 0x10,
/* When associated with up key events, this indicates that the key press
* has been canceled. Typically this is used with virtual touch screen
* keys, where the user can slide from the virtual key area on to the
* display: in that case, the application will receive a canceled up
* event and should not perform the action normally associated with the
* key. Note that for this to work, the application can not perform an
* action for a key until it receives an up or the long press timeout has
* expired. */
AKEY_EVENT_FLAG_CANCELED = 0x20,
/* This key event was generated by a virtual (on-screen) hard key area.
* Typically this is an area of the touchscreen, outside of the regular
* display, dedicated to "hardware" buttons. */
AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY = 0x40,
/* This flag is set for the first key repeat that occurs after the
* long press timeout. */
AKEY_EVENT_FLAG_LONG_PRESS = 0x80,
/* Set when a key event has AKEY_EVENT_FLAG_CANCELED set because a long
* press action was executed while it was down. */
AKEY_EVENT_FLAG_CANCELED_LONG_PRESS = 0x100,
/* Set for AKEY_EVENT_ACTION_UP when this event's key code is still being
* tracked from its initial down. That is, somebody requested that tracking
* started on the key down and a long press has not caused
* the tracking to be canceled. */
AKEY_EVENT_FLAG_TRACKING = 0x200,
/* Set when a key event has been synthesized to implement default behavior
* for an event that the application did not handle.
* Fallback key events are generated by unhandled trackball motions
* (to emulate a directional keypad) and by certain unhandled key presses
* that are declared in the key map (such as special function numeric keypad
* keys when numlock is off). */
AKEY_EVENT_FLAG_FALLBACK = 0x400,
};
/*
* Motion event actions.
*/
/* Bit shift for the action bits holding the pointer index as
* defined by AMOTION_EVENT_ACTION_POINTER_INDEX_MASK.
*/
#define AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT 8
enum {
/* Bit mask of the parts of the action code that are the action itself.
*/
AMOTION_EVENT_ACTION_MASK = 0xff,
/* Bits in the action code that represent a pointer index, used with
* AMOTION_EVENT_ACTION_POINTER_DOWN and AMOTION_EVENT_ACTION_POINTER_UP. Shifting
* down by AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT provides the actual pointer
* index where the data for the pointer going up or down can be found.
*/
AMOTION_EVENT_ACTION_POINTER_INDEX_MASK = 0xff00,
/* A pressed gesture has started, the motion contains the initial starting location.
*/
AMOTION_EVENT_ACTION_DOWN = 0,
/* A pressed gesture has finished, the motion contains the final release location
* as well as any intermediate points since the last down or move event.
*/
AMOTION_EVENT_ACTION_UP = 1,
/* A change has happened during a press gesture (between AMOTION_EVENT_ACTION_DOWN and
* AMOTION_EVENT_ACTION_UP). The motion contains the most recent point, as well as
* any intermediate points since the last down or move event.
*/
AMOTION_EVENT_ACTION_MOVE = 2,
/* The current gesture has been aborted.
* You will not receive any more points in it. You should treat this as
* an up event, but not perform any action that you normally would.
*/
AMOTION_EVENT_ACTION_CANCEL = 3,
/* A movement has happened outside of the normal bounds of the UI element.
* This does not provide a full gesture, but only the initial location of the movement/touch.
*/
AMOTION_EVENT_ACTION_OUTSIDE = 4,
/* A non-primary pointer has gone down.
* The bits in AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
*/
AMOTION_EVENT_ACTION_POINTER_DOWN = 5,
/* A non-primary pointer has gone up.
* The bits in AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
*/
AMOTION_EVENT_ACTION_POINTER_UP = 6,
/* A change happened but the pointer is not down (unlike AMOTION_EVENT_ACTION_MOVE).
* The motion contains the most recent point, as well as any intermediate points since
* the last hover move event.
*/
AMOTION_EVENT_ACTION_HOVER_MOVE = 7,
/* The motion event contains relative vertical and/or horizontal scroll offsets.
* Use getAxisValue to retrieve the information from AMOTION_EVENT_AXIS_VSCROLL
* and AMOTION_EVENT_AXIS_HSCROLL.
* The pointer may or may not be down when this event is dispatched.
* This action is always delivered to the winder under the pointer, which
* may not be the window currently touched.
*/
AMOTION_EVENT_ACTION_SCROLL = 8,
/* The pointer is not down but has entered the boundaries of a window or view.
*/
AMOTION_EVENT_ACTION_HOVER_ENTER = 9,
/* The pointer is not down but has exited the boundaries of a window or view.
*/
AMOTION_EVENT_ACTION_HOVER_EXIT = 10,
};
/*
* Motion event flags.
*/
enum {
/* This flag indicates that the window that received this motion event is partly
* or wholly obscured by another visible window above it. This flag is set to true
* even if the event did not directly pass through the obscured area.
* A security sensitive application can check this flag to identify situations in which
* a malicious application may have covered up part of its content for the purpose
* of misleading the user or hijacking touches. An appropriate response might be
* to drop the suspect touches or to take additional precautions to confirm the user's
* actual intent.
*/
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED = 0x1,
};
/*
* Motion event edge touch flags.
*/
enum {
/* No edges intersected */
AMOTION_EVENT_EDGE_FLAG_NONE = 0,
/* Flag indicating the motion event intersected the top edge of the screen. */
AMOTION_EVENT_EDGE_FLAG_TOP = 0x01,
/* Flag indicating the motion event intersected the bottom edge of the screen. */
AMOTION_EVENT_EDGE_FLAG_BOTTOM = 0x02,
/* Flag indicating the motion event intersected the left edge of the screen. */
AMOTION_EVENT_EDGE_FLAG_LEFT = 0x04,
/* Flag indicating the motion event intersected the right edge of the screen. */
AMOTION_EVENT_EDGE_FLAG_RIGHT = 0x08
};
/*
* Constants that identify each individual axis of a motion event.
* Refer to the documentation on the MotionEvent class for descriptions of each axis.
*/
enum {
AMOTION_EVENT_AXIS_X = 0,
AMOTION_EVENT_AXIS_Y = 1,
AMOTION_EVENT_AXIS_PRESSURE = 2,
AMOTION_EVENT_AXIS_SIZE = 3,
AMOTION_EVENT_AXIS_TOUCH_MAJOR = 4,
AMOTION_EVENT_AXIS_TOUCH_MINOR = 5,
AMOTION_EVENT_AXIS_TOOL_MAJOR = 6,
AMOTION_EVENT_AXIS_TOOL_MINOR = 7,
AMOTION_EVENT_AXIS_ORIENTATION = 8,
AMOTION_EVENT_AXIS_VSCROLL = 9,
AMOTION_EVENT_AXIS_HSCROLL = 10,
AMOTION_EVENT_AXIS_Z = 11,
AMOTION_EVENT_AXIS_RX = 12,
AMOTION_EVENT_AXIS_RY = 13,
AMOTION_EVENT_AXIS_RZ = 14,
AMOTION_EVENT_AXIS_HAT_X = 15,
AMOTION_EVENT_AXIS_HAT_Y = 16,
AMOTION_EVENT_AXIS_LTRIGGER = 17,
AMOTION_EVENT_AXIS_RTRIGGER = 18,
AMOTION_EVENT_AXIS_THROTTLE = 19,
AMOTION_EVENT_AXIS_RUDDER = 20,
AMOTION_EVENT_AXIS_WHEEL = 21,
AMOTION_EVENT_AXIS_GAS = 22,
AMOTION_EVENT_AXIS_BRAKE = 23,
AMOTION_EVENT_AXIS_DISTANCE = 24,
AMOTION_EVENT_AXIS_TILT = 25,
AMOTION_EVENT_AXIS_GENERIC_1 = 32,
AMOTION_EVENT_AXIS_GENERIC_2 = 33,
AMOTION_EVENT_AXIS_GENERIC_3 = 34,
AMOTION_EVENT_AXIS_GENERIC_4 = 35,
AMOTION_EVENT_AXIS_GENERIC_5 = 36,
AMOTION_EVENT_AXIS_GENERIC_6 = 37,
AMOTION_EVENT_AXIS_GENERIC_7 = 38,
AMOTION_EVENT_AXIS_GENERIC_8 = 39,
AMOTION_EVENT_AXIS_GENERIC_9 = 40,
AMOTION_EVENT_AXIS_GENERIC_10 = 41,
AMOTION_EVENT_AXIS_GENERIC_11 = 42,
AMOTION_EVENT_AXIS_GENERIC_12 = 43,
AMOTION_EVENT_AXIS_GENERIC_13 = 44,
AMOTION_EVENT_AXIS_GENERIC_14 = 45,
AMOTION_EVENT_AXIS_GENERIC_15 = 46,
AMOTION_EVENT_AXIS_GENERIC_16 = 47,
// NOTE: If you add a new axis here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
};
/*
* Constants that identify buttons that are associated with motion events.
* Refer to the documentation on the MotionEvent class for descriptions of each button.
*/
enum {
AMOTION_EVENT_BUTTON_PRIMARY = 1 << 0,
AMOTION_EVENT_BUTTON_SECONDARY = 1 << 1,
AMOTION_EVENT_BUTTON_TERTIARY = 1 << 2,
AMOTION_EVENT_BUTTON_BACK = 1 << 3,
AMOTION_EVENT_BUTTON_FORWARD = 1 << 4,
};
/*
* Constants that identify tool types.
* Refer to the documentation on the MotionEvent class for descriptions of each tool type.
*/
enum {
AMOTION_EVENT_TOOL_TYPE_UNKNOWN = 0,
AMOTION_EVENT_TOOL_TYPE_FINGER = 1,
AMOTION_EVENT_TOOL_TYPE_STYLUS = 2,
AMOTION_EVENT_TOOL_TYPE_MOUSE = 3,
AMOTION_EVENT_TOOL_TYPE_ERASER = 4,
};
/*
* Input sources.
*
* Refer to the documentation on android.view.InputDevice for more details about input sources
* and their correct interpretation.
*/
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,
AINPUT_SOURCE_CLASS_POSITION = 0x00000008,
AINPUT_SOURCE_CLASS_JOYSTICK = 0x00000010,
};
enum {
AINPUT_SOURCE_UNKNOWN = 0x00000000,
AINPUT_SOURCE_KEYBOARD = 0x00000100 | AINPUT_SOURCE_CLASS_BUTTON,
AINPUT_SOURCE_DPAD = 0x00000200 | AINPUT_SOURCE_CLASS_BUTTON,
AINPUT_SOURCE_GAMEPAD = 0x00000400 | AINPUT_SOURCE_CLASS_BUTTON,
AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER,
AINPUT_SOURCE_MOUSE = 0x00002000 | AINPUT_SOURCE_CLASS_POINTER,
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,
};
/*
* Keyboard types.
*
* Refer to the documentation on android.view.InputDevice for more details.
*/
enum {
AINPUT_KEYBOARD_TYPE_NONE = 0,
AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC = 1,
AINPUT_KEYBOARD_TYPE_ALPHABETIC = 2,
};
/*
* Constants used to retrieve information about the range of motion for a particular
* coordinate of a motion event.
*
* Refer to the documentation on android.view.InputDevice for more details about input sources
* and their correct interpretation.
*
* DEPRECATION NOTICE: These constants are deprecated. Use AMOTION_EVENT_AXIS_* constants instead.
*/
enum {
AINPUT_MOTION_RANGE_X = AMOTION_EVENT_AXIS_X,
AINPUT_MOTION_RANGE_Y = AMOTION_EVENT_AXIS_Y,
AINPUT_MOTION_RANGE_PRESSURE = AMOTION_EVENT_AXIS_PRESSURE,
AINPUT_MOTION_RANGE_SIZE = AMOTION_EVENT_AXIS_SIZE,
AINPUT_MOTION_RANGE_TOUCH_MAJOR = AMOTION_EVENT_AXIS_TOUCH_MAJOR,
AINPUT_MOTION_RANGE_TOUCH_MINOR = AMOTION_EVENT_AXIS_TOUCH_MINOR,
AINPUT_MOTION_RANGE_TOOL_MAJOR = AMOTION_EVENT_AXIS_TOOL_MAJOR,
AINPUT_MOTION_RANGE_TOOL_MINOR = AMOTION_EVENT_AXIS_TOOL_MINOR,
AINPUT_MOTION_RANGE_ORIENTATION = AMOTION_EVENT_AXIS_ORIENTATION,
} __attribute__ ((deprecated));
/*
* Input event accessors.
*
* Note that most functions can only be used on input events that are of a given type.
* Calling these functions on input events of other types will yield undefined behavior.
*/
/*** Accessors for all input events. ***/
/* Get the input event type. */
int32_t AInputEvent_getType(const AInputEvent* event);
/* Get the id for the device that an input event came from.
*
* Input events can be generated by multiple different input devices.
* Use the input device id to obtain information about the input
* device that was responsible for generating a particular event.
*
* An input device id of 0 indicates that the event didn't come from a physical device;
* other numbers are arbitrary and you shouldn't depend on the values.
* Use the provided input device query API to obtain information about input devices.
*/
int32_t AInputEvent_getDeviceId(const AInputEvent* event);
/* Get the input event source. */
int32_t AInputEvent_getSource(const AInputEvent* event);
/*** Accessors for key events only. ***/
/* Get the key event action. */
int32_t AKeyEvent_getAction(const AInputEvent* key_event);
/* Get the key event flags. */
int32_t AKeyEvent_getFlags(const AInputEvent* key_event);
/* Get the key code of the key event.
* This is the physical key that was pressed, not the Unicode character. */
int32_t AKeyEvent_getKeyCode(const AInputEvent* key_event);
/* Get the hardware key id of this key event.
* These values are not reliable and vary from device to device. */
int32_t AKeyEvent_getScanCode(const AInputEvent* key_event);
/* Get the meta key state. */
int32_t AKeyEvent_getMetaState(const AInputEvent* key_event);
/* Get the repeat count of the event.
* For both key up an key down events, this is the number of times the key has
* repeated with the first down starting at 0 and counting up from there. For
* multiple key events, this is the number of down/up pairs that have occurred. */
int32_t AKeyEvent_getRepeatCount(const AInputEvent* key_event);
/* Get the time of the most recent key down event, in the
* java.lang.System.nanoTime() time base. If this is a down event,
* this will be the same as eventTime.
* Note that when chording keys, this value is the down time of the most recently
* pressed key, which may not be the same physical key of this event. */
int64_t AKeyEvent_getDownTime(const AInputEvent* key_event);
/* Get the time this event occurred, in the
* java.lang.System.nanoTime() time base. */
int64_t AKeyEvent_getEventTime(const AInputEvent* key_event);
/*** Accessors for motion events only. ***/
/* Get the combined motion event action code and pointer index. */
int32_t AMotionEvent_getAction(const AInputEvent* motion_event);
/* Get the motion event flags. */
int32_t AMotionEvent_getFlags(const AInputEvent* motion_event);
/* Get the state of any meta / modifier keys that were in effect when the
* event was generated. */
int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event);
/* Get the button state of all buttons that are pressed. */
int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event);
/* Get a bitfield indicating which edges, if any, were touched by this motion event.
* For touch events, clients can use this to determine if the user's finger was
* touching the edge of the display. */
int32_t AMotionEvent_getEdgeFlags(const AInputEvent* motion_event);
/* Get the time when the user originally pressed down to start a stream of
* position events, in the java.lang.System.nanoTime() time base. */
int64_t AMotionEvent_getDownTime(const AInputEvent* motion_event);
/* Get the time when this specific event was generated,
* in the java.lang.System.nanoTime() time base. */
int64_t AMotionEvent_getEventTime(const AInputEvent* motion_event);
/* Get the X coordinate offset.
* For touch events on the screen, this is the delta that was added to the raw
* screen coordinates to adjust for the absolute position of the containing windows
* and views. */
float AMotionEvent_getXOffset(const AInputEvent* motion_event);
/* Get the precision of the Y coordinates being reported.
* For touch events on the screen, this is the delta that was added to the raw
* screen coordinates to adjust for the absolute position of the containing windows
* and views. */
float AMotionEvent_getYOffset(const AInputEvent* motion_event);
/* Get the precision of the X coordinates being reported.
* You can multiply this number with an X coordinate sample to find the
* actual hardware value of the X coordinate. */
float AMotionEvent_getXPrecision(const AInputEvent* motion_event);
/* Get the precision of the Y coordinates being reported.
* You can multiply this number with a Y coordinate sample to find the
* actual hardware value of the Y coordinate. */
float AMotionEvent_getYPrecision(const AInputEvent* motion_event);
/* Get the number of pointers of data contained in this event.
* Always >= 1. */
size_t AMotionEvent_getPointerCount(const AInputEvent* motion_event);
/* Get the pointer identifier associated with a particular pointer
* data index in this event. The identifier tells you the actual pointer
* number associated with the data, accounting for individual pointers
* going up and down since the start of the current gesture. */
int32_t AMotionEvent_getPointerId(const AInputEvent* motion_event, size_t pointer_index);
/* Get the tool type of a pointer for the given pointer index.
* The tool type indicates the type of tool used to make contact such as a
* finger or stylus, if known. */
int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index);
/* Get the original raw X coordinate of this event.
* For touch events on the screen, this is the original location of the event
* on the screen, before it had been adjusted for the containing window
* and views. */
float AMotionEvent_getRawX(const AInputEvent* motion_event, size_t pointer_index);
/* Get the original raw X coordinate of this event.
* For touch events on the screen, this is the original location of the event
* on the screen, before it had been adjusted for the containing window
* and views. */
float AMotionEvent_getRawY(const AInputEvent* motion_event, size_t pointer_index);
/* Get the current X coordinate of this event for the given pointer index.
* Whole numbers are pixels; the value may have a fraction for input devices
* that are sub-pixel precise. */
float AMotionEvent_getX(const AInputEvent* motion_event, size_t pointer_index);
/* Get the current Y coordinate of this event for the given pointer index.
* Whole numbers are pixels; the value may have a fraction for input devices
* that are sub-pixel precise. */
float AMotionEvent_getY(const AInputEvent* motion_event, size_t pointer_index);
/* Get the current pressure of this event for the given pointer index.
* The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure),
* although values higher than 1 may be generated depending on the calibration of
* the input device. */
float AMotionEvent_getPressure(const AInputEvent* motion_event, size_t pointer_index);
/* Get the current scaled value of the approximate size for the given pointer index.
* This represents some approximation of the area of the screen being
* pressed; the actual value in pixels corresponding to the
* touch is normalized with the device specific range of values
* and scaled to a value between 0 and 1. The value of size can be used to
* determine fat touch events. */
float AMotionEvent_getSize(const AInputEvent* motion_event, size_t pointer_index);
/* Get the current length of the major axis of an ellipse that describes the touch area
* at the point of contact for the given pointer index. */
float AMotionEvent_getTouchMajor(const AInputEvent* motion_event, size_t pointer_index);
/* Get the current length of the minor axis of an ellipse that describes the touch area
* at the point of contact for the given pointer index. */
float AMotionEvent_getTouchMinor(const AInputEvent* motion_event, size_t pointer_index);
/* Get the current length of the major axis of an ellipse that describes the size
* of the approaching tool for the given pointer index.
* The tool area represents the estimated size of the finger or pen that is
* touching the device independent of its actual touch area at the point of contact. */
float AMotionEvent_getToolMajor(const AInputEvent* motion_event, size_t pointer_index);
/* Get the current length of the minor axis of an ellipse that describes the size
* of the approaching tool for the given pointer index.
* The tool area represents the estimated size of the finger or pen that is
* touching the device independent of its actual touch area at the point of contact. */
float AMotionEvent_getToolMinor(const AInputEvent* motion_event, size_t pointer_index);
/* Get the current orientation of the touch area and tool area in radians clockwise from
* vertical for the given pointer index.
* An angle of 0 degrees indicates that the major axis of contact is oriented
* upwards, is perfectly circular or is of unknown orientation. A positive angle
* indicates that the major axis of contact is oriented to the right. A negative angle
* indicates that the major axis of contact is oriented to the left.
* The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
* (finger pointing fully right). */
float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index);
/* Get the value of the request axis for the given pointer index. */
float AMotionEvent_getAxisValue(const AInputEvent* motion_event,
int32_t axis, size_t pointer_index);
/* Get the number of historical points in this event. These are movements that
* have occurred between this event and the previous event. This only applies
* to AMOTION_EVENT_ACTION_MOVE events -- all other actions will have a size of 0.
* Historical samples are indexed from oldest to newest. */
size_t AMotionEvent_getHistorySize(const AInputEvent* motion_event);
/* Get the time that a historical movement occurred between this event and
* the previous event, in the java.lang.System.nanoTime() time base. */
int64_t AMotionEvent_getHistoricalEventTime(AInputEvent* motion_event,
size_t history_index);
/* Get the historical raw X coordinate of this event for the given pointer index that
* occurred between this event and the previous motion event.
* For touch events on the screen, this is the original location of the event
* on the screen, before it had been adjusted for the containing window
* and views.
* Whole numbers are pixels; the value may have a fraction for input devices
* that are sub-pixel precise. */
float AMotionEvent_getHistoricalRawX(const AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
/* Get the historical raw Y coordinate of this event for the given pointer index that
* occurred between this event and the previous motion event.
* For touch events on the screen, this is the original location of the event
* on the screen, before it had been adjusted for the containing window
* and views.
* Whole numbers are pixels; the value may have a fraction for input devices
* that are sub-pixel precise. */
float AMotionEvent_getHistoricalRawY(const AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
/* Get the historical X coordinate of this event for the given pointer index that
* occurred between this event and the previous motion event.
* Whole numbers are pixels; the value may have a fraction for input devices
* that are sub-pixel precise. */
float AMotionEvent_getHistoricalX(AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
/* Get the historical Y coordinate of this event for the given pointer index that
* occurred between this event and the previous motion event.
* Whole numbers are pixels; the value may have a fraction for input devices
* that are sub-pixel precise. */
float AMotionEvent_getHistoricalY(AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
/* Get the historical pressure of this event for the given pointer index that
* occurred between this event and the previous motion event.
* The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure),
* although values higher than 1 may be generated depending on the calibration of
* the input device. */
float AMotionEvent_getHistoricalPressure(AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
/* Get the current scaled value of the approximate size for the given pointer index that
* occurred between this event and the previous motion event.
* This represents some approximation of the area of the screen being
* pressed; the actual value in pixels corresponding to the
* touch is normalized with the device specific range of values
* and scaled to a value between 0 and 1. The value of size can be used to
* determine fat touch events. */
float AMotionEvent_getHistoricalSize(AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
/* Get the historical length of the major axis of an ellipse that describes the touch area
* at the point of contact for the given pointer index that
* occurred between this event and the previous motion event. */
float AMotionEvent_getHistoricalTouchMajor(const AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
/* Get the historical length of the minor axis of an ellipse that describes the touch area
* at the point of contact for the given pointer index that
* occurred between this event and the previous motion event. */
float AMotionEvent_getHistoricalTouchMinor(const AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
/* Get the historical length of the major axis of an ellipse that describes the size
* of the approaching tool for the given pointer index that
* occurred between this event and the previous motion event.
* The tool area represents the estimated size of the finger or pen that is
* touching the device independent of its actual touch area at the point of contact. */
float AMotionEvent_getHistoricalToolMajor(const AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
/* Get the historical length of the minor axis of an ellipse that describes the size
* of the approaching tool for the given pointer index that
* occurred between this event and the previous motion event.
* The tool area represents the estimated size of the finger or pen that is
* touching the device independent of its actual touch area at the point of contact. */
float AMotionEvent_getHistoricalToolMinor(const AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
/* Get the historical orientation of the touch area and tool area in radians clockwise from
* vertical for the given pointer index that
* occurred between this event and the previous motion event.
* An angle of 0 degrees indicates that the major axis of contact is oriented
* upwards, is perfectly circular or is of unknown orientation. A positive angle
* indicates that the major axis of contact is oriented to the right. A negative angle
* indicates that the major axis of contact is oriented to the left.
* The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
* (finger pointing fully right). */
float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
/* Get the historical value of the request axis for the given pointer index
* that occurred between this event and the previous motion event. */
float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
int32_t axis, size_t pointer_index, size_t history_index);
/*
* Input queue
*
* An input queue is the facility through which you retrieve input
* events.
*/
struct AInputQueue;
typedef struct AInputQueue AInputQueue;
/*
* Add this input queue to a looper for processing. See
* ALooper_addFd() for information on the ident, callback, and data params.
*/
void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
int ident, ALooper_callbackFunc callback, void* data);
/*
* Remove the input queue from the looper it is currently attached to.
*/
void AInputQueue_detachLooper(AInputQueue* queue);
/*
* Returns true if there are one or more events available in the
* input queue. Returns 1 if the queue has events; 0 if
* it does not have events; and a negative value if there is an error.
*/
int32_t AInputQueue_hasEvents(AInputQueue* queue);
/*
* Returns the next available event from the queue. Returns a negative
* value if no events are available or an error has occurred.
*/
int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent);
/*
* Sends the key for standard pre-dispatching -- that is, possibly deliver
* it to the current IME to be consumed before the app. Returns 0 if it
* was not pre-dispatched, meaning you can process it right now. If non-zero
* is returned, you must abandon the current event processing and allow the
* event to appear again in the event queue (if it does not get consumed during
* pre-dispatching).
*/
int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, AInputEvent* event);
/*
* Report that dispatching has finished with the given event.
* This must be called after receiving an event with AInputQueue_get_event().
*/
void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled);
#ifdef __cplusplus
}
#endif
#endif // _ANDROID_INPUT_H

View file

@ -0,0 +1,315 @@
/*
* 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 _ANDROID_KEYCODES_H
#define _ANDROID_KEYCODES_H
/******************************************************************
*
* IMPORTANT NOTICE:
*
* This file is part of Android's set of stable system headers
* exposed by the Android NDK (Native Development Kit).
*
* Third-party source AND binary code relies on the definitions
* here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
*
* - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
* - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
* - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
* - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
*/
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Key codes.
*/
enum {
AKEYCODE_UNKNOWN = 0,
AKEYCODE_SOFT_LEFT = 1,
AKEYCODE_SOFT_RIGHT = 2,
AKEYCODE_HOME = 3,
AKEYCODE_BACK = 4,
AKEYCODE_CALL = 5,
AKEYCODE_ENDCALL = 6,
AKEYCODE_0 = 7,
AKEYCODE_1 = 8,
AKEYCODE_2 = 9,
AKEYCODE_3 = 10,
AKEYCODE_4 = 11,
AKEYCODE_5 = 12,
AKEYCODE_6 = 13,
AKEYCODE_7 = 14,
AKEYCODE_8 = 15,
AKEYCODE_9 = 16,
AKEYCODE_STAR = 17,
AKEYCODE_POUND = 18,
AKEYCODE_DPAD_UP = 19,
AKEYCODE_DPAD_DOWN = 20,
AKEYCODE_DPAD_LEFT = 21,
AKEYCODE_DPAD_RIGHT = 22,
AKEYCODE_DPAD_CENTER = 23,
AKEYCODE_VOLUME_UP = 24,
AKEYCODE_VOLUME_DOWN = 25,
AKEYCODE_POWER = 26,
AKEYCODE_CAMERA = 27,
AKEYCODE_CLEAR = 28,
AKEYCODE_A = 29,
AKEYCODE_B = 30,
AKEYCODE_C = 31,
AKEYCODE_D = 32,
AKEYCODE_E = 33,
AKEYCODE_F = 34,
AKEYCODE_G = 35,
AKEYCODE_H = 36,
AKEYCODE_I = 37,
AKEYCODE_J = 38,
AKEYCODE_K = 39,
AKEYCODE_L = 40,
AKEYCODE_M = 41,
AKEYCODE_N = 42,
AKEYCODE_O = 43,
AKEYCODE_P = 44,
AKEYCODE_Q = 45,
AKEYCODE_R = 46,
AKEYCODE_S = 47,
AKEYCODE_T = 48,
AKEYCODE_U = 49,
AKEYCODE_V = 50,
AKEYCODE_W = 51,
AKEYCODE_X = 52,
AKEYCODE_Y = 53,
AKEYCODE_Z = 54,
AKEYCODE_COMMA = 55,
AKEYCODE_PERIOD = 56,
AKEYCODE_ALT_LEFT = 57,
AKEYCODE_ALT_RIGHT = 58,
AKEYCODE_SHIFT_LEFT = 59,
AKEYCODE_SHIFT_RIGHT = 60,
AKEYCODE_TAB = 61,
AKEYCODE_SPACE = 62,
AKEYCODE_SYM = 63,
AKEYCODE_EXPLORER = 64,
AKEYCODE_ENVELOPE = 65,
AKEYCODE_ENTER = 66,
AKEYCODE_DEL = 67,
AKEYCODE_GRAVE = 68,
AKEYCODE_MINUS = 69,
AKEYCODE_EQUALS = 70,
AKEYCODE_LEFT_BRACKET = 71,
AKEYCODE_RIGHT_BRACKET = 72,
AKEYCODE_BACKSLASH = 73,
AKEYCODE_SEMICOLON = 74,
AKEYCODE_APOSTROPHE = 75,
AKEYCODE_SLASH = 76,
AKEYCODE_AT = 77,
AKEYCODE_NUM = 78,
AKEYCODE_HEADSETHOOK = 79,
AKEYCODE_FOCUS = 80, // *Camera* focus
AKEYCODE_PLUS = 81,
AKEYCODE_MENU = 82,
AKEYCODE_NOTIFICATION = 83,
AKEYCODE_SEARCH = 84,
AKEYCODE_MEDIA_PLAY_PAUSE= 85,
AKEYCODE_MEDIA_STOP = 86,
AKEYCODE_MEDIA_NEXT = 87,
AKEYCODE_MEDIA_PREVIOUS = 88,
AKEYCODE_MEDIA_REWIND = 89,
AKEYCODE_MEDIA_FAST_FORWARD = 90,
AKEYCODE_MUTE = 91,
AKEYCODE_PAGE_UP = 92,
AKEYCODE_PAGE_DOWN = 93,
AKEYCODE_PICTSYMBOLS = 94,
AKEYCODE_SWITCH_CHARSET = 95,
AKEYCODE_BUTTON_A = 96,
AKEYCODE_BUTTON_B = 97,
AKEYCODE_BUTTON_C = 98,
AKEYCODE_BUTTON_X = 99,
AKEYCODE_BUTTON_Y = 100,
AKEYCODE_BUTTON_Z = 101,
AKEYCODE_BUTTON_L1 = 102,
AKEYCODE_BUTTON_R1 = 103,
AKEYCODE_BUTTON_L2 = 104,
AKEYCODE_BUTTON_R2 = 105,
AKEYCODE_BUTTON_THUMBL = 106,
AKEYCODE_BUTTON_THUMBR = 107,
AKEYCODE_BUTTON_START = 108,
AKEYCODE_BUTTON_SELECT = 109,
AKEYCODE_BUTTON_MODE = 110,
AKEYCODE_ESCAPE = 111,
AKEYCODE_FORWARD_DEL = 112,
AKEYCODE_CTRL_LEFT = 113,
AKEYCODE_CTRL_RIGHT = 114,
AKEYCODE_CAPS_LOCK = 115,
AKEYCODE_SCROLL_LOCK = 116,
AKEYCODE_META_LEFT = 117,
AKEYCODE_META_RIGHT = 118,
AKEYCODE_FUNCTION = 119,
AKEYCODE_SYSRQ = 120,
AKEYCODE_BREAK = 121,
AKEYCODE_MOVE_HOME = 122,
AKEYCODE_MOVE_END = 123,
AKEYCODE_INSERT = 124,
AKEYCODE_FORWARD = 125,
AKEYCODE_MEDIA_PLAY = 126,
AKEYCODE_MEDIA_PAUSE = 127,
AKEYCODE_MEDIA_CLOSE = 128,
AKEYCODE_MEDIA_EJECT = 129,
AKEYCODE_MEDIA_RECORD = 130,
AKEYCODE_F1 = 131,
AKEYCODE_F2 = 132,
AKEYCODE_F3 = 133,
AKEYCODE_F4 = 134,
AKEYCODE_F5 = 135,
AKEYCODE_F6 = 136,
AKEYCODE_F7 = 137,
AKEYCODE_F8 = 138,
AKEYCODE_F9 = 139,
AKEYCODE_F10 = 140,
AKEYCODE_F11 = 141,
AKEYCODE_F12 = 142,
AKEYCODE_NUM_LOCK = 143,
AKEYCODE_NUMPAD_0 = 144,
AKEYCODE_NUMPAD_1 = 145,
AKEYCODE_NUMPAD_2 = 146,
AKEYCODE_NUMPAD_3 = 147,
AKEYCODE_NUMPAD_4 = 148,
AKEYCODE_NUMPAD_5 = 149,
AKEYCODE_NUMPAD_6 = 150,
AKEYCODE_NUMPAD_7 = 151,
AKEYCODE_NUMPAD_8 = 152,
AKEYCODE_NUMPAD_9 = 153,
AKEYCODE_NUMPAD_DIVIDE = 154,
AKEYCODE_NUMPAD_MULTIPLY = 155,
AKEYCODE_NUMPAD_SUBTRACT = 156,
AKEYCODE_NUMPAD_ADD = 157,
AKEYCODE_NUMPAD_DOT = 158,
AKEYCODE_NUMPAD_COMMA = 159,
AKEYCODE_NUMPAD_ENTER = 160,
AKEYCODE_NUMPAD_EQUALS = 161,
AKEYCODE_NUMPAD_LEFT_PAREN = 162,
AKEYCODE_NUMPAD_RIGHT_PAREN = 163,
AKEYCODE_VOLUME_MUTE = 164,
AKEYCODE_INFO = 165,
AKEYCODE_CHANNEL_UP = 166,
AKEYCODE_CHANNEL_DOWN = 167,
AKEYCODE_ZOOM_IN = 168,
AKEYCODE_ZOOM_OUT = 169,
AKEYCODE_TV = 170,
AKEYCODE_WINDOW = 171,
AKEYCODE_GUIDE = 172,
AKEYCODE_DVR = 173,
AKEYCODE_BOOKMARK = 174,
AKEYCODE_CAPTIONS = 175,
AKEYCODE_SETTINGS = 176,
AKEYCODE_TV_POWER = 177,
AKEYCODE_TV_INPUT = 178,
AKEYCODE_STB_POWER = 179,
AKEYCODE_STB_INPUT = 180,
AKEYCODE_AVR_POWER = 181,
AKEYCODE_AVR_INPUT = 182,
AKEYCODE_PROG_RED = 183,
AKEYCODE_PROG_GREEN = 184,
AKEYCODE_PROG_YELLOW = 185,
AKEYCODE_PROG_BLUE = 186,
AKEYCODE_APP_SWITCH = 187,
AKEYCODE_BUTTON_1 = 188,
AKEYCODE_BUTTON_2 = 189,
AKEYCODE_BUTTON_3 = 190,
AKEYCODE_BUTTON_4 = 191,
AKEYCODE_BUTTON_5 = 192,
AKEYCODE_BUTTON_6 = 193,
AKEYCODE_BUTTON_7 = 194,
AKEYCODE_BUTTON_8 = 195,
AKEYCODE_BUTTON_9 = 196,
AKEYCODE_BUTTON_10 = 197,
AKEYCODE_BUTTON_11 = 198,
AKEYCODE_BUTTON_12 = 199,
AKEYCODE_BUTTON_13 = 200,
AKEYCODE_BUTTON_14 = 201,
AKEYCODE_BUTTON_15 = 202,
AKEYCODE_BUTTON_16 = 203,
AKEYCODE_LANGUAGE_SWITCH = 204,
AKEYCODE_MANNER_MODE = 205,
AKEYCODE_3D_MODE = 206,
AKEYCODE_CONTACTS = 207,
AKEYCODE_CALENDAR = 208,
AKEYCODE_MUSIC = 209,
AKEYCODE_CALCULATOR = 210,
AKEYCODE_ZENKAKU_HANKAKU = 211,
AKEYCODE_EISU = 212,
AKEYCODE_MUHENKAN = 213,
AKEYCODE_HENKAN = 214,
AKEYCODE_KATAKANA_HIRAGANA = 215,
AKEYCODE_YEN = 216,
AKEYCODE_RO = 217,
AKEYCODE_KANA = 218,
AKEYCODE_ASSIST = 219,
AKEYCODE_BRIGHTNESS_DOWN = 220,
AKEYCODE_BRIGHTNESS_UP = 221,
AKEYCODE_MEDIA_AUDIO_TRACK = 222,
AKEYCODE_SLEEP = 223,
AKEYCODE_WAKEUP = 224,
AKEYCODE_PAIRING = 225,
AKEYCODE_MEDIA_TOP_MENU = 226,
AKEYCODE_11 = 227,
AKEYCODE_12 = 228,
AKEYCODE_LAST_CHANNEL = 229,
AKEYCODE_TV_DATA_SERVICE = 230,
AKEYCODE_VOICE_ASSIST = 231,
AKEYCODE_TV_RADIO_SERVICE = 232,
AKEYCODE_TV_TELETEXT = 233,
AKEYCODE_TV_NUMBER_ENTRY = 234,
AKEYCODE_TV_TERRESTRIAL_ANALOG = 235,
AKEYCODE_TV_TERRESTRIAL_DIGITAL = 236,
AKEYCODE_TV_SATELLITE = 237,
AKEYCODE_TV_SATELLITE_BS = 238,
AKEYCODE_TV_SATELLITE_CS = 239,
AKEYCODE_TV_SATELLITE_SERVICE = 240,
AKEYCODE_TV_NETWORK = 241,
AKEYCODE_TV_ANTENNA_CABLE = 242,
AKEYCODE_TV_INPUT_HDMI_1 = 243,
AKEYCODE_TV_INPUT_HDMI_2 = 244,
AKEYCODE_TV_INPUT_HDMI_3 = 245,
AKEYCODE_TV_INPUT_HDMI_4 = 246,
AKEYCODE_TV_INPUT_COMPOSITE_1 = 247,
AKEYCODE_TV_INPUT_COMPOSITE_2 = 248,
AKEYCODE_TV_INPUT_COMPONENT_1 = 249,
AKEYCODE_TV_INPUT_COMPONENT_2 = 250,
AKEYCODE_TV_INPUT_VGA_1 = 251,
AKEYCODE_TV_AUDIO_DESCRIPTION = 252,
AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP = 253,
AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN = 254,
AKEYCODE_TV_ZOOM_MODE = 255,
AKEYCODE_TV_CONTENTS_MENU = 256,
AKEYCODE_TV_MEDIA_CONTEXT_MENU = 257,
AKEYCODE_TV_TIMER_PROGRAMMING = 258,
AKEYCODE_HELP = 259,
// 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.
};
#ifdef __cplusplus
}
#endif
#endif // _ANDROID_KEYCODES_H

View file

@ -0,0 +1,569 @@
/*
* 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.
//
#if !defined(_LIBS_CUTILS_LOG_H) && !defined(_LIBS_LOG_LOG_H)
#define _LIBS_LOG_LOG_H
#define _LIBS_CUTILS_LOG_H
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef HAVE_PTHREADS
#include <pthread.h>
#endif
#include <stdarg.h>
#if ANDROID_VERSION >= 19
#include <log/uio.h>
#include <log/logd.h>
#else
#include <cutils/uio.h>
#include <cutils/logd.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
// ---------------------------------------------------------------------
/*
* Normally we strip ALOGV (VERBOSE messages) from release builds.
* You can modify this (for example with "#define LOG_NDEBUG 0"
* at the top of your source file) to change that behavior.
*/
#ifndef LOG_NDEBUG
#ifdef NDEBUG
#define LOG_NDEBUG 1
#else
#define LOG_NDEBUG 0
#endif
#endif
/*
* This is the local tag used for the following simplified
* logging macros. You can change this preprocessor definition
* before using the other macros to change the tag.
*/
#ifndef LOG_TAG
#define LOG_TAG NULL
#endif
// ---------------------------------------------------------------------
/*
* Simplified macro to send a verbose log message using the current LOG_TAG.
*/
#ifndef ALOGV
#if LOG_NDEBUG
#define ALOGV(...) ((void)0)
#else
#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#endif
#endif
#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
#ifndef ALOGV_IF
#if LOG_NDEBUG
#define ALOGV_IF(cond, ...) ((void)0)
#else
#define ALOGV_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
#endif
/*
* Simplified macro to send a debug log message using the current LOG_TAG.
*/
#ifndef ALOGD
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif
#ifndef ALOGD_IF
#define ALOGD_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send an info log message using the current LOG_TAG.
*/
#ifndef ALOGI
#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
#endif
#ifndef ALOGI_IF
#define ALOGI_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send a warning log message using the current LOG_TAG.
*/
#ifndef ALOGW
#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
#endif
#ifndef ALOGW_IF
#define ALOGW_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send an error log message using the current LOG_TAG.
*/
#ifndef ALOGE
#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif
#ifndef ALOGE_IF
#define ALOGE_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
// ---------------------------------------------------------------------
/*
* Conditional based on whether the current LOG_TAG is enabled at
* verbose priority.
*/
#ifndef IF_ALOGV
#if LOG_NDEBUG
#define IF_ALOGV() if (false)
#else
#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
#endif
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* debug priority.
*/
#ifndef IF_ALOGD
#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* info priority.
*/
#ifndef IF_ALOGI
#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* warn priority.
*/
#ifndef IF_ALOGW
#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* error priority.
*/
#ifndef IF_ALOGE
#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
#endif
// ---------------------------------------------------------------------
/*
* Simplified macro to send a verbose system log message using the current LOG_TAG.
*/
#ifndef SLOGV
#if LOG_NDEBUG
#define SLOGV(...) ((void)0)
#else
#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#endif
#endif
#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
#ifndef SLOGV_IF
#if LOG_NDEBUG
#define SLOGV_IF(cond, ...) ((void)0)
#else
#define SLOGV_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
#endif
/*
* Simplified macro to send a debug system log message using the current LOG_TAG.
*/
#ifndef SLOGD
#define SLOGD(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif
#ifndef SLOGD_IF
#define SLOGD_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send an info system log message using the current LOG_TAG.
*/
#ifndef SLOGI
#define SLOGI(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
#endif
#ifndef SLOGI_IF
#define SLOGI_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send a warning system log message using the current LOG_TAG.
*/
#ifndef SLOGW
#define SLOGW(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
#endif
#ifndef SLOGW_IF
#define SLOGW_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send an error system log message using the current LOG_TAG.
*/
#ifndef SLOGE
#define SLOGE(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif
#ifndef SLOGE_IF
#define SLOGE_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
: (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
// ---------------------------------------------------------------------
/*
* Log a fatal error. If the given condition fails, this stops program
* execution like a normal assertion, but also generating the given message.
* It is NOT stripped from release builds. Note that the condition test
* is -inverted- from the normal assert() semantics.
*/
#ifndef LOG_ALWAYS_FATAL_IF
#define LOG_ALWAYS_FATAL_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \
: (void)0 )
#endif
#ifndef LOG_ALWAYS_FATAL
#define LOG_ALWAYS_FATAL(...) \
( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) )
#endif
/*
* Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
* are stripped out of release builds.
*/
#if LOG_NDEBUG
#ifndef LOG_FATAL_IF
#define LOG_FATAL_IF(cond, ...) ((void)0)
#endif
#ifndef LOG_FATAL
#define LOG_FATAL(...) ((void)0)
#endif
#else
#ifndef LOG_FATAL_IF
#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__)
#endif
#ifndef LOG_FATAL
#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
#endif
#endif
/*
* Assertion that generates a log message when the assertion fails.
* Stripped out of release builds. Uses the current LOG_TAG.
*/
#ifndef ALOG_ASSERT
#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__)
//#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
#endif
// ---------------------------------------------------------------------
/*
* Basic log message macro.
*
* Example:
* ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
*
* The second argument may be NULL or "" to indicate the "global" tag.
*/
#ifndef ALOG
#define ALOG(priority, tag, ...) \
LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#endif
/*
* Log macro that allows you to specify a number for the priority.
*/
#ifndef LOG_PRI
#define LOG_PRI(priority, tag, ...) \
android_printLog(priority, tag, __VA_ARGS__)
#endif
/*
* Log macro that allows you to pass in a varargs ("args" is a va_list).
*/
#ifndef LOG_PRI_VA
#define LOG_PRI_VA(priority, tag, fmt, args) \
android_vprintLog(priority, NULL, tag, fmt, args)
#endif
/*
* Conditional given a desired logging priority and tag.
*/
#ifndef IF_ALOG
#define IF_ALOG(priority, tag) \
if (android_testLog(ANDROID_##priority, tag))
#endif
// ---------------------------------------------------------------------
/*
* Event logging.
*/
/*
* Event log entry types. These must match up with the declarations in
* java/android/android/util/EventLog.java.
*/
typedef enum {
EVENT_TYPE_INT = 0,
EVENT_TYPE_LONG = 1,
EVENT_TYPE_STRING = 2,
EVENT_TYPE_LIST = 3,
} AndroidEventLogType;
#ifndef LOG_EVENT_INT
#define LOG_EVENT_INT(_tag, _value) { \
int intBuf = _value; \
(void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \
sizeof(intBuf)); \
}
#endif
#ifndef LOG_EVENT_LONG
#define LOG_EVENT_LONG(_tag, _value) { \
long long longBuf = _value; \
(void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \
sizeof(longBuf)); \
}
#endif
#ifndef LOG_EVENT_STRING
#define LOG_EVENT_STRING(_tag, _value) \
((void) 0) /* not implemented -- must combine len with string */
#endif
/* TODO: something for LIST */
/*
* ===========================================================================
*
* The stuff in the rest of this file should not be used directly.
*/
#define android_printLog(prio, tag, fmt...) \
__android_log_print(prio, tag, fmt)
#define android_vprintLog(prio, cond, tag, fmt...) \
__android_log_vprint(prio, tag, fmt)
/* XXX Macros to work around syntax errors in places where format string
* arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
* (happens only in debug builds).
*/
/* Returns 2nd arg. Used to substitute default value if caller's vararg list
* is empty.
*/
#define __android_second(dummy, second, ...) second
/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
* returns nothing.
*/
#define __android_rest(first, ...) , ## __VA_ARGS__
#define android_printAssert(cond, tag, fmt...) \
__android_log_assert(cond, tag, \
__android_second(0, ## fmt, NULL) __android_rest(fmt))
#define android_writeLog(prio, tag, text) \
__android_log_write(prio, tag, text)
#define android_bWriteLog(tag, payload, len) \
__android_log_bwrite(tag, payload, len)
#define android_btWriteLog(tag, type, payload, len) \
__android_log_btwrite(tag, type, payload, len)
// TODO: remove these prototypes and their users
#define android_testLog(prio, tag) (1)
#define android_writevLog(vec,num) do{}while(0)
#define android_write1Log(str,len) do{}while (0)
#define android_setMinPriority(tag, prio) do{}while(0)
//#define android_logToCallback(func) do{}while(0)
#define android_logToFile(tag, file) (0)
#define android_logToFd(tag, fd) (0)
typedef enum {
LOG_ID_MAIN = 0,
LOG_ID_RADIO = 1,
LOG_ID_EVENTS = 2,
LOG_ID_SYSTEM = 3,
LOG_ID_MAX
} log_id_t;
/*
* Send a simple string to the log.
*/
int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text);
int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...);
#ifdef __cplusplus
}
#endif
#endif // _LIBS_CUTILS_LOG_H

View 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

289
widget/gonk/libui/sha1.c Normal file
View file

@ -0,0 +1,289 @@
/* $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $ */
/* $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ */
/*
* SHA-1 in C
* By Steve Reid <steve@edmweb.com>
* 100% Public Domain
*
* Test Vectors (from FIPS PUB 180-1)
* "abc"
* A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
* "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
* 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
* A million repetitions of "a"
* 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/
#define SHA1HANDSOFF /* Copies data before messing with it. */
#include <sys/cdefs.h>
#if defined(_KERNEL) || defined(_STANDALONE)
__KERNEL_RCSID(0, "$NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $");
#include <lib/libkern/libkern.h>
#else
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $");
#endif /* LIBC_SCCS and not lint */
#include <assert.h>
#include <string.h>
#endif
#include <sys/types.h>
#include "sha1.h"
#define _DIAGASSERT assert
#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif
#if !HAVE_SHA1_H
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/*
* blk0() and blk() perform the initial expand.
* I got the idea of expanding during the round function from SSLeay
*/
#if BYTE_ORDER == LITTLE_ENDIAN
# define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|(rol(block->l[i],8)&0x00FF00FF))
#else
# define blk0(i) block->l[i]
#endif
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
/*
* (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
*/
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
#if !defined(_KERNEL) && !defined(_STANDALONE)
#if defined(__weak_alias)
__weak_alias(SHA1Transform,_SHA1Transform)
__weak_alias(SHA1Init,_SHA1Init)
__weak_alias(SHA1Update,_SHA1Update)
__weak_alias(SHA1Final,_SHA1Final)
#endif
#endif
typedef union {
uint8_t c[64];
uint32_t l[16];
} CHAR64LONG16;
/* old sparc64 gcc could not compile this */
#undef SPARC64_GCC_WORKAROUND
#if defined(__sparc64__) && defined(__GNUC__) && __GNUC__ < 3
#define SPARC64_GCC_WORKAROUND
#endif
#ifdef SPARC64_GCC_WORKAROUND
void do_R01(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *);
void do_R2(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *);
void do_R3(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *);
void do_R4(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *);
#define nR0(v,w,x,y,z,i) R0(*v,*w,*x,*y,*z,i)
#define nR1(v,w,x,y,z,i) R1(*v,*w,*x,*y,*z,i)
#define nR2(v,w,x,y,z,i) R2(*v,*w,*x,*y,*z,i)
#define nR3(v,w,x,y,z,i) R3(*v,*w,*x,*y,*z,i)
#define nR4(v,w,x,y,z,i) R4(*v,*w,*x,*y,*z,i)
void
do_R01(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *block)
{
nR0(a,b,c,d,e, 0); nR0(e,a,b,c,d, 1); nR0(d,e,a,b,c, 2); nR0(c,d,e,a,b, 3);
nR0(b,c,d,e,a, 4); nR0(a,b,c,d,e, 5); nR0(e,a,b,c,d, 6); nR0(d,e,a,b,c, 7);
nR0(c,d,e,a,b, 8); nR0(b,c,d,e,a, 9); nR0(a,b,c,d,e,10); nR0(e,a,b,c,d,11);
nR0(d,e,a,b,c,12); nR0(c,d,e,a,b,13); nR0(b,c,d,e,a,14); nR0(a,b,c,d,e,15);
nR1(e,a,b,c,d,16); nR1(d,e,a,b,c,17); nR1(c,d,e,a,b,18); nR1(b,c,d,e,a,19);
}
void
do_R2(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *block)
{
nR2(a,b,c,d,e,20); nR2(e,a,b,c,d,21); nR2(d,e,a,b,c,22); nR2(c,d,e,a,b,23);
nR2(b,c,d,e,a,24); nR2(a,b,c,d,e,25); nR2(e,a,b,c,d,26); nR2(d,e,a,b,c,27);
nR2(c,d,e,a,b,28); nR2(b,c,d,e,a,29); nR2(a,b,c,d,e,30); nR2(e,a,b,c,d,31);
nR2(d,e,a,b,c,32); nR2(c,d,e,a,b,33); nR2(b,c,d,e,a,34); nR2(a,b,c,d,e,35);
nR2(e,a,b,c,d,36); nR2(d,e,a,b,c,37); nR2(c,d,e,a,b,38); nR2(b,c,d,e,a,39);
}
void
do_R3(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *block)
{
nR3(a,b,c,d,e,40); nR3(e,a,b,c,d,41); nR3(d,e,a,b,c,42); nR3(c,d,e,a,b,43);
nR3(b,c,d,e,a,44); nR3(a,b,c,d,e,45); nR3(e,a,b,c,d,46); nR3(d,e,a,b,c,47);
nR3(c,d,e,a,b,48); nR3(b,c,d,e,a,49); nR3(a,b,c,d,e,50); nR3(e,a,b,c,d,51);
nR3(d,e,a,b,c,52); nR3(c,d,e,a,b,53); nR3(b,c,d,e,a,54); nR3(a,b,c,d,e,55);
nR3(e,a,b,c,d,56); nR3(d,e,a,b,c,57); nR3(c,d,e,a,b,58); nR3(b,c,d,e,a,59);
}
void
do_R4(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, CHAR64LONG16 *block)
{
nR4(a,b,c,d,e,60); nR4(e,a,b,c,d,61); nR4(d,e,a,b,c,62); nR4(c,d,e,a,b,63);
nR4(b,c,d,e,a,64); nR4(a,b,c,d,e,65); nR4(e,a,b,c,d,66); nR4(d,e,a,b,c,67);
nR4(c,d,e,a,b,68); nR4(b,c,d,e,a,69); nR4(a,b,c,d,e,70); nR4(e,a,b,c,d,71);
nR4(d,e,a,b,c,72); nR4(c,d,e,a,b,73); nR4(b,c,d,e,a,74); nR4(a,b,c,d,e,75);
nR4(e,a,b,c,d,76); nR4(d,e,a,b,c,77); nR4(c,d,e,a,b,78); nR4(b,c,d,e,a,79);
}
#endif
/*
* Hash a single 512-bit block. This is the core of the algorithm.
*/
void SHA1Transform(uint32_t state[5], const uint8_t buffer[64])
{
uint32_t a, b, c, d, e;
CHAR64LONG16 *block;
#ifdef SHA1HANDSOFF
CHAR64LONG16 workspace;
#endif
_DIAGASSERT(buffer != 0);
_DIAGASSERT(state != 0);
#ifdef SHA1HANDSOFF
block = &workspace;
(void)memcpy(block, buffer, 64);
#else
block = (CHAR64LONG16 *)(void *)buffer;
#endif
/* Copy context->state[] to working vars */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
#ifdef SPARC64_GCC_WORKAROUND
do_R01(&a, &b, &c, &d, &e, block);
do_R2(&a, &b, &c, &d, &e, block);
do_R3(&a, &b, &c, &d, &e, block);
do_R4(&a, &b, &c, &d, &e, block);
#else
/* 4 rounds of 20 operations each. Loop unrolled. */
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
#endif
/* Add the working vars back into context.state[] */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
/* Wipe variables */
a = b = c = d = e = 0;
}
/*
* SHA1Init - Initialize new context
*/
void SHA1Init(SHA1_CTX *context)
{
_DIAGASSERT(context != 0);
/* SHA1 initialization constants */
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
/*
* Run your data through this.
*/
void SHA1Update(SHA1_CTX *context, const uint8_t *data, unsigned int len)
{
unsigned int i, j;
_DIAGASSERT(context != 0);
_DIAGASSERT(data != 0);
j = context->count[0];
if ((context->count[0] += len << 3) < j)
context->count[1] += (len>>29)+1;
j = (j >> 3) & 63;
if ((j + len) > 63) {
(void)memcpy(&context->buffer[j], data, (i = 64-j));
SHA1Transform(context->state, context->buffer);
for ( ; i + 63 < len; i += 64)
SHA1Transform(context->state, &data[i]);
j = 0;
} else {
i = 0;
}
(void)memcpy(&context->buffer[j], &data[i], len - i);
}
/*
* Add padding and return the message digest.
*/
void SHA1Final(uint8_t digest[20], SHA1_CTX *context)
{
unsigned int i;
uint8_t finalcount[8];
_DIAGASSERT(digest != 0);
_DIAGASSERT(context != 0);
for (i = 0; i < 8; i++) {
finalcount[i] = (uint8_t)((context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
}
SHA1Update(context, (const uint8_t *)"\200", 1);
while ((context->count[0] & 504) != 448)
SHA1Update(context, (const uint8_t *)"\0", 1);
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
if (digest) {
for (i = 0; i < 20; i++)
digest[i] = (uint8_t)
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
}
#endif /* HAVE_SHA1_H */

31
widget/gonk/libui/sha1.h Normal file
View file

@ -0,0 +1,31 @@
/* $NetBSD: sha1.h,v 1.13 2005/12/26 18:41:36 perry Exp $ */
/*
* SHA-1 in C
* By Steve Reid <steve@edmweb.com>
* 100% Public Domain
*/
#ifndef _SYS_SHA1_H_
#define _SYS_SHA1_H_
#include <sys/cdefs.h>
#include <sys/types.h>
#define SHA1_DIGEST_LENGTH 20
#define SHA1_DIGEST_STRING_LENGTH 41
typedef struct {
uint32_t state[5];
uint32_t count[2];
u_char buffer[64];
} SHA1_CTX;
__BEGIN_DECLS
void SHA1Transform(uint32_t[5], const u_char[64]);
void SHA1Init(SHA1_CTX *);
void SHA1Update(SHA1_CTX *, const u_char *, u_int);
void SHA1Final(u_char[SHA1_DIGEST_LENGTH], SHA1_CTX *);
__END_DECLS
#endif /* _SYS_SHA1_H_ */