diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h index 5be17d3d3..dab35b3cf 100644 --- a/include/ui/EventHub.h +++ b/include/ui/EventHub.h @@ -59,6 +59,31 @@ namespace android { class KeyLayoutMap; +/* + * A raw event as retrieved from the EventHub. + */ +struct RawEvent { + nsecs_t when; + int32_t deviceId; + int32_t type; + int32_t scanCode; + int32_t keyCode; + int32_t value; + uint32_t flags; +}; + +/* 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 + + inline int32_t getRange() { return maxValue - minValue; } +}; + /* * Input device classes. */ @@ -82,7 +107,10 @@ enum { INPUT_DEVICE_CLASS_DPAD = 0x00000020, /* The input device is a gamepad (implies keyboard). */ - INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040 + INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040, + + /* The input device has switches. */ + INPUT_DEVICE_CLASS_SWITCH = 0x00000080, }; /* @@ -114,8 +142,8 @@ public: virtual String8 getDeviceName(int32_t deviceId) const = 0; - virtual int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, - int* outMaxValue, int* outFlat, int* outFuzz) const = 0; + virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const = 0; virtual status_t scancodeToKeycode(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const = 0; @@ -131,26 +159,19 @@ public: * 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). */ - virtual bool getEvent(int32_t* outDeviceId, int32_t* outType, - int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, - int32_t* outValue, nsecs_t* outWhen) = 0; + virtual bool getEvent(RawEvent* outEvent) = 0; /* * Query current input state. - * deviceId may be -1 to search for the device automatically, filtered by class. - * deviceClasses may be -1 to ignore device class while searching. */ - virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t scanCode) const = 0; - virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t keyCode) const = 0; - virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses, - int32_t sw) const = 0; + 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; /* * Examine key input devices for specific framework keycode support */ - virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, + virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0; }; @@ -165,33 +186,28 @@ public: virtual String8 getDeviceName(int32_t deviceId) const; - virtual int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, - int* outMaxValue, int* outFlat, int* outFuzz) const; - + virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const; + virtual status_t scancodeToKeycode(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const; virtual void addExcludedDevice(const char* deviceName); - virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t scanCode) const; - virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t keyCode) const; - virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses, - int32_t sw) const; + 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 bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const; + virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) const; - virtual bool getEvent(int32_t* outDeviceId, int32_t* outType, - int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, - int32_t* outValue, nsecs_t* outWhen); + virtual bool getEvent(RawEvent* outEvent); protected: virtual ~EventHub(); private: bool openPlatformInput(void); - int32_t convertDeviceKey_TI_P2(int code); int open_device(const char *device); int close_device(const char *device); @@ -220,6 +236,8 @@ private: int32_t getScanCodeStateLocked(device_t* device, int32_t scanCode) const; int32_t getKeyCodeStateLocked(device_t* device, int32_t keyCode) const; int32_t getSwitchStateLocked(device_t* device, int32_t sw) const; + bool markSupportedKeyCodesLocked(device_t* device, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) const; // Protect all internal state. mutable Mutex mLock; diff --git a/include/ui/Input.h b/include/ui/Input.h index d9b109197..2385973e6 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -23,7 +23,10 @@ #include #include +#include #include +#include +#include /* * Additional private constants not defined in ndk/ui/input.h. @@ -47,21 +50,16 @@ struct AInputEvent { virtual ~AInputEvent() { } }; -namespace android { - /* - * A raw event as retrieved from the EventHub. + * Declare a concrete type for the NDK's input device forward declaration. */ -struct RawEvent { - nsecs_t when; - int32_t deviceId; - int32_t type; - int32_t scanCode; - int32_t keyCode; - int32_t value; - uint32_t flags; +struct AInputDevice { + virtual ~AInputDevice() { } }; + +namespace android { + /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. @@ -424,6 +422,69 @@ private: MotionEvent mMotionEvent; }; +/* + * Describes the characteristics and capabilities of an input device. + */ +class InputDeviceInfo { +public: + InputDeviceInfo(); + InputDeviceInfo(const InputDeviceInfo& other); + ~InputDeviceInfo(); + + struct MotionRange { + float min; + float max; + float flat; + float fuzz; + }; + + void initialize(int32_t id, const String8& name); + + inline int32_t getId() const { return mId; } + inline const String8 getName() const { return mName; } + inline uint32_t getSources() const { return mSources; } + + const MotionRange* getMotionRange(int32_t rangeType) const; + + void addSource(uint32_t source); + void addMotionRange(int32_t rangeType, float min, float max, float flat, float fuzz); + void addMotionRange(int32_t rangeType, const MotionRange& range); + + inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } + inline int32_t getKeyboardType() const { return mKeyboardType; } + +private: + int32_t mId; + String8 mName; + uint32_t mSources; + int32_t mKeyboardType; + + KeyedVector mMotionRanges; +}; + +/* + * Provides remote access to information about an input device. + * + * Note: This is essentially a wrapper for Binder calls into the Window Manager Service. + */ +class InputDeviceProxy : public RefBase, public AInputDevice { +protected: + InputDeviceProxy(); + virtual ~InputDeviceProxy(); + +public: + static void getDeviceIds(Vector& outIds); + + static sp getDevice(int32_t id); + + inline const InputDeviceInfo* getInfo() { return & mInfo; } + + // TODO add hasKeys, keymap, etc... + +private: + InputDeviceInfo mInfo; +}; + } // namespace android diff --git a/include/ui/InputDevice.h b/include/ui/InputDevice.h deleted file mode 100644 index 3b9c70e2d..000000000 --- a/include/ui/InputDevice.h +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUT_DEVICE_H -#define _UI_INPUT_DEVICE_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* Maximum pointer id value supported. - * (This is limited by our use of BitSet32 to track pointer assignments.) */ -#define MAX_POINTER_ID 31 - -/* Maximum number of historical samples to average. */ -#define AVERAGING_HISTORY_SIZE 5 - - -namespace android { - -extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState); -extern int32_t rotateKeyCode(int32_t keyCode, int32_t orientation); - - -/* - * An input device structure tracks the state of a single input device. - * - * This structure is only used by ReaderThread and is not intended to be shared with - * DispatcherThread (because that would require locking). This works out fine because - * DispatcherThread is only interested in cooked event data anyways and does not need - * any of the low-level data from InputDevice. - */ -struct InputDevice { - struct AbsoluteAxisInfo { - bool valid; // set to true if axis parameters are known, false otherwise - - int32_t minValue; // minimum value - int32_t maxValue; // maximum value - int32_t range; // range of values, equal to maxValue - minValue - 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 - }; - - struct VirtualKey { - int32_t keyCode; - int32_t scanCode; - uint32_t flags; - - // computed hit box, specified in touch screen coords based on known display size - int32_t hitLeft; - int32_t hitTop; - int32_t hitRight; - int32_t hitBottom; - - inline bool isHit(int32_t x, int32_t y) const { - return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom; - } - }; - - struct KeyboardState { - struct Current { - int32_t metaState; - nsecs_t downTime; // time of most recent key down - } current; - - void reset(); - }; - - struct TrackballState { - struct Accumulator { - enum { - FIELD_BTN_MOUSE = 1, - FIELD_REL_X = 2, - FIELD_REL_Y = 4 - }; - - uint32_t fields; - - bool btnMouse; - int32_t relX; - int32_t relY; - - inline void clear() { - fields = 0; - } - - inline bool isDirty() { - return fields != 0; - } - } accumulator; - - struct Current { - bool down; - nsecs_t downTime; - } current; - - struct Precalculated { - float xScale; - float yScale; - float xPrecision; - float yPrecision; - } precalculated; - - void reset(); - }; - - struct SingleTouchScreenState { - struct Accumulator { - enum { - FIELD_BTN_TOUCH = 1, - FIELD_ABS_X = 2, - FIELD_ABS_Y = 4, - FIELD_ABS_PRESSURE = 8, - FIELD_ABS_TOOL_WIDTH = 16 - }; - - uint32_t fields; - - bool btnTouch; - int32_t absX; - int32_t absY; - int32_t absPressure; - int32_t absToolWidth; - - inline void clear() { - fields = 0; - } - - inline bool isDirty() { - return fields != 0; - } - } accumulator; - - struct Current { - bool down; - int32_t x; - int32_t y; - int32_t pressure; - int32_t size; - } current; - - void reset(); - }; - - struct MultiTouchScreenState { - struct Accumulator { - enum { - FIELD_ABS_MT_POSITION_X = 1, - FIELD_ABS_MT_POSITION_Y = 2, - FIELD_ABS_MT_TOUCH_MAJOR = 4, - FIELD_ABS_MT_TOUCH_MINOR = 8, - FIELD_ABS_MT_WIDTH_MAJOR = 16, - FIELD_ABS_MT_WIDTH_MINOR = 32, - FIELD_ABS_MT_ORIENTATION = 64, - FIELD_ABS_MT_TRACKING_ID = 128 - }; - - uint32_t pointerCount; - struct Pointer { - uint32_t fields; - - int32_t absMTPositionX; - int32_t absMTPositionY; - int32_t absMTTouchMajor; - int32_t absMTTouchMinor; - int32_t absMTWidthMajor; - int32_t absMTWidthMinor; - int32_t absMTOrientation; - int32_t absMTTrackingId; - - inline void clear() { - fields = 0; - } - } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks - - inline void clear() { - pointerCount = 0; - pointers[0].clear(); - } - - inline bool isDirty() { - return pointerCount != 0; - } - } accumulator; - - void reset(); - }; - - struct PointerData { - uint32_t id; - int32_t x; - int32_t y; - int32_t pressure; - int32_t size; - int32_t touchMajor; - int32_t touchMinor; - int32_t toolMajor; - int32_t toolMinor; - int32_t orientation; - }; - - struct TouchData { - uint32_t pointerCount; - PointerData pointers[MAX_POINTERS]; - BitSet32 idBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; - - void copyFrom(const TouchData& other); - - inline void clear() { - pointerCount = 0; - idBits.clear(); - } - }; - - // common state used for both single-touch and multi-touch screens after the initial - // touch decoding has been performed - struct TouchScreenState { - Vector virtualKeys; - - struct Parameters { - bool useBadTouchFilter; - bool useJumpyTouchFilter; - bool useAveragingTouchFilter; - - AbsoluteAxisInfo xAxis; - AbsoluteAxisInfo yAxis; - AbsoluteAxisInfo pressureAxis; - AbsoluteAxisInfo sizeAxis; - AbsoluteAxisInfo orientationAxis; - } parameters; - - // The touch data of the current sample being processed. - TouchData currentTouch; - - // The touch data of the previous sample that was processed. This is updated - // incrementally while the current sample is being processed. - TouchData lastTouch; - - // The time the primary pointer last went down. - nsecs_t downTime; - - struct CurrentVirtualKeyState { - enum Status { - STATUS_UP, - STATUS_DOWN, - STATUS_CANCELED - }; - - Status status; - nsecs_t downTime; - int32_t keyCode; - int32_t scanCode; - } currentVirtualKey; - - struct AveragingTouchFilterState { - // Individual history tracks are stored by pointer id - uint32_t historyStart[MAX_POINTERS]; - uint32_t historyEnd[MAX_POINTERS]; - struct { - struct { - int32_t x; - int32_t y; - int32_t pressure; - } pointers[MAX_POINTERS]; - } historyData[AVERAGING_HISTORY_SIZE]; - } averagingTouchFilter; - - struct JumpTouchFilterState { - int32_t jumpyPointsDropped; - } jumpyTouchFilter; - - struct Precalculated { - int32_t xOrigin; - float xScale; - - int32_t yOrigin; - float yScale; - - int32_t pressureOrigin; - float pressureScale; - - int32_t sizeOrigin; - float sizeScale; - - float orientationScale; - } precalculated; - - void reset(); - - bool applyBadTouchFilter(); - bool applyJumpyTouchFilter(); - void applyAveragingTouchFilter(); - void calculatePointerIds(); - - bool isPointInsideDisplay(int32_t x, int32_t y) const; - const InputDevice::VirtualKey* findVirtualKeyHit() const; - }; - - InputDevice(int32_t id, uint32_t classes, String8 name); - - int32_t id; - uint32_t classes; - String8 name; - bool ignored; - - KeyboardState keyboard; - TrackballState trackball; - TouchScreenState touchScreen; - union { - SingleTouchScreenState singleTouchScreen; - MultiTouchScreenState multiTouchScreen; - }; - - void reset(); - - inline bool isKeyboard() const { return classes & INPUT_DEVICE_CLASS_KEYBOARD; } - inline bool isAlphaKey() const { return classes & INPUT_DEVICE_CLASS_ALPHAKEY; } - inline bool isTrackball() const { return classes & INPUT_DEVICE_CLASS_TRACKBALL; } - inline bool isDPad() const { return classes & INPUT_DEVICE_CLASS_DPAD; } - inline bool isSingleTouchScreen() const { return (classes - & (INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT)) - == INPUT_DEVICE_CLASS_TOUCHSCREEN; } - inline bool isMultiTouchScreen() const { return classes - & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT; } - inline bool isTouchScreen() const { return classes - & (INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT); } -}; - -} // namespace android - -#endif // _UI_INPUT_DEVICE_H diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h index e75523811..7ebec10dd 100644 --- a/include/ui/InputManager.h +++ b/include/ui/InputManager.h @@ -96,22 +96,28 @@ public: virtual void preemptInputDispatch() = 0; /* Gets input device configuration. */ - virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0; + virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0; - /* - * Queries current input state. - * deviceId may be -1 to search for the device automatically, filtered by class. - * deviceClasses may be -1 to ignore device class while searching. + /* Gets information about the specified input device. + * Returns OK if the device information was obtained or NAME_NOT_FOUND if there + * was no such device. */ - virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t scanCode) const = 0; - virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t keyCode) const = 0; - virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses, - int32_t sw) const = 0; + virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0; + + /* Gets the list of all registered device ids. */ + virtual void getInputDeviceIds(Vector& outDeviceIds) = 0; + + /* Queries current input state. */ + virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode) = 0; + virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode) = 0; + virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, + int32_t sw) = 0; /* Determines whether physical keys exist for the given framework-domain key codes. */ - virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0; + virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0; }; class InputManager : public InputManagerInterface { @@ -140,14 +146,17 @@ public: virtual void preemptInputDispatch(); - virtual void getInputConfiguration(InputConfiguration* outConfiguration) const; - virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t scanCode) const; - virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t keyCode) const; - virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses, - int32_t sw) const; - virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const; + virtual void getInputConfiguration(InputConfiguration* outConfiguration); + virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo); + virtual void getInputDeviceIds(Vector& outDeviceIds); + virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode); + virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode); + virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, + int32_t sw); + virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); private: sp mReader; diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h index 14bea6504..d7ec8ea64 100644 --- a/include/ui/InputReader.h +++ b/include/ui/InputReader.h @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -33,6 +32,10 @@ namespace android { +class InputDevice; +class InputMapper; + + /* * Input reader policy interface. * @@ -68,14 +71,6 @@ public: // The input dispatcher should perform special filtering in preparation for // a pending app switch. ACTION_APP_SWITCH_COMING = 0x00000002, - - // The input dispatcher should add POLICY_FLAG_WOKE_HERE to the policy flags it - // passes through the dispatch pipeline. - ACTION_WOKE_HERE = 0x00000004, - - // The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it - // passes through the dispatch pipeline. - ACTION_BRIGHT_HERE = 0x00000008, }; /* Describes a virtual key. */ @@ -101,38 +96,30 @@ public: /* Intercepts a key event. * The policy can use this method as an opportunity to perform power management functions - * and early event preprocessing. + * and early event preprocessing such as updating policy flags. * * Returns a policy action constant such as ACTION_DISPATCH. */ virtual int32_t interceptKey(nsecs_t when, int32_t deviceId, - bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) = 0; - - /* Intercepts a trackball event. - * The policy can use this method as an opportunity to perform power management functions - * and early event preprocessing. - * - * Returns a policy action constant such as ACTION_DISPATCH. - */ - virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown, - bool rolled) = 0; - - /* Intercepts a touch event. - * The policy can use this method as an opportunity to perform power management functions - * and early event preprocessing. - * - * Returns a policy action constant such as ACTION_DISPATCH. - */ - virtual int32_t interceptTouch(nsecs_t when) = 0; + bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) = 0; /* Intercepts a switch event. * The policy can use this method as an opportunity to perform power management functions - * and early event preprocessing. + * and early event preprocessing such as updating policy flags. * * Switches are not dispatched to applications so this method should * usually return ACTION_NONE. */ - virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) = 0; + virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, + uint32_t& policyFlags) = 0; + + /* Intercepts a generic touch, trackball or other event. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing such as updating policy flags. + * + * Returns a policy action constant such as ACTION_DISPATCH. + */ + virtual int32_t interceptGeneric(nsecs_t when, uint32_t& policyFlags) = 0; /* Determines whether to turn on some hacks we have to improve the touch interaction with a * certain device whose screen currently is not all that good. @@ -167,32 +154,52 @@ public: */ virtual void loopOnce() = 0; - /* Gets the current virtual key. Returns false if not down. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const = 0; - /* Gets the current input device configuration. * * This method may be called on any thread (usually by the input manager). */ - virtual void getCurrentInputConfiguration(InputConfiguration* outConfiguration) const = 0; + virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0; - /* - * Query current input state. - * deviceId may be -1 to search for the device automatically, filtered by class. - * deviceClasses may be -1 to ignore device class while searching. + /* Gets information about the specified input device. + * Returns OK if the device information was obtained or NAME_NOT_FOUND if there + * was no such device. + * + * This method may be called on any thread (usually by the input manager). */ - virtual int32_t getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t scanCode) const = 0; - virtual int32_t getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t keyCode) const = 0; - virtual int32_t getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses, - int32_t sw) const = 0; + virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0; + + /* Gets the list of all registered device ids. */ + virtual void getInputDeviceIds(Vector& outDeviceIds) = 0; + + /* Query current input state. */ + virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode) = 0; + virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode) = 0; + virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, + int32_t sw) = 0; /* Determine whether physical keys exist for the given framework-domain key codes. */ - virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0; + virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0; +}; + + +/* Internal interface used by individual input devices to access global input device state + * and parameters maintained by the input reader. + */ +class InputReaderContext { +protected: + InputReaderContext() { } + virtual ~InputReaderContext() { } + +public: + virtual void updateGlobalMetaState() = 0; + virtual int32_t getGlobalMetaState() = 0; + + virtual InputReaderPolicyInterface* getPolicy() = 0; + virtual InputDispatcherInterface* getDispatcher() = 0; + virtual EventHubInterface* getEventHub() = 0; }; @@ -201,10 +208,11 @@ public: * event filtering in low power states, are controlled by a separate policy object. * * IMPORTANT INVARIANT: - * Because the policy can potentially block or cause re-entrance into the input reader, - * the input reader never calls into the policy while holding its internal locks. + * Because the policy and dispatcher can potentially block or cause re-entrance into + * the input reader, the input reader never calls into other components while holding + * an exclusive internal lock. */ -class InputReader : public InputReaderInterface { +class InputReader : public InputReaderInterface, private InputReaderContext { public: InputReader(const sp& eventHub, const sp& policy, @@ -213,107 +221,69 @@ public: virtual void loopOnce(); - virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const; + virtual void getInputConfiguration(InputConfiguration* outConfiguration); - virtual void getCurrentInputConfiguration(InputConfiguration* outConfiguration) const; + virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo); + virtual void getInputDeviceIds(Vector& outDeviceIds); - virtual int32_t getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t scanCode) const; - virtual int32_t getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t keyCode) const; - virtual int32_t getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses, - int32_t sw) const; + virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode); + virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode); + virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, + int32_t sw); - virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const; + virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); private: - // Lock that must be acquired while manipulating state that may be concurrently accessed - // from other threads by input state query methods. It should be held for as short a - // time as possible. - // - // Exported state: - // - global virtual key code and scan code - // - device list and immutable properties of devices such as id, name, and class - // (but not other internal device state) - mutable Mutex mExportedStateLock; - - // current virtual key information (lock mExportedStateLock) - int32_t mExportedVirtualKeyCode; - int32_t mExportedVirtualScanCode; - - // current input configuration (lock mExportedStateLock) - InputConfiguration mExportedInputConfiguration; - - // combined key meta state - int32_t mGlobalMetaState; - sp mEventHub; sp mPolicy; sp mDispatcher; + virtual InputReaderPolicyInterface* getPolicy() { return mPolicy.get(); } + virtual InputDispatcherInterface* getDispatcher() { return mDispatcher.get(); } + virtual EventHubInterface* getEventHub() { return mEventHub.get(); } + + // This reader/writer lock guards the list of input devices. + // The writer lock must be held whenever the list of input devices is modified + // and then promptly released. + // The reader lock must be held whenever the list of input devices is traversed or an + // input device in the list is accessed. + // This lock only protects the registry and prevents inadvertent deletion of device objects + // that are in use. Individual devices are responsible for guarding their own internal state + // as needed for concurrent operation. + RWLock mDeviceRegistryLock; KeyedVector mDevices; - // display properties needed to translate touch screen coordinates into display coordinates - int32_t mDisplayOrientation; - int32_t mDisplayWidth; - int32_t mDisplayHeight; - - // low-level input event decoding + // low-level input event decoding and device management void process(const RawEvent* rawEvent); - void handleDeviceAdded(const RawEvent* rawEvent); - void handleDeviceRemoved(const RawEvent* rawEvent); - void handleSync(const RawEvent* rawEvent); - void handleKey(const RawEvent* rawEvent); - void handleRelativeMotion(const RawEvent* rawEvent); - void handleAbsoluteMotion(const RawEvent* rawEvent); - void handleSwitch(const RawEvent* rawEvent); - // input policy processing and dispatch - void onKey(nsecs_t when, InputDevice* device, bool down, - int32_t keyCode, int32_t scanCode, uint32_t policyFlags); - void onSwitch(nsecs_t when, InputDevice* device, int32_t switchCode, int32_t switchValue); - void onSingleTouchScreenStateChanged(nsecs_t when, InputDevice* device); - void onMultiTouchScreenStateChanged(nsecs_t when, InputDevice* device); - void onTouchScreenChanged(nsecs_t when, InputDevice* device, bool havePointerIds); - void onTrackballStateChanged(nsecs_t when, InputDevice* device); - void onConfigurationChanged(nsecs_t when); - - bool applyStandardInputDispatchPolicyActions(nsecs_t when, - int32_t policyActions, uint32_t* policyFlags); - - bool consumeVirtualKeyTouches(nsecs_t when, InputDevice* device, uint32_t policyFlags); - void dispatchVirtualKey(nsecs_t when, InputDevice* device, uint32_t policyFlags, - int32_t keyEventAction, int32_t keyEventFlags); - void dispatchTouches(nsecs_t when, InputDevice* device, uint32_t policyFlags); - void dispatchTouch(nsecs_t when, InputDevice* device, uint32_t policyFlags, - InputDevice::TouchData* touch, BitSet32 idBits, uint32_t changedId, - int32_t motionEventAction); - - // display - void resetDisplayProperties(); - bool refreshDisplayProperties(); - - // device management - InputDevice* getDevice(int32_t deviceId); - InputDevice* getNonIgnoredDevice(int32_t deviceId); void addDevice(nsecs_t when, int32_t deviceId); - void removeDevice(nsecs_t when, InputDevice* device); - void configureDevice(InputDevice* device); - void configureDeviceForCurrentDisplaySize(InputDevice* device); - void configureVirtualKeys(InputDevice* device); - void configureAbsoluteAxisInfo(InputDevice* device, int axis, const char* name, - InputDevice::AbsoluteAxisInfo* out); + void removeDevice(nsecs_t when, int32_t deviceId); + InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes); void configureExcludedDevices(); - // global meta state management for all devices - void resetGlobalMetaState(); - int32_t globalMetaState(); + void consumeEvent(const RawEvent* rawEvent); - // virtual key management - void updateExportedVirtualKeyState(); + void handleConfigurationChanged(nsecs_t when); - // input configuration management - void updateExportedInputConfiguration(); + // state management for all devices + Mutex mStateLock; + + int32_t mGlobalMetaState; + virtual void updateGlobalMetaState(); + virtual int32_t getGlobalMetaState(); + + InputConfiguration mInputConfiguration; + void updateInputConfiguration(); + + // state queries + typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code); + int32_t getState(int32_t deviceId, uint32_t sourceMask, int32_t code, + GetStateFunc getStateFunc); + bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); }; @@ -329,6 +299,527 @@ private: virtual bool threadLoop(); }; + +/* Represents the state of a single input device. */ +class InputDevice { +public: + InputDevice(InputReaderContext* context, int32_t id, const String8& name); + ~InputDevice(); + + inline InputReaderContext* getContext() { return mContext; } + inline int32_t getId() { return mId; } + inline const String8& getName() { return mName; } + inline uint32_t getSources() { return mSources; } + + inline bool isIgnored() { return mMappers.isEmpty(); } + + void addMapper(InputMapper* mapper); + void configure(); + void reset(); + void process(const RawEvent* rawEvent); + + void getDeviceInfo(InputDeviceInfo* outDeviceInfo); + int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + int32_t getMetaState(); + +private: + InputReaderContext* mContext; + int32_t mId; + + Vector mMappers; + + String8 mName; + uint32_t mSources; + + typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); + int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); +}; + + +/* An input mapper transforms raw input events into cooked event data. + * A single input device can have multiple associated input mappers in order to interpret + * different classes of events. + */ +class InputMapper { +public: + InputMapper(InputDevice* device); + virtual ~InputMapper(); + + inline InputDevice* getDevice() { return mDevice; } + inline int32_t getDeviceId() { return mDevice->getId(); } + inline const String8 getDeviceName() { return mDevice->getName(); } + inline InputReaderContext* getContext() { return mContext; } + inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } + inline InputDispatcherInterface* getDispatcher() { return mContext->getDispatcher(); } + inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } + + virtual uint32_t getSources() = 0; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void configure(); + virtual void reset(); + virtual void process(const RawEvent* rawEvent) = 0; + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual int32_t getMetaState(); + +protected: + InputDevice* mDevice; + InputReaderContext* mContext; + + bool applyStandardPolicyActions(nsecs_t when, int32_t policyActions); +}; + + +class SwitchInputMapper : public InputMapper { +public: + SwitchInputMapper(InputDevice* device); + virtual ~SwitchInputMapper(); + + virtual uint32_t getSources(); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + +private: + void processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue); +}; + + +class KeyboardInputMapper : public InputMapper { +public: + KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, uint32_t sources, + int32_t keyboardType); + virtual ~KeyboardInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void reset(); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual int32_t getMetaState(); + +private: + struct KeyDown { + int32_t keyCode; + int32_t scanCode; + }; + + int32_t mAssociatedDisplayId; + uint32_t mSources; + int32_t mKeyboardType; + + Vector mKeyDowns; // keys that are down + int32_t mMetaState; + nsecs_t mDownTime; // time of most recent key down + + void initialize(); + + bool isKeyboardOrGamepadKey(int32_t scanCode); + void processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode, + uint32_t policyFlags); + + ssize_t findKeyDown(int32_t scanCode); +}; + + +class TrackballInputMapper : public InputMapper { +public: + TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId); + virtual ~TrackballInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void reset(); + virtual void process(const RawEvent* rawEvent); + +private: + // Amount that trackball needs to move in order to generate a key event. + static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; + + int32_t mAssociatedDisplayId; + + struct Accumulator { + enum { + FIELD_BTN_MOUSE = 1, + FIELD_REL_X = 2, + FIELD_REL_Y = 4 + }; + + uint32_t fields; + + bool btnMouse; + int32_t relX; + int32_t relY; + + inline void clear() { + fields = 0; + } + + inline bool isDirty() { + return fields != 0; + } + } mAccumulator; + + bool mDown; + nsecs_t mDownTime; + + float mXScale; + float mYScale; + float mXPrecision; + float mYPrecision; + + void initialize(); + + void sync(nsecs_t when); +}; + + +class TouchInputMapper : public InputMapper { +public: + TouchInputMapper(InputDevice* device, int32_t associatedDisplayId); + virtual ~TouchInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void configure(); + virtual void reset(); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + +protected: + /* Maximum pointer id value supported. + * (This is limited by our use of BitSet32 to track pointer assignments.) */ + static const uint32_t MAX_POINTER_ID = 31; + + struct VirtualKey { + int32_t keyCode; + int32_t scanCode; + uint32_t flags; + + // computed hit box, specified in touch screen coords based on known display size + int32_t hitLeft; + int32_t hitTop; + int32_t hitRight; + int32_t hitBottom; + + inline bool isHit(int32_t x, int32_t y) const { + return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom; + } + }; + + struct PointerData { + uint32_t id; + int32_t x; + int32_t y; + int32_t pressure; + int32_t size; + int32_t touchMajor; + int32_t touchMinor; + int32_t toolMajor; + int32_t toolMinor; + int32_t orientation; + }; + + struct TouchData { + uint32_t pointerCount; + PointerData pointers[MAX_POINTERS]; + BitSet32 idBits; + uint32_t idToIndex[MAX_POINTER_ID + 1]; + + void copyFrom(const TouchData& other) { + pointerCount = other.pointerCount; + idBits = other.idBits; + + for (uint32_t i = 0; i < pointerCount; i++) { + pointers[i] = other.pointers[i]; + idToIndex[i] = other.idToIndex[i]; + } + } + + inline void clear() { + pointerCount = 0; + idBits.clear(); + } + }; + + int32_t mAssociatedDisplayId; + Vector mVirtualKeys; + + // Immutable configuration parameters. + struct Parameters { + bool useBadTouchFilter; + bool useJumpyTouchFilter; + bool useAveragingTouchFilter; + } mParameters; + + // Raw axis information. + struct Axes { + RawAbsoluteAxisInfo x; + RawAbsoluteAxisInfo y; + RawAbsoluteAxisInfo pressure; + RawAbsoluteAxisInfo size; + RawAbsoluteAxisInfo touchMajor; + RawAbsoluteAxisInfo touchMinor; + RawAbsoluteAxisInfo toolMajor; + RawAbsoluteAxisInfo toolMinor; + RawAbsoluteAxisInfo orientation; + } mAxes; + + // The surface orientation and width and height set by configureSurface(). + int32_t mSurfaceOrientation; + int32_t mSurfaceWidth, mSurfaceHeight; + + // Translation and scaling factors, orientation-independent. + int32_t mXOrigin; + float mXScale; + float mXPrecision; + + int32_t mYOrigin; + float mYScale; + float mYPrecision; + + int32_t mPressureOrigin; + float mPressureScale; + + int32_t mSizeOrigin; + float mSizeScale; + + float mOrientationScale; + + // Oriented motion ranges for input device info. + struct OrientedRanges { + InputDeviceInfo::MotionRange x; + InputDeviceInfo::MotionRange y; + InputDeviceInfo::MotionRange pressure; + InputDeviceInfo::MotionRange size; + InputDeviceInfo::MotionRange touchMajor; + InputDeviceInfo::MotionRange touchMinor; + InputDeviceInfo::MotionRange toolMajor; + InputDeviceInfo::MotionRange toolMinor; + InputDeviceInfo::MotionRange orientation; + } mOrientedRanges; + + // Oriented dimensions and precision. + float mOrientedSurfaceWidth, mOrientedSurfaceHeight; + float mOrientedXPrecision, mOrientedYPrecision; + + // The touch data of the current sample being processed. + TouchData mCurrentTouch; + + // The touch data of the previous sample that was processed. This is updated + // incrementally while the current sample is being processed. + TouchData mLastTouch; + + // The time the primary pointer last went down. + nsecs_t mDownTime; + + struct CurrentVirtualKeyState { + bool down; + nsecs_t downTime; + int32_t keyCode; + int32_t scanCode; + } mCurrentVirtualKey; + + // Lock for virtual key state. + Mutex mVirtualKeyLock; // methods use "Lvk" suffix + + virtual void configureAxes(); + virtual bool configureSurface(); + virtual void configureVirtualKeys(); + + enum TouchResult { + // Dispatch the touch normally. + DISPATCH_TOUCH, + // Do not dispatch the touch, but keep tracking the current stroke. + SKIP_TOUCH, + // Do not dispatch the touch, and drop all information associated with the current stoke + // so the next movement will appear as a new down. + DROP_STROKE + }; + + void syncTouch(nsecs_t when, bool havePointerIds); + +private: + /* Maximum number of historical samples to average. */ + static const uint32_t AVERAGING_HISTORY_SIZE = 5; + + /* Slop distance for jumpy pointer detection. + * The vertical range of the screen divided by this is our epsilon value. */ + static const uint32_t JUMPY_EPSILON_DIVISOR = 212; + + /* Number of jumpy points to drop for touchscreens that need it. */ + static const uint32_t JUMPY_TRANSITION_DROPS = 3; + static const uint32_t JUMPY_DROP_LIMIT = 3; + + /* Maximum squared distance for averaging. + * If moving farther than this, turn of averaging to avoid lag in response. */ + static const uint64_t AVERAGING_DISTANCE_LIMIT = 75 * 75; + + struct AveragingTouchFilterState { + // Individual history tracks are stored by pointer id + uint32_t historyStart[MAX_POINTERS]; + uint32_t historyEnd[MAX_POINTERS]; + struct { + struct { + int32_t x; + int32_t y; + int32_t pressure; + } pointers[MAX_POINTERS]; + } historyData[AVERAGING_HISTORY_SIZE]; + } mAveragingTouchFilter; + + struct JumpTouchFilterState { + uint32_t jumpyPointsDropped; + } mJumpyTouchFilter; + + struct PointerDistanceHeapElement { + uint32_t currentPointerIndex : 8; + uint32_t lastPointerIndex : 8; + uint64_t distance : 48; // squared distance + }; + + void initialize(); + + TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags); + void dispatchTouches(nsecs_t when, uint32_t policyFlags); + void dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch, + BitSet32 idBits, uint32_t changedId, int32_t motionEventAction); + + bool isPointInsideSurface(int32_t x, int32_t y); + const VirtualKey* findVirtualKeyHitLvk(int32_t x, int32_t y); + + bool applyBadTouchFilter(); + bool applyJumpyTouchFilter(); + void applyAveragingTouchFilter(); + void calculatePointerIds(); +}; + + +class SingleTouchInputMapper : public TouchInputMapper { +public: + SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId); + virtual ~SingleTouchInputMapper(); + + virtual void reset(); + virtual void process(const RawEvent* rawEvent); + +protected: + virtual void configureAxes(); + +private: + struct Accumulator { + enum { + FIELD_BTN_TOUCH = 1, + FIELD_ABS_X = 2, + FIELD_ABS_Y = 4, + FIELD_ABS_PRESSURE = 8, + FIELD_ABS_TOOL_WIDTH = 16 + }; + + uint32_t fields; + + bool btnTouch; + int32_t absX; + int32_t absY; + int32_t absPressure; + int32_t absToolWidth; + + inline void clear() { + fields = 0; + } + + inline bool isDirty() { + return fields != 0; + } + } mAccumulator; + + bool mDown; + int32_t mX; + int32_t mY; + int32_t mPressure; + int32_t mSize; + + void initialize(); + + void sync(nsecs_t when); +}; + + +class MultiTouchInputMapper : public TouchInputMapper { +public: + MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId); + virtual ~MultiTouchInputMapper(); + + virtual void reset(); + virtual void process(const RawEvent* rawEvent); + +protected: + virtual void configureAxes(); + +private: + struct Accumulator { + enum { + FIELD_ABS_MT_POSITION_X = 1, + FIELD_ABS_MT_POSITION_Y = 2, + FIELD_ABS_MT_TOUCH_MAJOR = 4, + FIELD_ABS_MT_TOUCH_MINOR = 8, + FIELD_ABS_MT_WIDTH_MAJOR = 16, + FIELD_ABS_MT_WIDTH_MINOR = 32, + FIELD_ABS_MT_ORIENTATION = 64, + FIELD_ABS_MT_TRACKING_ID = 128 + }; + + uint32_t pointerCount; + struct Pointer { + uint32_t fields; + + int32_t absMTPositionX; + int32_t absMTPositionY; + int32_t absMTTouchMajor; + int32_t absMTTouchMinor; + int32_t absMTWidthMajor; + int32_t absMTWidthMinor; + int32_t absMTOrientation; + int32_t absMTTrackingId; + + inline void clear() { + fields = 0; + } + } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks + + inline void clear() { + pointerCount = 0; + pointers[0].clear(); + } + + inline bool isDirty() { + return pointerCount != 0; + } + } mAccumulator; + + void initialize(); + + void sync(nsecs_t when); +}; + } // namespace android #endif // _UI_INPUT_READER_H diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index 4243bbf47..9f493483b 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -12,7 +12,6 @@ LOCAL_SRC_FILES:= \ KeyLayoutMap.cpp \ KeyCharacterMap.cpp \ Input.cpp \ - InputDevice.cpp \ InputDispatcher.cpp \ InputManager.cpp \ InputReader.cpp \ diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index 33dd3732b..124f7b3cd 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -137,9 +137,14 @@ uint32_t EventHub::getDeviceClasses(int32_t deviceId) const return device->classes; } -int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, - int* outMaxValue, int* outFlat, int* outFuzz) const -{ +status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const { + outAxisInfo->valid = false; + outAxisInfo->minValue = 0; + outAxisInfo->maxValue = 0; + outAxisInfo->flat = 0; + outAxisInfo->fuzz = 0; + AutoMutex _l(mLock); device_t* device = getDevice(deviceId); if (device == NULL) return -1; @@ -147,38 +152,28 @@ int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, struct input_absinfo info; if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) { - LOGE("Error reading absolute controller %d for device %s fd %d\n", + LOGW("Error reading absolute controller %d for device %s fd %d\n", axis, device->name.string(), mFDs[id_to_index(device->id)].fd); - return -1; + return -errno; } - *outMinValue = info.minimum; - *outMaxValue = info.maximum; - *outFlat = info.flat; - *outFuzz = info.fuzz; - return 0; + + if (info.minimum != info.maximum) { + outAxisInfo->valid = true; + outAxisInfo->minValue = info.minimum; + outAxisInfo->maxValue = info.maximum; + outAxisInfo->flat = info.flat; + outAxisInfo->fuzz = info.fuzz; + } + return OK; } -int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t scanCode) const { +int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { if (scanCode >= 0 && scanCode <= KEY_MAX) { AutoMutex _l(mLock); - if (deviceId == -1) { - for (int i = 0; i < mNumDevicesById; i++) { - device_t* device = mDevicesById[i].device; - if (device != NULL && (device->classes & deviceClasses) != 0) { - int32_t result = getScanCodeStateLocked(device, scanCode); - if (result >= AKEY_STATE_DOWN) { - return result; - } - } - } - return AKEY_STATE_UP; - } else { - device_t* device = getDevice(deviceId); - if (device != NULL) { - return getScanCodeStateLocked(device, scanCode); - } + device_t* device = getDevice(deviceId); + if (device != NULL) { + return getScanCodeStateLocked(device, scanCode); } } return AKEY_STATE_UNKNOWN; @@ -194,25 +189,12 @@ int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) con return AKEY_STATE_UNKNOWN; } -int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t keyCode) const { +int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { + AutoMutex _l(mLock); - if (deviceId == -1) { - for (int i = 0; i < mNumDevicesById; i++) { - device_t* device = mDevicesById[i].device; - if (device != NULL && (device->classes & deviceClasses) != 0) { - int32_t result = getKeyCodeStateLocked(device, keyCode); - if (result >= AKEY_STATE_DOWN) { - return result; - } - } - } - return AKEY_STATE_UP; - } else { - device_t* device = getDevice(deviceId); - if (device != NULL) { - return getKeyCodeStateLocked(device, keyCode); - } + device_t* device = getDevice(deviceId); + if (device != NULL) { + return getKeyCodeStateLocked(device, keyCode); } return AKEY_STATE_UNKNOWN; } @@ -243,24 +225,15 @@ int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const return AKEY_STATE_UNKNOWN; } -int32_t EventHub::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const { +int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { #ifdef EV_SW if (sw >= 0 && sw <= SW_MAX) { AutoMutex _l(mLock); - if (deviceId == -1) { - deviceId = mSwitches[sw]; - if (deviceId == 0) { - return AKEY_STATE_UNKNOWN; - } - } - device_t* device = getDevice(deviceId); - if (device == NULL) { - return AKEY_STATE_UNKNOWN; + if (device != NULL) { + return getSwitchStateLocked(device, sw); } - - return getSwitchStateLocked(device, sw); } #endif return AKEY_STATE_UNKNOWN; @@ -276,6 +249,42 @@ int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const { return AKEY_STATE_UNKNOWN; } +bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) const { + AutoMutex _l(mLock); + + device_t* device = getDevice(deviceId); + if (device != NULL) { + return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags); + } + return false; +} + +bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) const { + if (device->layoutMap == NULL || device->keyBitmask == NULL) { + return false; + } + + Vector scanCodes; + for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { + scanCodes.clear(); + + status_t err = device->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes); + if (! err) { + // check the possible scan codes identified by the layout map against the + // map of codes actually emitted by the driver + for (size_t sc = 0; sc < scanCodes.size(); sc++) { + if (test_bit(scanCodes[sc], device->keyBitmask)) { + outFlags[codeIndex] = 1; + break; + } + } + } + } + return true; +} + status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode, int32_t* outKeycode, uint32_t* outFlags) const { @@ -324,17 +333,15 @@ EventHub::device_t* EventHub::getDevice(int32_t deviceId) const return NULL; } -bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, - int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, - int32_t* outValue, nsecs_t* outWhen) +bool EventHub::getEvent(RawEvent* outEvent) { - *outDeviceId = 0; - *outType = 0; - *outScancode = 0; - *outKeycode = 0; - *outFlags = 0; - *outValue = 0; - *outWhen = 0; + outEvent->deviceId = 0; + outEvent->type = 0; + outEvent->scanCode = 0; + outEvent->keyCode = 0; + outEvent->flags = 0; + outEvent->value = 0; + outEvent->when = 0; status_t err; @@ -359,20 +366,27 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, LOGV("Reporting device closed: id=0x%x, name=%s\n", device->id, device->path.string()); mClosingDevices = device->next; - *outDeviceId = device->id; - if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; - *outType = DEVICE_REMOVED; + if (device->id == mFirstKeyboardId) { + outEvent->deviceId = 0; + } else { + outEvent->deviceId = device->id; + } + outEvent->type = DEVICE_REMOVED; delete device; return true; } + if (mOpeningDevices != NULL) { device_t* device = mOpeningDevices; LOGV("Reporting device opened: id=0x%x, name=%s\n", device->id, device->path.string()); mOpeningDevices = device->next; - *outDeviceId = device->id; - if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; - *outType = DEVICE_ADDED; + if (device->id == mFirstKeyboardId) { + outEvent->deviceId = 0; + } else { + outEvent->deviceId = device->id; + } + outEvent->type = DEVICE_ADDED; return true; } @@ -399,27 +413,36 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, if(mFDs[i].revents & POLLIN) { res = read(mFDs[i].fd, &iev, sizeof(iev)); if (res == sizeof(iev)) { + device_t* device = mDevices[i]; LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", - mDevices[i]->path.string(), + device->path.string(), (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); - *outDeviceId = mDevices[i]->id; - if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; - *outType = iev.type; - *outScancode = iev.code; + if (device->id == mFirstKeyboardId) { + outEvent->deviceId = 0; + } else { + outEvent->deviceId = device->id; + } + outEvent->type = iev.type; + outEvent->scanCode = iev.code; if (iev.type == EV_KEY) { - err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags); - LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n", - iev.code, *outKeycode, *outFlags, err); + err = device->layoutMap->map(iev.code, + & outEvent->keyCode, & outEvent->flags); + LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", + iev.code, outEvent->keyCode, outEvent->flags, err); if (err != 0) { - *outKeycode = AKEYCODE_UNKNOWN; - *outFlags = 0; + outEvent->keyCode = AKEYCODE_UNKNOWN; + outEvent->flags = 0; } } else { - *outKeycode = iev.code; + outEvent->keyCode = iev.code; } - *outValue = iev.value; - *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec); + outEvent->value = iev.value; + + // Use an event timestamp in the same timebase as + // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis() + // as expected by the rest of the system. + outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); return true; } else { if (res<0) { @@ -479,37 +502,6 @@ bool EventHub::openPlatformInput(void) return true; } -/* - * Inspect the known devices to determine whether physical keys exist for the given - * framework-domain key codes. - */ -bool EventHub::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { - for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { - outFlags[codeIndex] = 0; - - // check each available hardware device for support for this keycode - Vector scanCodes; - for (int n = 0; (n < mFDCount) && (outFlags[codeIndex] == 0); n++) { - if (mDevices[n]) { - status_t err = mDevices[n]->layoutMap->findScancodes( - keyCodes[codeIndex], &scanCodes); - if (!err) { - // check the possible scan codes identified by the layout map against the - // map of codes actually emitted by the driver - for (size_t sc = 0; sc < scanCodes.size(); sc++) { - if (test_bit(scanCodes[sc], mDevices[n]->keyBitmask)) { - outFlags[codeIndex] = 1; - break; - } - } - } - } - } - } - - return true; -} - // ---------------------------------------------------------------------------- static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) { @@ -715,16 +707,21 @@ int EventHub::open_device(const char *deviceName) // figure out the switches this device reports uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)]; memset(sw_bitmask, 0, sizeof(sw_bitmask)); + bool hasSwitches = false; if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) { for (int i=0; iid, i, test_bit(i, sw_bitmask)); if (test_bit(i, sw_bitmask)) { + hasSwitches = true; if (mSwitches[i] == 0) { mSwitches[i] = device->id; } } } } + if (hasSwitches) { + device->classes |= INPUT_DEVICE_CLASS_SWITCH; + } #endif if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) { diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index 5253c72aa..5fbaf0961 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -168,4 +168,63 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) { mYOffset += yOffset; } +// class InputDeviceInfo + +InputDeviceInfo::InputDeviceInfo() { + initialize(-1, String8("uninitialized device info")); +} + +InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : + mId(other.mId), mName(other.mName), mSources(other.mSources), + mKeyboardType(other.mKeyboardType), + mMotionRanges(other.mMotionRanges) { +} + +InputDeviceInfo::~InputDeviceInfo() { +} + +void InputDeviceInfo::initialize(int32_t id, const String8& name) { + mId = id; + mName = name; + mSources = 0; + mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; + mMotionRanges.clear(); +} + +const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t rangeType) const { + ssize_t index = mMotionRanges.indexOfKey(rangeType); + return index >= 0 ? & mMotionRanges.valueAt(index) : NULL; +} + +void InputDeviceInfo::addSource(uint32_t source) { + mSources |= source; +} + +void InputDeviceInfo::addMotionRange(int32_t rangeType, float min, float max, + float flat, float fuzz) { + MotionRange range = { min, max, flat, fuzz }; + addMotionRange(rangeType, range); +} + +void InputDeviceInfo::addMotionRange(int32_t rangeType, const MotionRange& range) { + mMotionRanges.add(rangeType, range); +} + +// class InputDeviceProxy + +InputDeviceProxy::InputDeviceProxy() { +} + +InputDeviceProxy::~InputDeviceProxy() { +} + +void InputDeviceProxy::getDeviceIds(Vector& outIds) { + // TODO use Binder +} + +sp InputDeviceProxy::getDevice(int32_t id) { + // TODO use Binder + return NULL; +} + } // namespace android diff --git a/libs/ui/InputDevice.cpp b/libs/ui/InputDevice.cpp deleted file mode 100644 index b2a4d6c8f..000000000 --- a/libs/ui/InputDevice.cpp +++ /dev/null @@ -1,729 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// -// The input reader. -// -#define LOG_TAG "InputDevice" - -//#define LOG_NDEBUG 0 - -// Log debug messages for each raw event received from the EventHub. -#define DEBUG_RAW_EVENTS 0 - -// Log debug messages about touch screen filtering hacks. -#define DEBUG_HACKS 0 - -// Log debug messages about virtual key processing. -#define DEBUG_VIRTUAL_KEYS 0 - -// Log debug messages about pointers. -#define DEBUG_POINTERS 0 - -// Log debug messages about pointer assignment calculations. -#define DEBUG_POINTER_ASSIGNMENT 0 - -#include -#include - -#include -#include -#include -#include - -/* Slop distance for jumpy pointer detection. - * The vertical range of the screen divided by this is our epsilon value. */ -#define JUMPY_EPSILON_DIVISOR 212 - -/* Number of jumpy points to drop for touchscreens that need it. */ -#define JUMPY_TRANSITION_DROPS 3 -#define JUMPY_DROP_LIMIT 3 - -/* Maximum squared distance for averaging. - * If moving farther than this, turn of averaging to avoid lag in response. */ -#define AVERAGING_DISTANCE_LIMIT (75 * 75) - - -namespace android { - -// --- Static Functions --- - -template -inline static T abs(const T& value) { - return value < 0 ? - value : value; -} - -template -inline static T min(const T& a, const T& b) { - return a < b ? a : b; -} - -template -inline static void swap(T& a, T& b) { - T temp = a; - a = b; - b = temp; -} - - -// --- InputDevice --- - -InputDevice::InputDevice(int32_t id, uint32_t classes, String8 name) : - id(id), classes(classes), name(name), ignored(false) { -} - -void InputDevice::reset() { - if (isKeyboard()) { - keyboard.reset(); - } - - if (isTrackball()) { - trackball.reset(); - } - - if (isMultiTouchScreen()) { - multiTouchScreen.reset(); - } else if (isSingleTouchScreen()) { - singleTouchScreen.reset(); - } - - if (isTouchScreen()) { - touchScreen.reset(); - } -} - - -// --- InputDevice::TouchData --- - -void InputDevice::TouchData::copyFrom(const TouchData& other) { - pointerCount = other.pointerCount; - idBits = other.idBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointers[i] = other.pointers[i]; - idToIndex[i] = other.idToIndex[i]; - } -} - - -// --- InputDevice::KeyboardState --- - -void InputDevice::KeyboardState::reset() { - current.metaState = AMETA_NONE; - current.downTime = 0; -} - - -// --- InputDevice::TrackballState --- - -void InputDevice::TrackballState::reset() { - accumulator.clear(); - current.down = false; - current.downTime = 0; -} - - -// --- InputDevice::TouchScreenState --- - -void InputDevice::TouchScreenState::reset() { - lastTouch.clear(); - downTime = 0; - currentVirtualKey.status = CurrentVirtualKeyState::STATUS_UP; - - for (uint32_t i = 0; i < MAX_POINTERS; i++) { - averagingTouchFilter.historyStart[i] = 0; - averagingTouchFilter.historyEnd[i] = 0; - } - - jumpyTouchFilter.jumpyPointsDropped = 0; -} - -struct PointerDistanceHeapElement { - uint32_t currentPointerIndex : 8; - uint32_t lastPointerIndex : 8; - uint64_t distance : 48; // squared distance -}; - -void InputDevice::TouchScreenState::calculatePointerIds() { - uint32_t currentPointerCount = currentTouch.pointerCount; - uint32_t lastPointerCount = lastTouch.pointerCount; - - if (currentPointerCount == 0) { - // No pointers to assign. - currentTouch.idBits.clear(); - } else if (lastPointerCount == 0) { - // All pointers are new. - currentTouch.idBits.clear(); - for (uint32_t i = 0; i < currentPointerCount; i++) { - currentTouch.pointers[i].id = i; - currentTouch.idToIndex[i] = i; - currentTouch.idBits.markBit(i); - } - } else if (currentPointerCount == 1 && lastPointerCount == 1) { - // Only one pointer and no change in count so it must have the same id as before. - uint32_t id = lastTouch.pointers[0].id; - currentTouch.pointers[0].id = id; - currentTouch.idToIndex[id] = 0; - currentTouch.idBits.value = BitSet32::valueForBit(id); - } else { - // General case. - // We build a heap of squared euclidean distances between current and last pointers - // associated with the current and last pointer indices. Then, we find the best - // match (by distance) for each current pointer. - PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; - - uint32_t heapSize = 0; - for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; - currentPointerIndex++) { - for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; - lastPointerIndex++) { - int64_t deltaX = currentTouch.pointers[currentPointerIndex].x - - lastTouch.pointers[lastPointerIndex].x; - int64_t deltaY = currentTouch.pointers[currentPointerIndex].y - - lastTouch.pointers[lastPointerIndex].y; - - uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); - - // Insert new element into the heap (sift up). - heap[heapSize].currentPointerIndex = currentPointerIndex; - heap[heapSize].lastPointerIndex = lastPointerIndex; - heap[heapSize].distance = distance; - heapSize += 1; - } - } - - // Heapify - for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) { - startIndex -= 1; - for (uint32_t parentIndex = startIndex; ;) { - uint32_t childIndex = parentIndex * 2 + 1; - if (childIndex >= heapSize) { - break; - } - - if (childIndex + 1 < heapSize - && heap[childIndex + 1].distance < heap[childIndex].distance) { - childIndex += 1; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - } - -#if DEBUG_POINTER_ASSIGNMENT - LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", - i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, - heap[i].distance); - } -#endif - - // Pull matches out by increasing order of distance. - // To avoid reassigning pointers that have already been matched, the loop keeps track - // of which last and current pointers have been matched using the matchedXXXBits variables. - // It also tracks the used pointer id bits. - BitSet32 matchedLastBits(0); - BitSet32 matchedCurrentBits(0); - BitSet32 usedIdBits(0); - bool first = true; - for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) { - for (;;) { - if (first) { - // The first time through the loop, we just consume the root element of - // the heap (the one with smallest distance). - first = false; - } else { - // Previous iterations consumed the root element of the heap. - // Pop root element off of the heap (sift down). - heapSize -= 1; - assert(heapSize > 0); - - // Sift down. - heap[0] = heap[heapSize]; - for (uint32_t parentIndex = 0; ;) { - uint32_t childIndex = parentIndex * 2 + 1; - if (childIndex >= heapSize) { - break; - } - - if (childIndex + 1 < heapSize - && heap[childIndex + 1].distance < heap[childIndex].distance) { - childIndex += 1; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - -#if DEBUG_POINTER_ASSIGNMENT - LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", - i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, - heap[i].distance); - } -#endif - } - - uint32_t currentPointerIndex = heap[0].currentPointerIndex; - if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched - - uint32_t lastPointerIndex = heap[0].lastPointerIndex; - if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched - - matchedCurrentBits.markBit(currentPointerIndex); - matchedLastBits.markBit(lastPointerIndex); - - uint32_t id = lastTouch.pointers[lastPointerIndex].id; - currentTouch.pointers[currentPointerIndex].id = id; - currentTouch.idToIndex[id] = currentPointerIndex; - usedIdBits.markBit(id); - -#if DEBUG_POINTER_ASSIGNMENT - LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld", - lastPointerIndex, currentPointerIndex, id, heap[0].distance); -#endif - break; - } - } - - // Assign fresh ids to new pointers. - if (currentPointerCount > lastPointerCount) { - for (uint32_t i = currentPointerCount - lastPointerCount; ;) { - uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit(); - uint32_t id = usedIdBits.firstUnmarkedBit(); - - currentTouch.pointers[currentPointerIndex].id = id; - currentTouch.idToIndex[id] = currentPointerIndex; - usedIdBits.markBit(id); - -#if DEBUG_POINTER_ASSIGNMENT - LOGD("calculatePointerIds - assigned: cur=%d, id=%d", - currentPointerIndex, id); -#endif - - if (--i == 0) break; // done - matchedCurrentBits.markBit(currentPointerIndex); - } - } - - // Fix id bits. - currentTouch.idBits = usedIdBits; - } -} - -/* Special hack for devices that have bad screen data: if one of the - * points has moved more than a screen height from the last position, - * then drop it. */ -bool InputDevice::TouchScreenState::applyBadTouchFilter() { - // This hack requires valid axis parameters. - if (! parameters.yAxis.valid) { - return false; - } - - uint32_t pointerCount = currentTouch.pointerCount; - - // Nothing to do if there are no points. - if (pointerCount == 0) { - return false; - } - - // Don't do anything if a finger is going down or up. We run - // here before assigning pointer IDs, so there isn't a good - // way to do per-finger matching. - if (pointerCount != lastTouch.pointerCount) { - return false; - } - - // We consider a single movement across more than a 7/16 of - // the long size of the screen to be bad. This was a magic value - // determined by looking at the maximum distance it is feasible - // to actually move in one sample. - int32_t maxDeltaY = parameters.yAxis.range * 7 / 16; - - // XXX The original code in InputDevice.java included commented out - // code for testing the X axis. Note that when we drop a point - // we don't actually restore the old X either. Strange. - // The old code also tries to track when bad points were previously - // detected but it turns out that due to the placement of a "break" - // at the end of the loop, we never set mDroppedBadPoint to true - // so it is effectively dead code. - // Need to figure out if the old code is busted or just overcomplicated - // but working as intended. - - // Look through all new points and see if any are farther than - // acceptable from all previous points. - for (uint32_t i = pointerCount; i-- > 0; ) { - int32_t y = currentTouch.pointers[i].y; - int32_t closestY = INT_MAX; - int32_t closestDeltaY = 0; - -#if DEBUG_HACKS - LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y); -#endif - - for (uint32_t j = pointerCount; j-- > 0; ) { - int32_t lastY = lastTouch.pointers[j].y; - int32_t deltaY = abs(y - lastY); - -#if DEBUG_HACKS - LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d", - j, lastY, deltaY); -#endif - - if (deltaY < maxDeltaY) { - goto SkipSufficientlyClosePoint; - } - if (deltaY < closestDeltaY) { - closestDeltaY = deltaY; - closestY = lastY; - } - } - - // Must not have found a close enough match. -#if DEBUG_HACKS - LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d", - i, y, closestY, closestDeltaY, maxDeltaY); -#endif - - currentTouch.pointers[i].y = closestY; - return true; // XXX original code only corrects one point - - SkipSufficientlyClosePoint: ; - } - - // No change. - return false; -} - -/* Special hack for devices that have bad screen data: drop points where - * the coordinate value for one axis has jumped to the other pointer's location. - */ -bool InputDevice::TouchScreenState::applyJumpyTouchFilter() { - // This hack requires valid axis parameters. - if (! parameters.yAxis.valid) { - return false; - } - - uint32_t pointerCount = currentTouch.pointerCount; - if (lastTouch.pointerCount != pointerCount) { -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Different pointer count %d -> %d", - lastTouch.pointerCount, pointerCount); - for (uint32_t i = 0; i < pointerCount; i++) { - LOGD(" Pointer %d (%d, %d)", i, - currentTouch.pointers[i].x, currentTouch.pointers[i].y); - } -#endif - - if (jumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) { - if (lastTouch.pointerCount == 1 && pointerCount == 2) { - // Just drop the first few events going from 1 to 2 pointers. - // They're bad often enough that they're not worth considering. - currentTouch.pointerCount = 1; - jumpyTouchFilter.jumpyPointsDropped += 1; - -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Pointer 2 dropped"); -#endif - return true; - } else if (lastTouch.pointerCount == 2 && pointerCount == 1) { - // The event when we go from 2 -> 1 tends to be messed up too - currentTouch.pointerCount = 2; - currentTouch.pointers[0] = lastTouch.pointers[0]; - currentTouch.pointers[1] = lastTouch.pointers[1]; - jumpyTouchFilter.jumpyPointsDropped += 1; - -#if DEBUG_HACKS - for (int32_t i = 0; i < 2; i++) { - LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i, - currentTouch.pointers[i].x, currentTouch.pointers[i].y); - } -#endif - return true; - } - } - // Reset jumpy points dropped on other transitions or if limit exceeded. - jumpyTouchFilter.jumpyPointsDropped = 0; - -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Transition - drop limit reset"); -#endif - return false; - } - - // We have the same number of pointers as last time. - // A 'jumpy' point is one where the coordinate value for one axis - // has jumped to the other pointer's location. No need to do anything - // else if we only have one pointer. - if (pointerCount < 2) { - return false; - } - - if (jumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) { - int jumpyEpsilon = parameters.yAxis.range / JUMPY_EPSILON_DIVISOR; - - // We only replace the single worst jumpy point as characterized by pointer distance - // in a single axis. - int32_t badPointerIndex = -1; - int32_t badPointerReplacementIndex = -1; - int32_t badPointerDistance = INT_MIN; // distance to be corrected - - for (uint32_t i = pointerCount; i-- > 0; ) { - int32_t x = currentTouch.pointers[i].x; - int32_t y = currentTouch.pointers[i].y; - -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y); -#endif - - // Check if a touch point is too close to another's coordinates - bool dropX = false, dropY = false; - for (uint32_t j = 0; j < pointerCount; j++) { - if (i == j) { - continue; - } - - if (abs(x - currentTouch.pointers[j].x) <= jumpyEpsilon) { - dropX = true; - break; - } - - if (abs(y - currentTouch.pointers[j].y) <= jumpyEpsilon) { - dropY = true; - break; - } - } - if (! dropX && ! dropY) { - continue; // not jumpy - } - - // Find a replacement candidate by comparing with older points on the - // complementary (non-jumpy) axis. - int32_t distance = INT_MIN; // distance to be corrected - int32_t replacementIndex = -1; - - if (dropX) { - // X looks too close. Find an older replacement point with a close Y. - int32_t smallestDeltaY = INT_MAX; - for (uint32_t j = 0; j < pointerCount; j++) { - int32_t deltaY = abs(y - lastTouch.pointers[j].y); - if (deltaY < smallestDeltaY) { - smallestDeltaY = deltaY; - replacementIndex = j; - } - } - distance = abs(x - lastTouch.pointers[replacementIndex].x); - } else { - // Y looks too close. Find an older replacement point with a close X. - int32_t smallestDeltaX = INT_MAX; - for (uint32_t j = 0; j < pointerCount; j++) { - int32_t deltaX = abs(x - lastTouch.pointers[j].x); - if (deltaX < smallestDeltaX) { - smallestDeltaX = deltaX; - replacementIndex = j; - } - } - distance = abs(y - lastTouch.pointers[replacementIndex].y); - } - - // If replacing this pointer would correct a worse error than the previous ones - // considered, then use this replacement instead. - if (distance > badPointerDistance) { - badPointerIndex = i; - badPointerReplacementIndex = replacementIndex; - badPointerDistance = distance; - } - } - - // Correct the jumpy pointer if one was found. - if (badPointerIndex >= 0) { -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)", - badPointerIndex, - lastTouch.pointers[badPointerReplacementIndex].x, - lastTouch.pointers[badPointerReplacementIndex].y); -#endif - - currentTouch.pointers[badPointerIndex].x = - lastTouch.pointers[badPointerReplacementIndex].x; - currentTouch.pointers[badPointerIndex].y = - lastTouch.pointers[badPointerReplacementIndex].y; - jumpyTouchFilter.jumpyPointsDropped += 1; - return true; - } - } - - jumpyTouchFilter.jumpyPointsDropped = 0; - return false; -} - -/* Special hack for devices that have bad screen data: aggregate and - * compute averages of the coordinate data, to reduce the amount of - * jitter seen by applications. */ -void InputDevice::TouchScreenState::applyAveragingTouchFilter() { - for (uint32_t currentIndex = 0; currentIndex < currentTouch.pointerCount; currentIndex++) { - uint32_t id = currentTouch.pointers[currentIndex].id; - int32_t x = currentTouch.pointers[currentIndex].x; - int32_t y = currentTouch.pointers[currentIndex].y; - int32_t pressure = currentTouch.pointers[currentIndex].pressure; - - if (lastTouch.idBits.hasBit(id)) { - // Pointer was down before and is still down now. - // Compute average over history trace. - uint32_t start = averagingTouchFilter.historyStart[id]; - uint32_t end = averagingTouchFilter.historyEnd[id]; - - int64_t deltaX = x - averagingTouchFilter.historyData[end].pointers[id].x; - int64_t deltaY = y - averagingTouchFilter.historyData[end].pointers[id].y; - uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); - -#if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld", - id, distance); -#endif - - if (distance < AVERAGING_DISTANCE_LIMIT) { - // Increment end index in preparation for recording new historical data. - end += 1; - if (end > AVERAGING_HISTORY_SIZE) { - end = 0; - } - - // If the end index has looped back to the start index then we have filled - // the historical trace up to the desired size so we drop the historical - // data at the start of the trace. - if (end == start) { - start += 1; - if (start > AVERAGING_HISTORY_SIZE) { - start = 0; - } - } - - // Add the raw data to the historical trace. - averagingTouchFilter.historyStart[id] = start; - averagingTouchFilter.historyEnd[id] = end; - averagingTouchFilter.historyData[end].pointers[id].x = x; - averagingTouchFilter.historyData[end].pointers[id].y = y; - averagingTouchFilter.historyData[end].pointers[id].pressure = pressure; - - // Average over all historical positions in the trace by total pressure. - int32_t averagedX = 0; - int32_t averagedY = 0; - int32_t totalPressure = 0; - for (;;) { - int32_t historicalX = averagingTouchFilter.historyData[start].pointers[id].x; - int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].y; - int32_t historicalPressure = averagingTouchFilter.historyData[start] - .pointers[id].pressure; - - averagedX += historicalX * historicalPressure; - averagedY += historicalY * historicalPressure; - totalPressure += historicalPressure; - - if (start == end) { - break; - } - - start += 1; - if (start > AVERAGING_HISTORY_SIZE) { - start = 0; - } - } - - averagedX /= totalPressure; - averagedY /= totalPressure; - -#if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - " - "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure, - averagedX, averagedY); -#endif - - currentTouch.pointers[currentIndex].x = averagedX; - currentTouch.pointers[currentIndex].y = averagedY; - } else { -#if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id); -#endif - } - } else { -#if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id); -#endif - } - - // Reset pointer history. - averagingTouchFilter.historyStart[id] = 0; - averagingTouchFilter.historyEnd[id] = 0; - averagingTouchFilter.historyData[0].pointers[id].x = x; - averagingTouchFilter.historyData[0].pointers[id].y = y; - averagingTouchFilter.historyData[0].pointers[id].pressure = pressure; - } -} - -bool InputDevice::TouchScreenState::isPointInsideDisplay(int32_t x, int32_t y) const { - if (! parameters.xAxis.valid || ! parameters.yAxis.valid) { - // Assume all points on a touch screen without valid axis parameters are - // inside the display. - return true; - } - - return x >= parameters.xAxis.minValue - && x <= parameters.xAxis.maxValue - && y >= parameters.yAxis.minValue - && y <= parameters.yAxis.maxValue; -} - -const InputDevice::VirtualKey* InputDevice::TouchScreenState::findVirtualKeyHit() const { - int32_t x = currentTouch.pointers[0].x; - int32_t y = currentTouch.pointers[0].y; - for (size_t i = 0; i < virtualKeys.size(); i++) { - const InputDevice::VirtualKey& virtualKey = virtualKeys[i]; - -#if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " - "left=%d, top=%d, right=%d, bottom=%d", - x, y, - virtualKey.keyCode, virtualKey.scanCode, - virtualKey.hitLeft, virtualKey.hitTop, - virtualKey.hitRight, virtualKey.hitBottom); -#endif - - if (virtualKey.isHit(x, y)) { - return & virtualKey; - } - } - - return NULL; -} - - -// --- InputDevice::SingleTouchScreenState --- - -void InputDevice::SingleTouchScreenState::reset() { - accumulator.clear(); - current.down = false; - current.x = 0; - current.y = 0; - current.pressure = 0; - current.size = 0; -} - - -// --- InputDevice::MultiTouchScreenState --- - -void InputDevice::MultiTouchScreenState::reset() { - accumulator.clear(); -} - -} // namespace android diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp index e1d15a4ba..bf2347902 100644 --- a/libs/ui/InputManager.cpp +++ b/libs/ui/InputManager.cpp @@ -89,26 +89,35 @@ void InputManager::preemptInputDispatch() { mDispatcher->preemptInputDispatch(); } -void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const { - mReader->getCurrentInputConfiguration(outConfiguration); +void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) { + mReader->getInputConfiguration(outConfiguration); } -int32_t InputManager::getScanCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t scanCode) const { - return mReader->getCurrentScanCodeState(deviceId, deviceClasses, scanCode); +status_t InputManager::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) { + return mReader->getInputDeviceInfo(deviceId, outDeviceInfo); } -int32_t InputManager::getKeyCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t keyCode) const { - return mReader->getCurrentKeyCodeState(deviceId, deviceClasses, keyCode); +void InputManager::getInputDeviceIds(Vector& outDeviceIds) { + mReader->getInputDeviceIds(outDeviceIds); } -int32_t InputManager::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const { - return mReader->getCurrentSwitchState(deviceId, deviceClasses, sw); +int32_t InputManager::getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode) { + return mReader->getScanCodeState(deviceId, sourceMask, scanCode); } -bool InputManager::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { - return mReader->hasKeys(numCodes, keyCodes, outFlags); +int32_t InputManager::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode) { + return mReader->getKeyCodeState(deviceId, sourceMask, keyCode); +} + +int32_t InputManager::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) { + return mReader->getSwitchState(deviceId, sourceMask, sw); +} + +bool InputManager::hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { + return mReader->hasKeys(deviceId, sourceMask, numCodes, keyCodes, outFlags); } } // namespace android diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index 30e391ff6..c5183e41b 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -31,10 +31,6 @@ #include #include -/** Amount that trackball needs to move in order to generate a key event. */ -#define TRACKBALL_MOVEMENT_THRESHOLD 6 - - namespace android { // --- Static Functions --- @@ -115,17 +111,21 @@ int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { return keyCode; } +static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { + return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0; +} + // --- InputReader --- InputReader::InputReader(const sp& eventHub, const sp& policy, const sp& dispatcher) : - mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher) { + mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher), + mGlobalMetaState(0) { configureExcludedDevices(); - resetGlobalMetaState(); - resetDisplayProperties(); - updateExportedVirtualKeyState(); + updateGlobalMetaState(); + updateInputConfiguration(); } InputReader::~InputReader() { @@ -136,12 +136,7 @@ InputReader::~InputReader() { void InputReader::loopOnce() { RawEvent rawEvent; - mEventHub->getEvent(& rawEvent.deviceId, & rawEvent.type, & rawEvent.scanCode, - & rawEvent.keyCode, & rawEvent.flags, & rawEvent.value, & rawEvent.when); - - // Replace the event timestamp so it is in same timebase as java.lang.System.nanoTime() - // and android.os.SystemClock.uptimeMillis() as expected by the rest of the system. - rawEvent.when = systemTime(SYSTEM_TIME_MONOTONIC); + mEventHub->getEvent(& rawEvent); #if DEBUG_RAW_EVENTS LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d", @@ -155,621 +150,1371 @@ void InputReader::loopOnce() { void InputReader::process(const RawEvent* rawEvent) { switch (rawEvent->type) { case EventHubInterface::DEVICE_ADDED: - handleDeviceAdded(rawEvent); + addDevice(rawEvent->when, rawEvent->deviceId); break; case EventHubInterface::DEVICE_REMOVED: - handleDeviceRemoved(rawEvent); + removeDevice(rawEvent->when, rawEvent->deviceId); break; - case EV_SYN: - handleSync(rawEvent); - break; - - case EV_KEY: - handleKey(rawEvent); - break; - - case EV_REL: - handleRelativeMotion(rawEvent); - break; - - case EV_ABS: - handleAbsoluteMotion(rawEvent); - break; - - case EV_SW: - handleSwitch(rawEvent); + default: + consumeEvent(rawEvent); break; } } -void InputReader::handleDeviceAdded(const RawEvent* rawEvent) { - InputDevice* device = getDevice(rawEvent->deviceId); - if (device) { - LOGW("Ignoring spurious device added event for deviceId %d.", rawEvent->deviceId); +void InputReader::addDevice(nsecs_t when, int32_t deviceId) { + String8 name = mEventHub->getDeviceName(deviceId); + uint32_t classes = mEventHub->getDeviceClasses(deviceId); + + InputDevice* device = createDevice(deviceId, name, classes); + device->configure(); + + bool added = false; + { // acquire device registry writer lock + RWLock::AutoWLock _wl(mDeviceRegistryLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + mDevices.add(deviceId, device); + added = true; + } + } // release device registry writer lock + + if (! added) { + LOGW("Ignoring spurious device added event for deviceId %d.", deviceId); + delete device; return; } - addDevice(rawEvent->when, rawEvent->deviceId); + if (device->isIgnored()) { + LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", + deviceId, name.string()); + } else { + LOGI("Device added: id=0x%x, name=%s, sources=%08x", + deviceId, name.string(), device->getSources()); + } + + handleConfigurationChanged(when); } -void InputReader::handleDeviceRemoved(const RawEvent* rawEvent) { - InputDevice* device = getDevice(rawEvent->deviceId); - if (! device) { - LOGW("Ignoring spurious device removed event for deviceId %d.", rawEvent->deviceId); +void InputReader::removeDevice(nsecs_t when, int32_t deviceId) { + bool removed = false; + InputDevice* device = NULL; + { // acquire device registry writer lock + RWLock::AutoWLock _wl(mDeviceRegistryLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + device = mDevices.valueAt(deviceIndex); + mDevices.removeItemsAt(deviceIndex, 1); + removed = true; + } + } // release device registry writer lock + + if (! removed) { + LOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); return; } - removeDevice(rawEvent->when, device); + device->reset(); + + if (device->isIgnored()) { + LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)", + device->getId(), device->getName().string()); + } else { + LOGI("Device removed: id=0x%x, name=%s, sources=%08x", + device->getId(), device->getName().string(), device->getSources()); + } + + delete device; + + handleConfigurationChanged(when); } -void InputReader::handleSync(const RawEvent* rawEvent) { - InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId); - if (! device) return; +InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) { + InputDevice* device = new InputDevice(this, deviceId, name); - if (rawEvent->scanCode == SYN_MT_REPORT) { - // MultiTouch Sync: The driver has returned all data for *one* of the pointers. - // We drop pointers with pressure <= 0 since that indicates they are not down. - if (device->isMultiTouchScreen()) { - uint32_t pointerIndex = device->multiTouchScreen.accumulator.pointerCount; + const int32_t associatedDisplayId = 0; // FIXME: hardcoded for current single-display devices - if (device->multiTouchScreen.accumulator.pointers[pointerIndex].fields) { - if (pointerIndex == MAX_POINTERS) { - LOGW("MultiTouch device driver returned more than maximum of %d pointers.", - MAX_POINTERS); - } else { - pointerIndex += 1; - device->multiTouchScreen.accumulator.pointerCount = pointerIndex; + // Switch-like devices. + if (classes & INPUT_DEVICE_CLASS_SWITCH) { + device->addMapper(new SwitchInputMapper(device)); + } + + // Keyboard-like devices. + uint32_t keyboardSources = 0; + int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; + if (classes & INPUT_DEVICE_CLASS_KEYBOARD) { + keyboardSources |= AINPUT_SOURCE_KEYBOARD; + } + if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { + keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; + } + if (classes & INPUT_DEVICE_CLASS_DPAD) { + keyboardSources |= AINPUT_SOURCE_DPAD; + } + if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { + keyboardSources |= AINPUT_SOURCE_GAMEPAD; + } + + if (keyboardSources != 0) { + device->addMapper(new KeyboardInputMapper(device, + associatedDisplayId, keyboardSources, keyboardType)); + } + + // Trackball-like devices. + if (classes & INPUT_DEVICE_CLASS_TRACKBALL) { + device->addMapper(new TrackballInputMapper(device, associatedDisplayId)); + } + + // Touchscreen-like devices. + if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT) { + device->addMapper(new MultiTouchInputMapper(device, associatedDisplayId)); + } else if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN) { + device->addMapper(new SingleTouchInputMapper(device, associatedDisplayId)); + } + + return device; +} + +void InputReader::consumeEvent(const RawEvent* rawEvent) { + int32_t deviceId = rawEvent->deviceId; + + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + LOGW("Discarding event for unknown deviceId %d.", deviceId); + return; + } + + InputDevice* device = mDevices.valueAt(deviceIndex); + if (device->isIgnored()) { + //LOGD("Discarding event for ignored deviceId %d.", deviceId); + return; + } + + device->process(rawEvent); + } // release device registry reader lock +} + +void InputReader::handleConfigurationChanged(nsecs_t when) { + // Reset global meta state because it depends on the list of all configured devices. + updateGlobalMetaState(); + + // Update input configuration. + updateInputConfiguration(); + + // Enqueue configuration changed. + mDispatcher->notifyConfigurationChanged(when); +} + +void InputReader::configureExcludedDevices() { + Vector excludedDeviceNames; + mPolicy->getExcludedDeviceNames(excludedDeviceNames); + + for (size_t i = 0; i < excludedDeviceNames.size(); i++) { + mEventHub->addExcludedDevice(excludedDeviceNames[i]); + } +} + +void InputReader::updateGlobalMetaState() { + { // acquire state lock + AutoMutex _l(mStateLock); + + mGlobalMetaState = 0; + + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + mGlobalMetaState |= device->getMetaState(); + } + } // release device registry reader lock + } // release state lock +} + +int32_t InputReader::getGlobalMetaState() { + { // acquire state lock + AutoMutex _l(mStateLock); + + return mGlobalMetaState; + } // release state lock +} + +void InputReader::updateInputConfiguration() { + { // acquire state lock + AutoMutex _l(mStateLock); + + int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH; + int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS; + int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV; + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + InputDeviceInfo deviceInfo; + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + device->getDeviceInfo(& deviceInfo); + uint32_t sources = deviceInfo.getSources(); + + if ((sources & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) { + touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER; + } + if ((sources & AINPUT_SOURCE_TRACKBALL) == AINPUT_SOURCE_TRACKBALL) { + navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL; + } else if ((sources & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD) { + navigationConfig = InputConfiguration::NAVIGATION_DPAD; + } + if (deviceInfo.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) { + keyboardConfig = InputConfiguration::KEYBOARD_QWERTY; } } + } // release device registry reader lock - device->multiTouchScreen.accumulator.pointers[pointerIndex].clear(); - } - } else if (rawEvent->scanCode == SYN_REPORT) { - // General Sync: The driver has returned all data for the current event update. - if (device->isMultiTouchScreen()) { - if (device->multiTouchScreen.accumulator.isDirty()) { - onMultiTouchScreenStateChanged(rawEvent->when, device); - device->multiTouchScreen.accumulator.clear(); - } - } else if (device->isSingleTouchScreen()) { - if (device->singleTouchScreen.accumulator.isDirty()) { - onSingleTouchScreenStateChanged(rawEvent->when, device); - device->singleTouchScreen.accumulator.clear(); - } + mInputConfiguration.touchScreen = touchScreenConfig; + mInputConfiguration.keyboard = keyboardConfig; + mInputConfiguration.navigation = navigationConfig; + } // release state lock +} + +void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) { + { // acquire state lock + AutoMutex _l(mStateLock); + + *outConfiguration = mInputConfiguration; + } // release state lock +} + +status_t InputReader::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) { + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + return NAME_NOT_FOUND; } - if (device->trackball.accumulator.isDirty()) { - onTrackballStateChanged(rawEvent->when, device); - device->trackball.accumulator.clear(); + InputDevice* device = mDevices.valueAt(deviceIndex); + if (device->isIgnored()) { + return NAME_NOT_FOUND; } + + device->getDeviceInfo(outDeviceInfo); + return OK; + } // release device registy reader lock +} + +void InputReader::getInputDeviceIds(Vector& outDeviceIds) { + outDeviceIds.clear(); + + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (! device->isIgnored()) { + outDeviceIds.add(device->getId()); + } + } + } // release device registy reader lock +} + +int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode) { + return getState(deviceId, sourceMask, keyCode, & InputDevice::getKeyCodeState); +} + +int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode) { + return getState(deviceId, sourceMask, scanCode, & InputDevice::getScanCodeState); +} + +int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) { + return getState(deviceId, sourceMask, switchCode, & InputDevice::getSwitchState); +} + +int32_t InputReader::getState(int32_t deviceId, uint32_t sourceMask, int32_t code, + GetStateFunc getStateFunc) { + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + int32_t result = AKEY_STATE_UNKNOWN; + if (deviceId >= 0) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = (device->*getStateFunc)(sourceMask, code); + } + } + } else { + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = (device->*getStateFunc)(sourceMask, code); + if (result >= AKEY_STATE_DOWN) { + return result; + } + } + } + } + return result; + } // release device registy reader lock +} + +bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { + memset(outFlags, 0, numCodes); + return markSupportedKeyCodes(deviceId, sourceMask, numCodes, keyCodes, outFlags); +} + +bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + bool result = false; + if (deviceId >= 0) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = device->markSupportedKeyCodes(sourceMask, + numCodes, keyCodes, outFlags); + } + } + } else { + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result |= device->markSupportedKeyCodes(sourceMask, + numCodes, keyCodes, outFlags); + } + } + } + return result; + } // release device registy reader lock +} + + +// --- InputReaderThread --- + +InputReaderThread::InputReaderThread(const sp& reader) : + Thread(/*canCallJava*/ true), mReader(reader) { +} + +InputReaderThread::~InputReaderThread() { +} + +bool InputReaderThread::threadLoop() { + mReader->loopOnce(); + return true; +} + + +// --- InputDevice --- + +InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) : + mContext(context), mId(id), mName(name), mSources(0) { +} + +InputDevice::~InputDevice() { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + delete mMappers[i]; + } + mMappers.clear(); +} + +void InputDevice::addMapper(InputMapper* mapper) { + mMappers.add(mapper); +} + +void InputDevice::configure() { + mSources = 0; + + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->configure(); + mSources |= mapper->getSources(); } } -void InputReader::handleKey(const RawEvent* rawEvent) { - InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId); - if (! device) return; +void InputDevice::reset() { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->reset(); + } +} - bool down = rawEvent->value != 0; - int32_t scanCode = rawEvent->scanCode; +void InputDevice::process(const RawEvent* rawEvent) { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->process(rawEvent); + } +} - if (device->isSingleTouchScreen()) { - switch (rawEvent->scanCode) { - case BTN_TOUCH: - device->singleTouchScreen.accumulator.fields |= - InputDevice::SingleTouchScreenState::Accumulator::FIELD_BTN_TOUCH; - device->singleTouchScreen.accumulator.btnTouch = down; +void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { + outDeviceInfo->initialize(mId, mName); + + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->populateDeviceInfo(outDeviceInfo); + } +} + +int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState); +} + +int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getState(sourceMask, scanCode, & InputMapper::getScanCodeState); +} + +int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getState(sourceMask, switchCode, & InputMapper::getSwitchState); +} + +int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { + int32_t result = AKEY_STATE_UNKNOWN; + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + result = (mapper->*getStateFunc)(sourceMask, code); + if (result >= AKEY_STATE_DOWN) { + return result; + } + } + } + return result; +} + +bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + bool result = false; + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + } + } + return result; +} + +int32_t InputDevice::getMetaState() { + int32_t result = 0; + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + result |= mapper->getMetaState(); + } + return result; +} + + +// --- InputMapper --- + +InputMapper::InputMapper(InputDevice* device) : + mDevice(device), mContext(device->getContext()) { +} + +InputMapper::~InputMapper() { +} + +void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { + info->addSource(getSources()); +} + +void InputMapper::configure() { +} + +void InputMapper::reset() { +} + +int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return AKEY_STATE_UNKNOWN; +} + +bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return false; +} + +int32_t InputMapper::getMetaState() { + return 0; +} + +bool InputMapper::applyStandardPolicyActions(nsecs_t when, int32_t policyActions) { + if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) { + getDispatcher()->notifyAppSwitchComing(when); + } + + return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH; +} + + +// --- SwitchInputMapper --- + +SwitchInputMapper::SwitchInputMapper(InputDevice* device) : + InputMapper(device) { +} + +SwitchInputMapper::~SwitchInputMapper() { +} + +uint32_t SwitchInputMapper::getSources() { + return 0; +} + +void SwitchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_SW: + processSwitch(rawEvent->when, rawEvent->scanCode, rawEvent->value); + break; + } +} + +void SwitchInputMapper::processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) { + uint32_t policyFlags = 0; + int32_t policyActions = getPolicy()->interceptSwitch( + when, switchCode, switchValue, policyFlags); + + applyStandardPolicyActions(when, policyActions); +} + +int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getEventHub()->getSwitchState(getDeviceId(), switchCode); +} + + +// --- KeyboardInputMapper --- + +KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, + uint32_t sources, int32_t keyboardType) : + InputMapper(device), mAssociatedDisplayId(associatedDisplayId), mSources(sources), + mKeyboardType(keyboardType) { + initialize(); +} + +KeyboardInputMapper::~KeyboardInputMapper() { +} + +void KeyboardInputMapper::initialize() { + mMetaState = AMETA_NONE; + mDownTime = 0; +} + +uint32_t KeyboardInputMapper::getSources() { + return mSources; +} + +void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->setKeyboardType(mKeyboardType); +} + +void KeyboardInputMapper::reset() { + // Synthesize key up event on reset if keys are currently down. + while (! mKeyDowns.isEmpty()) { + const KeyDown& keyDown = mKeyDowns.top(); + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + processKey(when, false, keyDown.keyCode, keyDown.scanCode, 0); + } + + InputMapper::reset(); + + // Reinitialize. + initialize(); + getContext()->updateGlobalMetaState(); +} + +void KeyboardInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: { + int32_t scanCode = rawEvent->scanCode; + if (isKeyboardOrGamepadKey(scanCode)) { + processKey(rawEvent->when, rawEvent->value != 0, rawEvent->keyCode, scanCode, + rawEvent->flags); + } + break; + } + } +} + +bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { + return scanCode < BTN_MOUSE + || scanCode >= KEY_OK + || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI); +} + +void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode, + uint32_t policyFlags) { + if (down) { + // Rotate key codes according to orientation. + if (mAssociatedDisplayId >= 0) { + int32_t orientation; + if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { + return; + } + + keyCode = rotateKeyCode(keyCode, orientation); + } + + // Add key down. + ssize_t keyDownIndex = findKeyDown(scanCode); + if (keyDownIndex >= 0) { + // key repeat, be sure to use same keycode as before in case of rotation + keyCode = mKeyDowns.top().keyCode; + } else { + // key down + mKeyDowns.push(); + KeyDown& keyDown = mKeyDowns.editTop(); + keyDown.keyCode = keyCode; + keyDown.scanCode = scanCode; + } + } else { + // Remove key down. + ssize_t keyDownIndex = findKeyDown(scanCode); + if (keyDownIndex >= 0) { + // key up, be sure to use same keycode as before in case of rotation + keyCode = mKeyDowns.top().keyCode; + mKeyDowns.removeAt(size_t(keyDownIndex)); + } else { + // key was not actually down + LOGI("Dropping key up from device %s because the key was not down. " + "keyCode=%d, scanCode=%d", + getDeviceName().string(), keyCode, scanCode); return; } } - if (device->isTrackball()) { - switch (rawEvent->scanCode) { - case BTN_MOUSE: - device->trackball.accumulator.fields |= - InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE; - device->trackball.accumulator.btnMouse = down; - - // Process the trackball change now since we may not receive a sync immediately. - onTrackballStateChanged(rawEvent->when, device); - device->trackball.accumulator.clear(); - return; - } - } - - if (device->isKeyboard()) { - int32_t keyCode = rawEvent->keyCode; - onKey(rawEvent->when, device, down, keyCode, scanCode, rawEvent->flags); - } -} - -void InputReader::handleRelativeMotion(const RawEvent* rawEvent) { - InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId); - if (! device) return; - - if (device->isTrackball()) { - switch (rawEvent->scanCode) { - case REL_X: - device->trackball.accumulator.fields |= - InputDevice::TrackballState::Accumulator::FIELD_REL_X; - device->trackball.accumulator.relX = rawEvent->value; - break; - case REL_Y: - device->trackball.accumulator.fields |= - InputDevice::TrackballState::Accumulator::FIELD_REL_Y; - device->trackball.accumulator.relY = rawEvent->value; - break; - } - } -} - -void InputReader::handleAbsoluteMotion(const RawEvent* rawEvent) { - InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId); - if (! device) return; - - if (device->isMultiTouchScreen()) { - uint32_t pointerIndex = device->multiTouchScreen.accumulator.pointerCount; - InputDevice::MultiTouchScreenState::Accumulator::Pointer* pointer = - & device->multiTouchScreen.accumulator.pointers[pointerIndex]; - - switch (rawEvent->scanCode) { - case ABS_MT_POSITION_X: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_X; - pointer->absMTPositionX = rawEvent->value; - break; - case ABS_MT_POSITION_Y: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_Y; - pointer->absMTPositionY = rawEvent->value; - break; - case ABS_MT_TOUCH_MAJOR: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MAJOR; - pointer->absMTTouchMajor = rawEvent->value; - break; - case ABS_MT_TOUCH_MINOR: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MINOR; - pointer->absMTTouchMinor = rawEvent->value; - break; - case ABS_MT_WIDTH_MAJOR: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; - pointer->absMTWidthMajor = rawEvent->value; - break; - case ABS_MT_WIDTH_MINOR: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MINOR; - pointer->absMTWidthMinor = rawEvent->value; - break; - case ABS_MT_ORIENTATION: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_ORIENTATION; - pointer->absMTOrientation = rawEvent->value; - break; - case ABS_MT_TRACKING_ID: - pointer->fields |= - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TRACKING_ID; - pointer->absMTTrackingId = rawEvent->value; - break; - } - } else if (device->isSingleTouchScreen()) { - switch (rawEvent->scanCode) { - case ABS_X: - device->singleTouchScreen.accumulator.fields |= - InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X; - device->singleTouchScreen.accumulator.absX = rawEvent->value; - break; - case ABS_Y: - device->singleTouchScreen.accumulator.fields |= - InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y; - device->singleTouchScreen.accumulator.absY = rawEvent->value; - break; - case ABS_PRESSURE: - device->singleTouchScreen.accumulator.fields |= - InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE; - device->singleTouchScreen.accumulator.absPressure = rawEvent->value; - break; - case ABS_TOOL_WIDTH: - device->singleTouchScreen.accumulator.fields |= - InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH; - device->singleTouchScreen.accumulator.absToolWidth = rawEvent->value; - break; - } - } -} - -void InputReader::handleSwitch(const RawEvent* rawEvent) { - InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId); - if (! device) return; - - onSwitch(rawEvent->when, device, rawEvent->scanCode, rawEvent->value); -} - -void InputReader::onKey(nsecs_t when, InputDevice* device, - bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) { - /* Refresh display properties so we can rotate key codes according to display orientation */ - - if (! refreshDisplayProperties()) { - return; - } - - /* Update device state */ - - int32_t oldMetaState = device->keyboard.current.metaState; + int32_t oldMetaState = mMetaState; int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState); if (oldMetaState != newMetaState) { - device->keyboard.current.metaState = newMetaState; - resetGlobalMetaState(); + mMetaState = newMetaState; + getContext()->updateGlobalMetaState(); } - // FIXME if we send a down event about a rotated key press we should ensure that we send - // a corresponding up event about the rotated key press even if the orientation - // has changed in the meantime - keyCode = rotateKeyCode(keyCode, mDisplayOrientation); + /* Apply policy. */ - if (down) { - device->keyboard.current.downTime = when; - } + int32_t policyActions = getPolicy()->interceptKey(when, + getDeviceId(), down, keyCode, scanCode, policyFlags); - /* Apply policy */ - - int32_t policyActions = mPolicy->interceptKey(when, device->id, - down, keyCode, scanCode, policyFlags); - - if (! applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) { + if (! applyStandardPolicyActions(when, policyActions)) { return; // event dropped } - /* Enqueue key event for dispatch */ + /* Enqueue key event for dispatch. */ int32_t keyEventAction; if (down) { - device->keyboard.current.downTime = when; + mDownTime = when; keyEventAction = AKEY_EVENT_ACTION_DOWN; } else { keyEventAction = AKEY_EVENT_ACTION_UP; } int32_t keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM; - if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) { + if (policyFlags & POLICY_FLAG_WOKE_HERE) { keyEventFlags = keyEventFlags | AKEY_EVENT_FLAG_WOKE_HERE; } - mDispatcher->notifyKey(when, device->id, AINPUT_SOURCE_KEYBOARD, policyFlags, + getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, - device->keyboard.current.metaState, - device->keyboard.current.downTime); + mMetaState, mDownTime); } -void InputReader::onSwitch(nsecs_t when, InputDevice* device, int32_t switchCode, - int32_t switchValue) { - int32_t policyActions = mPolicy->interceptSwitch(when, switchCode, switchValue); - - uint32_t policyFlags = 0; - applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags); +ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { + size_t n = mKeyDowns.size(); + for (size_t i = 0; i < n; i++) { + if (mKeyDowns[i].scanCode == scanCode) { + return i; + } + } + return -1; } -void InputReader::onMultiTouchScreenStateChanged(nsecs_t when, - InputDevice* device) { - static const uint32_t REQUIRED_FIELDS = - InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_X - | InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_Y - | InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MAJOR - | InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; +int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); +} - /* Refresh display properties so we can map touch screen coords into display coords */ +int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); +} - if (! refreshDisplayProperties()) { - return; +bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); +} + +int32_t KeyboardInputMapper::getMetaState() { + return mMetaState; +} + + +// --- TrackballInputMapper --- + +TrackballInputMapper::TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId) : + InputMapper(device), mAssociatedDisplayId(associatedDisplayId) { + mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + + initialize(); +} + +TrackballInputMapper::~TrackballInputMapper() { +} + +uint32_t TrackballInputMapper::getSources() { + return AINPUT_SOURCE_TRACKBALL; +} + +void TrackballInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->addMotionRange(AINPUT_MOTION_RANGE_X, -1.0f, 1.0f, 0.0f, mXScale); + info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale); +} + +void TrackballInputMapper::initialize() { + mAccumulator.clear(); + + mDown = false; + mDownTime = 0; +} + +void TrackballInputMapper::reset() { + // Synthesize trackball button up event on reset if trackball button is currently down. + if (mDown) { + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE; + mAccumulator.btnMouse = false; + sync(when); } - /* Update device state */ + InputMapper::reset(); - InputDevice::MultiTouchScreenState* in = & device->multiTouchScreen; - InputDevice::TouchData* out = & device->touchScreen.currentTouch; + // Reinitialize. + initialize(); +} - uint32_t inCount = in->accumulator.pointerCount; - uint32_t outCount = 0; - bool havePointerIds = true; +void TrackballInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: + switch (rawEvent->scanCode) { + case BTN_MOUSE: + mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE; + mAccumulator.btnMouse = rawEvent->value != 0; - out->clear(); - - for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) { - uint32_t fields = in->accumulator.pointers[inIndex].fields; - - if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) { -#if DEBUG_POINTERS - LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d", - inIndex, fields); - continue; -#endif + sync(rawEvent->when); + mAccumulator.clear(); + break; } + break; - if (in->accumulator.pointers[inIndex].absMTTouchMajor <= 0) { - // Pointer is not down. Drop it. - continue; + case EV_REL: + switch (rawEvent->scanCode) { + case REL_X: + mAccumulator.fields |= Accumulator::FIELD_REL_X; + mAccumulator.relX = rawEvent->value; + break; + case REL_Y: + mAccumulator.fields |= Accumulator::FIELD_REL_Y; + mAccumulator.relY = rawEvent->value; + break; } + break; - out->pointers[outCount].x = in->accumulator.pointers[inIndex].absMTPositionX; - out->pointers[outCount].y = in->accumulator.pointers[inIndex].absMTPositionY; - - out->pointers[outCount].touchMajor = in->accumulator.pointers[inIndex].absMTTouchMajor; - out->pointers[outCount].touchMinor = (fields - & InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0 - ? in->accumulator.pointers[inIndex].absMTTouchMinor - : in->accumulator.pointers[inIndex].absMTTouchMajor; - - out->pointers[outCount].toolMajor = in->accumulator.pointers[inIndex].absMTWidthMajor; - out->pointers[outCount].toolMinor = (fields - & InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0 - ? in->accumulator.pointers[inIndex].absMTWidthMinor - : in->accumulator.pointers[inIndex].absMTWidthMajor; - - out->pointers[outCount].orientation = (fields - & InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_ORIENTATION) != 0 - ? in->accumulator.pointers[inIndex].absMTOrientation : 0; - - // Derive an approximation of pressure and size. - // FIXME assignment of pressure may be incorrect, probably better to let - // pressure = touch / width. Later on we pass width to MotionEvent as a size, which - // isn't quite right either. Should be using touch for that. - out->pointers[outCount].pressure = in->accumulator.pointers[inIndex].absMTTouchMajor; - out->pointers[outCount].size = in->accumulator.pointers[inIndex].absMTWidthMajor; - - if (havePointerIds) { - if (fields & InputDevice::MultiTouchScreenState::Accumulator:: - FIELD_ABS_MT_TRACKING_ID) { - uint32_t id = uint32_t(in->accumulator.pointers[inIndex].absMTTrackingId); - - if (id > MAX_POINTER_ID) { -#if DEBUG_POINTERS - LOGD("Pointers: Ignoring driver provided pointer id %d because " - "it is larger than max supported id %d for optimizations", - id, MAX_POINTER_ID); -#endif - havePointerIds = false; - } - else { - out->pointers[outCount].id = id; - out->idToIndex[id] = outCount; - out->idBits.markBit(id); - } - } else { - havePointerIds = false; + case EV_SYN: + switch (rawEvent->scanCode) { + case SYN_REPORT: + if (mAccumulator.isDirty()) { + sync(rawEvent->when); + mAccumulator.clear(); } + break; } - - outCount += 1; + break; } - - out->pointerCount = outCount; - - onTouchScreenChanged(when, device, havePointerIds); } -void InputReader::onSingleTouchScreenStateChanged(nsecs_t when, - InputDevice* device) { - /* Refresh display properties so we can map touch screen coords into display coords */ +void TrackballInputMapper::sync(nsecs_t when) { + /* Get display properties so for rotation based on display orientation. */ - if (! refreshDisplayProperties()) { - return; + int32_t orientation; + if (mAssociatedDisplayId >= 0) { + if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { + return; + } + } else { + orientation = InputReaderPolicyInterface::ROTATION_0; } - /* Update device state */ + /* Update saved trackball state */ - InputDevice::SingleTouchScreenState* in = & device->singleTouchScreen; - InputDevice::TouchData* out = & device->touchScreen.currentTouch; + uint32_t fields = mAccumulator.fields; + bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE; - uint32_t fields = in->accumulator.fields; - - if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_BTN_TOUCH) { - in->current.down = in->accumulator.btnTouch; + if (downChanged) { + if (mAccumulator.btnMouse) { + mDown = true; + mDownTime = when; + } else { + mDown = false; + } } - if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X) { - in->current.x = in->accumulator.absX; - } - - if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y) { - in->current.y = in->accumulator.absY; - } - - if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE) { - in->current.pressure = in->accumulator.absPressure; - } - - if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH) { - in->current.size = in->accumulator.absToolWidth; - } - - out->clear(); - - if (in->current.down) { - out->pointerCount = 1; - out->pointers[0].id = 0; - out->pointers[0].x = in->current.x; - out->pointers[0].y = in->current.y; - out->pointers[0].pressure = in->current.pressure; - out->pointers[0].size = in->current.size; - out->pointers[0].touchMajor = in->current.pressure; - out->pointers[0].touchMinor = in->current.pressure; - out->pointers[0].toolMajor = in->current.size; - out->pointers[0].toolMinor = in->current.size; - out->pointers[0].orientation = 0; - out->idToIndex[0] = 0; - out->idBits.markBit(0); - } - - onTouchScreenChanged(when, device, true); -} - -void InputReader::onTouchScreenChanged(nsecs_t when, - InputDevice* device, bool havePointerIds) { /* Apply policy */ - int32_t policyActions = mPolicy->interceptTouch(when); + uint32_t policyFlags = 0; + int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags); + + if (! applyStandardPolicyActions(when, policyActions)) { + return; // event dropped + } + + /* Enqueue motion event for dispatch. */ + + int32_t motionEventAction; + if (downChanged) { + motionEventAction = mDown ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; + } else { + motionEventAction = AMOTION_EVENT_ACTION_MOVE; + } + + int32_t pointerId = 0; + PointerCoords pointerCoords; + pointerCoords.x = fields & Accumulator::FIELD_REL_X + ? mAccumulator.relX * mXScale : 0; + pointerCoords.y = fields & Accumulator::FIELD_REL_Y + ? mAccumulator.relY * mYScale : 0; + pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise. + pointerCoords.size = 0; + pointerCoords.touchMajor = 0; + pointerCoords.touchMinor = 0; + pointerCoords.toolMajor = 0; + pointerCoords.toolMinor = 0; + pointerCoords.orientation = 0; + + float temp; + switch (orientation) { + case InputReaderPolicyInterface::ROTATION_90: + temp = pointerCoords.x; + pointerCoords.x = pointerCoords.y; + pointerCoords.y = - temp; + break; + + case InputReaderPolicyInterface::ROTATION_180: + pointerCoords.x = - pointerCoords.x; + pointerCoords.y = - pointerCoords.y; + break; + + case InputReaderPolicyInterface::ROTATION_270: + temp = pointerCoords.x; + pointerCoords.x = - pointerCoords.y; + pointerCoords.y = temp; + break; + } + + int32_t metaState = mContext->getGlobalMetaState(); + getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, policyFlags, + motionEventAction, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + 1, & pointerId, & pointerCoords, mXPrecision, mYPrecision, mDownTime); +} + + +// --- TouchInputMapper --- + +TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : + InputMapper(device), mAssociatedDisplayId(associatedDisplayId), + mSurfaceOrientation(-1), mSurfaceWidth(-1), mSurfaceHeight(-1) { + initialize(); +} + +TouchInputMapper::~TouchInputMapper() { +} + +uint32_t TouchInputMapper::getSources() { + return mAssociatedDisplayId >= 0 ? AINPUT_SOURCE_TOUCHSCREEN : AINPUT_SOURCE_TOUCHPAD; +} + +void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + // FIXME: Should ensure the surface information is up to date so that orientation changes + // are noticed immediately. Unfortunately we will need to add some extra locks here + // to prevent race conditions. + // configureSurface(); + + info->addMotionRange(AINPUT_MOTION_RANGE_X, mOrientedRanges.x); + info->addMotionRange(AINPUT_MOTION_RANGE_Y, mOrientedRanges.y); + info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, mOrientedRanges.pressure); + info->addMotionRange(AINPUT_MOTION_RANGE_SIZE, mOrientedRanges.size); + info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, mOrientedRanges.touchMajor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, mOrientedRanges.touchMinor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR, mOrientedRanges.toolMajor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR, mOrientedRanges.toolMinor); + info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION, mOrientedRanges.orientation); +} + +void TouchInputMapper::initialize() { + mLastTouch.clear(); + mDownTime = 0; + mCurrentVirtualKey.down = false; + + for (uint32_t i = 0; i < MAX_POINTERS; i++) { + mAveragingTouchFilter.historyStart[i] = 0; + mAveragingTouchFilter.historyEnd[i] = 0; + } + + mJumpyTouchFilter.jumpyPointsDropped = 0; +} + +void TouchInputMapper::configure() { + InputMapper::configure(); + + // Configure basic parameters. + mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents(); + mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents(); + mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents(); + + // Configure absolute axis information. + configureAxes(); + + // Configure pressure factors. + if (mAxes.pressure.valid) { + mPressureOrigin = mAxes.pressure.minValue; + mPressureScale = 1.0f / mAxes.pressure.getRange(); + } else { + mPressureOrigin = 0; + mPressureScale = 1.0f; + } + + mOrientedRanges.pressure.min = 0.0f; + mOrientedRanges.pressure.max = 1.0f; + mOrientedRanges.pressure.flat = 0.0f; + mOrientedRanges.pressure.fuzz = mPressureScale; + + // Configure size factors. + if (mAxes.size.valid) { + mSizeOrigin = mAxes.size.minValue; + mSizeScale = 1.0f / mAxes.size.getRange(); + } else { + mSizeOrigin = 0; + mSizeScale = 1.0f; + } + + mOrientedRanges.size.min = 0.0f; + mOrientedRanges.size.max = 1.0f; + mOrientedRanges.size.flat = 0.0f; + mOrientedRanges.size.fuzz = mSizeScale; + + // Configure orientation factors. + if (mAxes.orientation.valid && mAxes.orientation.maxValue > 0) { + mOrientationScale = float(M_PI_2) / mAxes.orientation.maxValue; + } else { + mOrientationScale = 0.0f; + } + + mOrientedRanges.orientation.min = - M_PI_2; + mOrientedRanges.orientation.max = M_PI_2; + mOrientedRanges.orientation.flat = 0; + mOrientedRanges.orientation.fuzz = mOrientationScale; + + // Configure surface dimensions and orientation. + configureSurface(); +} + +void TouchInputMapper::configureAxes() { + mAxes.x.valid = false; + mAxes.y.valid = false; + mAxes.pressure.valid = false; + mAxes.size.valid = false; + mAxes.touchMajor.valid = false; + mAxes.touchMinor.valid = false; + mAxes.toolMajor.valid = false; + mAxes.toolMinor.valid = false; + mAxes.orientation.valid = false; +} + +bool TouchInputMapper::configureSurface() { + // Update orientation and dimensions if needed. + int32_t orientation; + int32_t width, height; + if (mAssociatedDisplayId >= 0) { + if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) { + return false; + } + } else { + orientation = InputReaderPolicyInterface::ROTATION_0; + width = mAxes.x.getRange(); + height = mAxes.y.getRange(); + } + + bool orientationChanged = mSurfaceOrientation != orientation; + if (orientationChanged) { + mSurfaceOrientation = orientation; + } + + bool sizeChanged = mSurfaceWidth != width || mSurfaceHeight != height; + if (sizeChanged) { + mSurfaceWidth = width; + mSurfaceHeight = height; + + // Compute size-dependent translation and scaling factors and place virtual keys. + if (mAxes.x.valid && mAxes.y.valid) { + mXOrigin = mAxes.x.minValue; + mYOrigin = mAxes.y.minValue; + + LOGI("Device configured: id=0x%x, name=%s (display size was changed)", + getDeviceId(), getDeviceName().string()); + + mXScale = float(width) / mAxes.x.getRange(); + mYScale = float(height) / mAxes.y.getRange(); + mXPrecision = 1.0f / mXScale; + mYPrecision = 1.0f / mYScale; + + configureVirtualKeys(); + } else { + mXOrigin = 0; + mYOrigin = 0; + mXScale = 1.0f; + mYScale = 1.0f; + mXPrecision = 1.0f; + mYPrecision = 1.0f; + } + + // Configure touch and tool area ranges. + float diagonal = sqrt(float(width * width + height * height)); + float diagonalFuzz = sqrt(mXScale * mXScale + mYScale * mYScale); + + mOrientedRanges.touchMajor.min = 0.0f; + mOrientedRanges.touchMajor.max = diagonal; + mOrientedRanges.touchMajor.flat = 0.0f; + mOrientedRanges.touchMajor.fuzz = diagonalFuzz; + mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; + + mOrientedRanges.toolMinor = mOrientedRanges.toolMajor = mOrientedRanges.touchMajor; + } + + if (orientationChanged || sizeChanged) { + // Compute oriented surface dimensions, precision, and scales. + float orientedXScale, orientedYScale; + switch (mSurfaceOrientation) { + case InputReaderPolicyInterface::ROTATION_90: + case InputReaderPolicyInterface::ROTATION_270: + mOrientedSurfaceWidth = mSurfaceHeight; + mOrientedSurfaceHeight = mSurfaceWidth; + mOrientedXPrecision = mYPrecision; + mOrientedYPrecision = mXPrecision; + orientedXScale = mYScale; + orientedYScale = mXScale; + break; + default: + mOrientedSurfaceWidth = mSurfaceWidth; + mOrientedSurfaceHeight = mSurfaceHeight; + mOrientedXPrecision = mXPrecision; + mOrientedYPrecision = mYPrecision; + orientedXScale = mXScale; + orientedYScale = mYScale; + break; + } + + // Configure position ranges. + mOrientedRanges.x.min = 0; + mOrientedRanges.x.max = mOrientedSurfaceWidth; + mOrientedRanges.x.flat = 0; + mOrientedRanges.x.fuzz = orientedXScale; + + mOrientedRanges.y.min = 0; + mOrientedRanges.y.max = mOrientedSurfaceHeight; + mOrientedRanges.y.flat = 0; + mOrientedRanges.y.fuzz = orientedYScale; + } + + return true; +} + +void TouchInputMapper::configureVirtualKeys() { + assert(mAxes.x.valid && mAxes.y.valid); + + Vector virtualKeyDefinitions; + getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions); + + { // acquire virtual key lock + AutoMutex _l(mVirtualKeyLock); + + mVirtualKeys.clear(); + + if (virtualKeyDefinitions.size() == 0) { + return; + } + + mVirtualKeys.setCapacity(virtualKeyDefinitions.size()); + + int32_t touchScreenLeft = mAxes.x.minValue; + int32_t touchScreenTop = mAxes.y.minValue; + int32_t touchScreenWidth = mAxes.x.getRange(); + int32_t touchScreenHeight = mAxes.y.getRange(); + + for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) { + const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition = + virtualKeyDefinitions[i]; + + mVirtualKeys.add(); + VirtualKey& virtualKey = mVirtualKeys.editTop(); + + virtualKey.scanCode = virtualKeyDefinition.scanCode; + int32_t keyCode; + uint32_t flags; + if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode, + & keyCode, & flags)) { + LOGW(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); + mVirtualKeys.pop(); // drop the key + continue; + } + + virtualKey.keyCode = keyCode; + virtualKey.flags = flags; + + // convert the key definition's display coordinates into touch coordinates for a hit box + int32_t halfWidth = virtualKeyDefinition.width / 2; + int32_t halfHeight = virtualKeyDefinition.height / 2; + + virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) + * touchScreenWidth / mSurfaceWidth + touchScreenLeft; + virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth) + * touchScreenWidth / mSurfaceWidth + touchScreenLeft; + virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) + * touchScreenHeight / mSurfaceHeight + touchScreenTop; + virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) + * touchScreenHeight / mSurfaceHeight + touchScreenTop; + + LOGI(" VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d", + virtualKey.scanCode, virtualKey.keyCode, + virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); + } + } // release virtual key lock +} + +void TouchInputMapper::reset() { + // Synthesize touch up event if touch is currently down. + // This will also take care of finishing virtual key processing if needed. + if (mLastTouch.pointerCount != 0) { + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + mCurrentTouch.clear(); + syncTouch(when, true); + } + + InputMapper::reset(); + + // Reinitialize. + initialize(); +} + +void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { + /* Refresh associated display information and update our size configuration if needed. */ + + if (! configureSurface()) { + return; + } + + /* Apply policy */ uint32_t policyFlags = 0; - if (! applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) { - device->touchScreen.lastTouch.clear(); + int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags); + + if (! applyStandardPolicyActions(when, policyActions)) { + mLastTouch.clear(); return; // event dropped } /* Preprocess pointer data */ - if (device->touchScreen.parameters.useBadTouchFilter) { - if (device->touchScreen.applyBadTouchFilter()) { + if (mParameters.useBadTouchFilter) { + if (applyBadTouchFilter()) { havePointerIds = false; } } - if (device->touchScreen.parameters.useJumpyTouchFilter) { - if (device->touchScreen.applyJumpyTouchFilter()) { + if (mParameters.useJumpyTouchFilter) { + if (applyJumpyTouchFilter()) { havePointerIds = false; } } if (! havePointerIds) { - device->touchScreen.calculatePointerIds(); + calculatePointerIds(); } - InputDevice::TouchData temp; - InputDevice::TouchData* savedTouch; - if (device->touchScreen.parameters.useAveragingTouchFilter) { - temp.copyFrom(device->touchScreen.currentTouch); + TouchData temp; + TouchData* savedTouch; + if (mParameters.useAveragingTouchFilter) { + temp.copyFrom(mCurrentTouch); savedTouch = & temp; - device->touchScreen.applyAveragingTouchFilter(); + applyAveragingTouchFilter(); } else { - savedTouch = & device->touchScreen.currentTouch; + savedTouch = & mCurrentTouch; } - /* Process virtual keys or touches */ + /* Process touches and virtual keys */ - if (! consumeVirtualKeyTouches(when, device, policyFlags)) { - dispatchTouches(when, device, policyFlags); + TouchResult touchResult = consumeOffScreenTouches(when, policyFlags); + if (touchResult == DISPATCH_TOUCH) { + dispatchTouches(when, policyFlags); } - // Copy current touch to last touch in preparation for the next cycle. - device->touchScreen.lastTouch.copyFrom(*savedTouch); -} + /* Copy current touch to last touch in preparation for the next cycle. */ -bool InputReader::consumeVirtualKeyTouches(nsecs_t when, - InputDevice* device, uint32_t policyFlags) { - switch (device->touchScreen.currentVirtualKey.status) { - case InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_CANCELED: - if (device->touchScreen.currentTouch.pointerCount == 0) { - // Pointer went up after virtual key canceled. - device->touchScreen.currentVirtualKey.status = - InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_UP; - } - return true; // consumed - - case InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN: - if (device->touchScreen.currentTouch.pointerCount == 0) { - // Pointer went up while virtual key was down. - device->touchScreen.currentVirtualKey.status = - InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_UP; -#if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", - device->touchScreen.currentVirtualKey.keyCode, - device->touchScreen.currentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, device, policyFlags, AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); - return true; // consumed - } - - if (device->touchScreen.currentTouch.pointerCount == 1) { - const InputDevice::VirtualKey* virtualKey = device->touchScreen.findVirtualKeyHit(); - if (virtualKey - && virtualKey->keyCode == device->touchScreen.currentVirtualKey.keyCode) { - // Pointer is still within the space of the virtual key. - return true; // consumed - } - } - - // Pointer left virtual key area or another pointer also went down. - // Send key cancellation. - device->touchScreen.currentVirtualKey.status = - InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_CANCELED; -#if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", - device->touchScreen.currentVirtualKey.keyCode, - device->touchScreen.currentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, device, policyFlags, AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY - | AKEY_EVENT_FLAG_CANCELED); - return true; // consumed - - default: - if (device->touchScreen.currentTouch.pointerCount == 1 - && device->touchScreen.lastTouch.pointerCount == 0) { - // Pointer just went down. Check for virtual key hit. - const InputDevice::VirtualKey* virtualKey = device->touchScreen.findVirtualKeyHit(); - if (virtualKey) { - device->touchScreen.currentVirtualKey.status = - InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN; - device->touchScreen.currentVirtualKey.downTime = when; - device->touchScreen.currentVirtualKey.keyCode = virtualKey->keyCode; - device->touchScreen.currentVirtualKey.scanCode = virtualKey->scanCode; -#if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", - device->touchScreen.currentVirtualKey.keyCode, - device->touchScreen.currentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, device, policyFlags, AKEY_EVENT_ACTION_DOWN, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); - return true; // consumed - } - } - return false; // not consumed + if (touchResult == DROP_STROKE) { + mLastTouch.clear(); + } else { + mLastTouch.copyFrom(*savedTouch); } } -void InputReader::dispatchVirtualKey(nsecs_t when, - InputDevice* device, uint32_t policyFlags, - int32_t keyEventAction, int32_t keyEventFlags) { - updateExportedVirtualKeyState(); +TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches( + nsecs_t when, uint32_t policyFlags) { + int32_t keyEventAction, keyEventFlags; + int32_t keyCode, scanCode, downTime; + TouchResult touchResult; - int32_t keyCode = device->touchScreen.currentVirtualKey.keyCode; - int32_t scanCode = device->touchScreen.currentVirtualKey.scanCode; - nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime; - int32_t metaState = globalMetaState(); + { // acquire virtual key lock + AutoMutex _l(mVirtualKeyLock); + + if (mCurrentVirtualKey.down) { + if (mCurrentTouch.pointerCount == 0) { + // Pointer went up while virtual key was down. + mCurrentVirtualKey.down = false; +#if DEBUG_VIRTUAL_KEYS + LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + keyEventAction = AKEY_EVENT_ACTION_UP; + keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; + touchResult = SKIP_TOUCH; + goto DispatchVirtualKey; + } + + if (mCurrentTouch.pointerCount == 1) { + int32_t x = mCurrentTouch.pointers[0].x; + int32_t y = mCurrentTouch.pointers[0].y; + const VirtualKey* virtualKey = findVirtualKeyHitLvk(x, y); + if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { + // Pointer is still within the space of the virtual key. + return SKIP_TOUCH; + } + } + + // Pointer left virtual key area or another pointer also went down. + // Send key cancellation and drop the stroke so subsequent motions will be + // considered fresh downs. This is useful when the user swipes away from the + // virtual key area into the main display surface. + mCurrentVirtualKey.down = false; +#if DEBUG_VIRTUAL_KEYS + LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + keyEventAction = AKEY_EVENT_ACTION_UP; + keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY + | AKEY_EVENT_FLAG_CANCELED; + touchResult = DROP_STROKE; + goto DispatchVirtualKey; + } else { + if (mCurrentTouch.pointerCount >= 1 && mLastTouch.pointerCount == 0) { + // Pointer just went down. Handle off-screen touches, if needed. + int32_t x = mCurrentTouch.pointers[0].x; + int32_t y = mCurrentTouch.pointers[0].y; + if (! isPointInsideSurface(x, y)) { + // If exactly one pointer went down, check for virtual key hit. + // Otherwise we will drop the entire stroke. + if (mCurrentTouch.pointerCount == 1) { + const VirtualKey* virtualKey = findVirtualKeyHitLvk(x, y); + if (virtualKey) { + mCurrentVirtualKey.down = true; + mCurrentVirtualKey.downTime = when; + mCurrentVirtualKey.keyCode = virtualKey->keyCode; + mCurrentVirtualKey.scanCode = virtualKey->scanCode; +#if DEBUG_VIRTUAL_KEYS + LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + keyEventAction = AKEY_EVENT_ACTION_DOWN; + keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM + | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; + touchResult = SKIP_TOUCH; + goto DispatchVirtualKey; + } + } + return DROP_STROKE; + } + } + return DISPATCH_TOUCH; + } + + DispatchVirtualKey: + // Collect remaining state needed to dispatch virtual key. + keyCode = mCurrentVirtualKey.keyCode; + scanCode = mCurrentVirtualKey.scanCode; + downTime = mCurrentVirtualKey.downTime; + } // release virtual key lock + + // Dispatch virtual key. + int32_t metaState = mContext->getGlobalMetaState(); if (keyEventAction == AKEY_EVENT_ACTION_DOWN) { - mPolicy->virtualKeyDownFeedback(); + getPolicy()->virtualKeyDownFeedback(); } - int32_t policyActions = mPolicy->interceptKey(when, device->id, + int32_t policyActions = getPolicy()->interceptKey(when, getDeviceId(), keyEventAction == AKEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags); - if (applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) { - mDispatcher->notifyKey(when, device->id, AINPUT_SOURCE_KEYBOARD, policyFlags, + if (applyStandardPolicyActions(when, policyActions)) { + getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); } + return touchResult; } -void InputReader::dispatchTouches(nsecs_t when, - InputDevice* device, uint32_t policyFlags) { - uint32_t currentPointerCount = device->touchScreen.currentTouch.pointerCount; - uint32_t lastPointerCount = device->touchScreen.lastTouch.pointerCount; +void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { + uint32_t currentPointerCount = mCurrentTouch.pointerCount; + uint32_t lastPointerCount = mLastTouch.pointerCount; if (currentPointerCount == 0 && lastPointerCount == 0) { return; // nothing to do! } - BitSet32 currentIdBits = device->touchScreen.currentTouch.idBits; - BitSet32 lastIdBits = device->touchScreen.lastTouch.idBits; + BitSet32 currentIdBits = mCurrentTouch.idBits; + BitSet32 lastIdBits = mLastTouch.idBits; if (currentIdBits == lastIdBits) { // No pointer id changes so this is a move event. // The dispatcher takes care of batching moves so we don't have to deal with that here. int32_t motionEventAction = AMOTION_EVENT_ACTION_MOVE; - dispatchTouch(when, device, policyFlags, & device->touchScreen.currentTouch, + dispatchTouch(when, policyFlags, & mCurrentTouch, currentIdBits, -1, motionEventAction); } else { // There may be pointers going up and pointers going down at the same time when pointer @@ -791,7 +1536,7 @@ void InputReader::dispatchTouches(nsecs_t when, motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP; } - dispatchTouch(when, device, policyFlags, & device->touchScreen.lastTouch, + dispatchTouch(when, policyFlags, & mLastTouch, oldActiveIdBits, upId, motionEventAction); } @@ -804,40 +1549,24 @@ void InputReader::dispatchTouches(nsecs_t when, int32_t motionEventAction; if (oldActiveIdBits.isEmpty()) { motionEventAction = AMOTION_EVENT_ACTION_DOWN; - device->touchScreen.downTime = when; + mDownTime = when; } else { motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN; } - dispatchTouch(when, device, policyFlags, & device->touchScreen.currentTouch, + dispatchTouch(when, policyFlags, & mCurrentTouch, activeIdBits, downId, motionEventAction); } } } -void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t policyFlags, - InputDevice::TouchData* touch, BitSet32 idBits, uint32_t changedId, +void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, + TouchData* touch, BitSet32 idBits, uint32_t changedId, int32_t motionEventAction) { - int32_t orientedWidth, orientedHeight; - switch (mDisplayOrientation) { - case InputReaderPolicyInterface::ROTATION_90: - case InputReaderPolicyInterface::ROTATION_270: - orientedWidth = mDisplayHeight; - orientedHeight = mDisplayWidth; - break; - default: - orientedWidth = mDisplayWidth; - orientedHeight = mDisplayHeight; - break; - } - uint32_t pointerCount = 0; int32_t pointerIds[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; - const InputDevice::TouchScreenState::Precalculated& precalculated = - device->touchScreen.precalculated; - // Walk through the the active pointers and map touch screen coordinates (TouchData) into // display coordinates (PointerCoords) and adjust for display orientation. while (! idBits.isEmpty()) { @@ -845,55 +1574,57 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli idBits.clearBit(id); uint32_t index = touch->idToIndex[id]; - float x = float(touch->pointers[index].x - - precalculated.xOrigin) * precalculated.xScale; - float y = float(touch->pointers[index].y - - precalculated.yOrigin) * precalculated.yScale; - float pressure = float(touch->pointers[index].pressure - - precalculated.pressureOrigin) * precalculated.pressureScale; - float size = float(touch->pointers[index].size - - precalculated.sizeOrigin) * precalculated.sizeScale; + float x = float(touch->pointers[index].x - mXOrigin) * mXScale; + float y = float(touch->pointers[index].y - mYOrigin) * mYScale; + float pressure = float(touch->pointers[index].pressure - mPressureOrigin) * mPressureScale; + float size = float(touch->pointers[index].size - mSizeOrigin) * mSizeScale; - float orientation = float(touch->pointers[index].orientation) - * precalculated.orientationScale; + float orientation = float(touch->pointers[index].orientation) * mOrientationScale; - bool vertical = abs(orientation) <= M_PI / 8; + float touchMajor, touchMinor, toolMajor, toolMinor; + if (abs(orientation) <= M_PI_4) { + // Nominally vertical orientation: scale major axis by Y, and scale minor axis by X. + touchMajor = float(touch->pointers[index].touchMajor) * mYScale; + touchMinor = float(touch->pointers[index].touchMinor) * mXScale; + toolMajor = float(touch->pointers[index].toolMajor) * mYScale; + toolMinor = float(touch->pointers[index].toolMinor) * mXScale; + } else { + // Nominally horizontal orientation: scale major axis by X, and scale minor axis by Y. + touchMajor = float(touch->pointers[index].touchMajor) * mXScale; + touchMinor = float(touch->pointers[index].touchMinor) * mYScale; + toolMajor = float(touch->pointers[index].toolMajor) * mXScale; + toolMinor = float(touch->pointers[index].toolMinor) * mYScale; + } - switch (mDisplayOrientation) { + switch (mSurfaceOrientation) { case InputReaderPolicyInterface::ROTATION_90: { float xTemp = x; x = y; - y = mDisplayWidth - xTemp; - vertical = ! vertical; + y = mOrientedSurfaceWidth - xTemp; + orientation -= M_PI_2; + if (orientation < - M_PI_2) { + orientation += M_PI; + } break; } case InputReaderPolicyInterface::ROTATION_180: { - x = mDisplayWidth - x; - y = mDisplayHeight - y; + x = mOrientedSurfaceWidth - x; + y = mOrientedSurfaceHeight - y; + orientation = - orientation; break; } case InputReaderPolicyInterface::ROTATION_270: { float xTemp = x; - x = mDisplayHeight - y; + x = mOrientedSurfaceHeight - y; y = xTemp; - vertical = ! vertical; + orientation += M_PI_2; + if (orientation > M_PI_2) { + orientation -= M_PI; + } break; } } - float touchMajor, touchMinor, toolMajor, toolMinor; - if (vertical) { - touchMajor = float(touch->pointers[index].touchMajor) * precalculated.yScale; - touchMinor = float(touch->pointers[index].touchMinor) * precalculated.xScale; - toolMajor = float(touch->pointers[index].toolMajor) * precalculated.yScale; - toolMinor = float(touch->pointers[index].toolMinor) * precalculated.xScale; - } else { - touchMajor = float(touch->pointers[index].touchMajor) * precalculated.xScale; - touchMinor = float(touch->pointers[index].touchMinor) * precalculated.yScale; - toolMajor = float(touch->pointers[index].toolMajor) * precalculated.xScale; - toolMinor = float(touch->pointers[index].toolMinor) * precalculated.yScale; - } - pointerIds[pointerCount] = int32_t(id); pointerCoords[pointerCount].x = x; @@ -919,561 +1650,984 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) { if (pointerCoords[0].x <= 0) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT; - } else if (pointerCoords[0].x >= orientedWidth) { + } else if (pointerCoords[0].x >= mOrientedSurfaceWidth) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT; } if (pointerCoords[0].y <= 0) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP; - } else if (pointerCoords[0].y >= orientedHeight) { + } else if (pointerCoords[0].y >= mOrientedSurfaceHeight) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM; } } - nsecs_t downTime = device->touchScreen.downTime; - mDispatcher->notifyMotion(when, device->id, AINPUT_SOURCE_TOUCHSCREEN, policyFlags, - motionEventAction, globalMetaState(), motionEventEdgeFlags, + getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TOUCHSCREEN, policyFlags, + motionEventAction, getContext()->getGlobalMetaState(), motionEventEdgeFlags, pointerCount, pointerIds, pointerCoords, - 0, 0, downTime); + mOrientedXPrecision, mOrientedYPrecision, mDownTime); } -void InputReader::onTrackballStateChanged(nsecs_t when, - InputDevice* device) { - static const uint32_t DELTA_FIELDS = - InputDevice::TrackballState::Accumulator::FIELD_REL_X - | InputDevice::TrackballState::Accumulator::FIELD_REL_Y; - - /* Refresh display properties so we can trackball moves according to display orientation */ - - if (! refreshDisplayProperties()) { - return; +bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { + if (mAxes.x.valid && mAxes.y.valid) { + return x >= mAxes.x.minValue && x <= mAxes.x.maxValue + && y >= mAxes.y.minValue && y <= mAxes.y.maxValue; } - - /* Update device state */ - - uint32_t fields = device->trackball.accumulator.fields; - bool downChanged = fields & InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE; - bool deltaChanged = fields & DELTA_FIELDS; - - bool down; - if (downChanged) { - if (device->trackball.accumulator.btnMouse) { - device->trackball.current.down = true; - device->trackball.current.downTime = when; - down = true; - } else { - device->trackball.current.down = false; - down = false; - } - } else { - down = device->trackball.current.down; - } - - /* Apply policy */ - - int32_t policyActions = mPolicy->interceptTrackball(when, downChanged, down, deltaChanged); - - uint32_t policyFlags = 0; - if (! applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) { - return; // event dropped - } - - /* Enqueue motion event for dispatch */ - - int32_t motionEventAction; - if (downChanged) { - motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; - } else { - motionEventAction = AMOTION_EVENT_ACTION_MOVE; - } - - int32_t pointerId = 0; - PointerCoords pointerCoords; - pointerCoords.x = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_X - ? device->trackball.accumulator.relX * device->trackball.precalculated.xScale : 0; - pointerCoords.y = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_Y - ? device->trackball.accumulator.relY * device->trackball.precalculated.yScale : 0; - pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise. - pointerCoords.size = 0; - pointerCoords.touchMajor = 0; - pointerCoords.touchMinor = 0; - pointerCoords.toolMajor = 0; - pointerCoords.toolMinor = 0; - pointerCoords.orientation = 0; - - float temp; - switch (mDisplayOrientation) { - case InputReaderPolicyInterface::ROTATION_90: - temp = pointerCoords.x; - pointerCoords.x = pointerCoords.y; - pointerCoords.y = - temp; - break; - - case InputReaderPolicyInterface::ROTATION_180: - pointerCoords.x = - pointerCoords.x; - pointerCoords.y = - pointerCoords.y; - break; - - case InputReaderPolicyInterface::ROTATION_270: - temp = pointerCoords.x; - pointerCoords.x = - pointerCoords.y; - pointerCoords.y = temp; - break; - } - - mDispatcher->notifyMotion(when, device->id, AINPUT_SOURCE_TRACKBALL, policyFlags, - motionEventAction, globalMetaState(), AMOTION_EVENT_EDGE_FLAG_NONE, - 1, & pointerId, & pointerCoords, - device->trackball.precalculated.xPrecision, - device->trackball.precalculated.yPrecision, - device->trackball.current.downTime); -} - -void InputReader::onConfigurationChanged(nsecs_t when) { - // Reset global meta state because it depends on the list of all configured devices. - resetGlobalMetaState(); - - // Reset virtual keys, just in case. - updateExportedVirtualKeyState(); - - // Update input configuration. - updateExportedInputConfiguration(); - - // Enqueue configuration changed. - mDispatcher->notifyConfigurationChanged(when); -} - -bool InputReader::applyStandardInputDispatchPolicyActions(nsecs_t when, - int32_t policyActions, uint32_t* policyFlags) { - if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) { - mDispatcher->notifyAppSwitchComing(when); - } - - if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) { - *policyFlags |= POLICY_FLAG_WOKE_HERE; - } - - if (policyActions & InputReaderPolicyInterface::ACTION_BRIGHT_HERE) { - *policyFlags |= POLICY_FLAG_BRIGHT_HERE; - } - - return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH; -} - -void InputReader::resetDisplayProperties() { - mDisplayWidth = mDisplayHeight = -1; - mDisplayOrientation = -1; -} - -bool InputReader::refreshDisplayProperties() { - int32_t newWidth, newHeight, newOrientation; - if (mPolicy->getDisplayInfo(0, & newWidth, & newHeight, & newOrientation)) { - if (newWidth != mDisplayWidth || newHeight != mDisplayHeight) { - LOGD("Display size changed from %dx%d to %dx%d, updating device configuration", - mDisplayWidth, mDisplayHeight, newWidth, newHeight); - - mDisplayWidth = newWidth; - mDisplayHeight = newHeight; - - for (size_t i = 0; i < mDevices.size(); i++) { - configureDeviceForCurrentDisplaySize(mDevices.valueAt(i)); - } - } - - if (newOrientation != mDisplayOrientation) { - LOGD("Display orientation changed to %d", mDisplayOrientation); - - mDisplayOrientation = newOrientation; - } - return true; - } else { - resetDisplayProperties(); - return false; - } -} - -InputDevice* InputReader::getDevice(int32_t deviceId) { - ssize_t index = mDevices.indexOfKey(deviceId); - return index >= 0 ? mDevices.valueAt((size_t) index) : NULL; -} - -InputDevice* InputReader::getNonIgnoredDevice(int32_t deviceId) { - InputDevice* device = getDevice(deviceId); - return device && ! device->ignored ? device : NULL; -} - -void InputReader::addDevice(nsecs_t when, int32_t deviceId) { - uint32_t classes = mEventHub->getDeviceClasses(deviceId); - String8 name = mEventHub->getDeviceName(deviceId); - InputDevice* device = new InputDevice(deviceId, classes, name); - - if (classes != 0) { - LOGI("Device added: id=0x%x, name=%s, classes=%02x", device->id, - device->name.string(), device->classes); - - configureDevice(device); - } else { - LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", device->id, - device->name.string()); - - device->ignored = true; - } - - device->reset(); - - mDevices.add(deviceId, device); - - if (! device->ignored) { - onConfigurationChanged(when); - } -} - -void InputReader::removeDevice(nsecs_t when, InputDevice* device) { - mDevices.removeItem(device->id); - - if (! device->ignored) { - LOGI("Device removed: id=0x%x, name=%s, classes=%02x", device->id, - device->name.string(), device->classes); - - onConfigurationChanged(when); - } else { - LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)", device->id, - device->name.string()); - } - - delete device; -} - -void InputReader::configureDevice(InputDevice* device) { - if (device->isMultiTouchScreen()) { - configureAbsoluteAxisInfo(device, ABS_MT_POSITION_X, "X", - & device->touchScreen.parameters.xAxis); - configureAbsoluteAxisInfo(device, ABS_MT_POSITION_Y, "Y", - & device->touchScreen.parameters.yAxis); - configureAbsoluteAxisInfo(device, ABS_MT_TOUCH_MAJOR, "Pressure", - & device->touchScreen.parameters.pressureAxis); - configureAbsoluteAxisInfo(device, ABS_MT_WIDTH_MAJOR, "Size", - & device->touchScreen.parameters.sizeAxis); - configureAbsoluteAxisInfo(device, ABS_MT_ORIENTATION, "Orientation", - & device->touchScreen.parameters.orientationAxis); - } else if (device->isSingleTouchScreen()) { - configureAbsoluteAxisInfo(device, ABS_X, "X", - & device->touchScreen.parameters.xAxis); - configureAbsoluteAxisInfo(device, ABS_Y, "Y", - & device->touchScreen.parameters.yAxis); - configureAbsoluteAxisInfo(device, ABS_PRESSURE, "Pressure", - & device->touchScreen.parameters.pressureAxis); - configureAbsoluteAxisInfo(device, ABS_TOOL_WIDTH, "Size", - & device->touchScreen.parameters.sizeAxis); - device->touchScreen.parameters.orientationAxis.valid = false; - } - - if (device->isTouchScreen()) { - device->touchScreen.parameters.useBadTouchFilter = - mPolicy->filterTouchEvents(); - device->touchScreen.parameters.useAveragingTouchFilter = - mPolicy->filterTouchEvents(); - device->touchScreen.parameters.useJumpyTouchFilter = - mPolicy->filterJumpyTouchEvents(); - - if (device->touchScreen.parameters.pressureAxis.valid) { - device->touchScreen.precalculated.pressureOrigin = - device->touchScreen.parameters.pressureAxis.minValue; - device->touchScreen.precalculated.pressureScale = - 1.0f / device->touchScreen.parameters.pressureAxis.range; - } else { - device->touchScreen.precalculated.pressureOrigin = 0; - device->touchScreen.precalculated.pressureScale = 1.0f; - } - - if (device->touchScreen.parameters.sizeAxis.valid) { - device->touchScreen.precalculated.sizeOrigin = - device->touchScreen.parameters.sizeAxis.minValue; - device->touchScreen.precalculated.sizeScale = - 1.0f / device->touchScreen.parameters.sizeAxis.range; - } else { - device->touchScreen.precalculated.sizeOrigin = 0; - device->touchScreen.precalculated.sizeScale = 1.0f; - } - - if (device->touchScreen.parameters.orientationAxis.valid - && device->touchScreen.parameters.orientationAxis.maxValue > 0) { - device->touchScreen.precalculated.orientationScale = - M_PI_4 / device->touchScreen.parameters.orientationAxis.maxValue; - } else { - device->touchScreen.precalculated.orientationScale = 0.0f; - } - } - - if (device->isTrackball()) { - device->trackball.precalculated.xPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - device->trackball.precalculated.yPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - device->trackball.precalculated.xScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - device->trackball.precalculated.yScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - } - - configureDeviceForCurrentDisplaySize(device); -} - -void InputReader::configureDeviceForCurrentDisplaySize(InputDevice* device) { - if (device->isTouchScreen()) { - if (device->touchScreen.parameters.xAxis.valid - && device->touchScreen.parameters.yAxis.valid) { - device->touchScreen.precalculated.xOrigin = - device->touchScreen.parameters.xAxis.minValue; - device->touchScreen.precalculated.yOrigin = - device->touchScreen.parameters.yAxis.minValue; - - if (mDisplayWidth < 0) { - LOGD("Skipping part of touch screen configuration since display size is unknown."); - - device->touchScreen.precalculated.xScale = 1.0f; - device->touchScreen.precalculated.yScale = 1.0f; - } else { - LOGI("Device configured: id=0x%x, name=%s (display size was changed)", device->id, - device->name.string()); - - device->touchScreen.precalculated.xScale = - float(mDisplayWidth) / device->touchScreen.parameters.xAxis.range; - device->touchScreen.precalculated.yScale = - float(mDisplayHeight) / device->touchScreen.parameters.yAxis.range; - - configureVirtualKeys(device); - } - } else { - device->touchScreen.precalculated.xOrigin = 0; - device->touchScreen.precalculated.xScale = 1.0f; - device->touchScreen.precalculated.yOrigin = 0; - device->touchScreen.precalculated.yScale = 1.0f; - } - } -} - -void InputReader::configureVirtualKeys(InputDevice* device) { - assert(device->touchScreen.parameters.xAxis.valid - && device->touchScreen.parameters.yAxis.valid); - - device->touchScreen.virtualKeys.clear(); - - Vector virtualKeyDefinitions; - mPolicy->getVirtualKeyDefinitions(device->name, virtualKeyDefinitions); - if (virtualKeyDefinitions.size() == 0) { - return; - } - - device->touchScreen.virtualKeys.setCapacity(virtualKeyDefinitions.size()); - - int32_t touchScreenLeft = device->touchScreen.parameters.xAxis.minValue; - int32_t touchScreenTop = device->touchScreen.parameters.yAxis.minValue; - int32_t touchScreenWidth = device->touchScreen.parameters.xAxis.range; - int32_t touchScreenHeight = device->touchScreen.parameters.yAxis.range; - - for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) { - const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition = - virtualKeyDefinitions[i]; - - device->touchScreen.virtualKeys.add(); - InputDevice::VirtualKey& virtualKey = - device->touchScreen.virtualKeys.editTop(); - - virtualKey.scanCode = virtualKeyDefinition.scanCode; - int32_t keyCode; - uint32_t flags; - if (mEventHub->scancodeToKeycode(device->id, virtualKey.scanCode, - & keyCode, & flags)) { - LOGW(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); - device->touchScreen.virtualKeys.pop(); // drop the key - continue; - } - - virtualKey.keyCode = keyCode; - virtualKey.flags = flags; - - // convert the key definition's display coordinates into touch coordinates for a hit box - int32_t halfWidth = virtualKeyDefinition.width / 2; - int32_t halfHeight = virtualKeyDefinition.height / 2; - - virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) - * touchScreenWidth / mDisplayWidth + touchScreenLeft; - virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth) - * touchScreenWidth / mDisplayWidth + touchScreenLeft; - virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) - * touchScreenHeight / mDisplayHeight + touchScreenTop; - virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) - * touchScreenHeight / mDisplayHeight + touchScreenTop; - - LOGI(" VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d", - virtualKey.scanCode, virtualKey.keyCode, - virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); - } -} - -void InputReader::configureAbsoluteAxisInfo(InputDevice* device, - int axis, const char* name, InputDevice::AbsoluteAxisInfo* out) { - if (! mEventHub->getAbsoluteInfo(device->id, axis, - & out->minValue, & out->maxValue, & out->flat, &out->fuzz)) { - out->range = out->maxValue - out->minValue; - if (out->range != 0) { - LOGI(" %s: min=%d max=%d flat=%d fuzz=%d", - name, out->minValue, out->maxValue, out->flat, out->fuzz); - out->valid = true; - return; - } - } - - out->valid = false; - out->minValue = 0; - out->maxValue = 0; - out->flat = 0; - out->fuzz = 0; - out->range = 0; - LOGI(" %s: unknown axis values, marking as invalid", name); -} - -void InputReader::configureExcludedDevices() { - Vector excludedDeviceNames; - mPolicy->getExcludedDeviceNames(excludedDeviceNames); - - for (size_t i = 0; i < excludedDeviceNames.size(); i++) { - mEventHub->addExcludedDevice(excludedDeviceNames[i]); - } -} - -void InputReader::resetGlobalMetaState() { - mGlobalMetaState = -1; -} - -int32_t InputReader::globalMetaState() { - if (mGlobalMetaState == -1) { - mGlobalMetaState = 0; - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - if (device->isKeyboard()) { - mGlobalMetaState |= device->keyboard.current.metaState; - } - } - } - return mGlobalMetaState; -} - -void InputReader::updateExportedVirtualKeyState() { - int32_t keyCode = -1, scanCode = -1; - - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - if (device->isTouchScreen()) { - if (device->touchScreen.currentVirtualKey.status - == InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN) { - keyCode = device->touchScreen.currentVirtualKey.keyCode; - scanCode = device->touchScreen.currentVirtualKey.scanCode; - } - } - } - - { // acquire exported state lock - AutoMutex _l(mExportedStateLock); - - mExportedVirtualKeyCode = keyCode; - mExportedVirtualScanCode = scanCode; - } // release exported state lock -} - -bool InputReader::getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const { - { // acquire exported state lock - AutoMutex _l(mExportedStateLock); - - *outKeyCode = mExportedVirtualKeyCode; - *outScanCode = mExportedVirtualScanCode; - return mExportedVirtualKeyCode != -1; - } // release exported state lock -} - -void InputReader::updateExportedInputConfiguration() { - int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH; - int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS; - int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV; - - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - int32_t deviceClasses = device->classes; - - if (deviceClasses & INPUT_DEVICE_CLASS_TOUCHSCREEN) { - touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER; - } - if (deviceClasses & INPUT_DEVICE_CLASS_ALPHAKEY) { - keyboardConfig = InputConfiguration::KEYBOARD_QWERTY; - } - if (deviceClasses & INPUT_DEVICE_CLASS_TRACKBALL) { - navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL; - } else if (deviceClasses & INPUT_DEVICE_CLASS_DPAD) { - navigationConfig = InputConfiguration::NAVIGATION_DPAD; - } - } - - { // acquire exported state lock - AutoMutex _l(mExportedStateLock); - - mExportedInputConfiguration.touchScreen = touchScreenConfig; - mExportedInputConfiguration.keyboard = keyboardConfig; - mExportedInputConfiguration.navigation = navigationConfig; - } // release exported state lock -} - -void InputReader::getCurrentInputConfiguration(InputConfiguration* outConfiguration) const { - { // acquire exported state lock - AutoMutex _l(mExportedStateLock); - - *outConfiguration = mExportedInputConfiguration; - } // release exported state lock -} - -int32_t InputReader::getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t scanCode) const { - { // acquire exported state lock - AutoMutex _l(mExportedStateLock); - - if (mExportedVirtualScanCode == scanCode) { - return AKEY_STATE_VIRTUAL; - } - } // release exported state lock - - return mEventHub->getScanCodeState(deviceId, deviceClasses, scanCode); -} - -int32_t InputReader::getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses, - int32_t keyCode) const { - { // acquire exported state lock - AutoMutex _l(mExportedStateLock); - - if (mExportedVirtualKeyCode == keyCode) { - return AKEY_STATE_VIRTUAL; - } - } // release exported state lock - - return mEventHub->getKeyCodeState(deviceId, deviceClasses, keyCode); -} - -int32_t InputReader::getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses, - int32_t sw) const { - return mEventHub->getSwitchState(deviceId, deviceClasses, sw); -} - -bool InputReader::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { - return mEventHub->hasKeys(numCodes, keyCodes, outFlags); -} - - -// --- InputReaderThread --- - -InputReaderThread::InputReaderThread(const sp& reader) : - Thread(/*canCallJava*/ true), mReader(reader) { -} - -InputReaderThread::~InputReaderThread() { -} - -bool InputReaderThread::threadLoop() { - mReader->loopOnce(); return true; } +const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLvk(int32_t x, int32_t y) { + for (size_t i = 0; i < mVirtualKeys.size(); i++) { + const VirtualKey& virtualKey = mVirtualKeys[i]; + +#if DEBUG_VIRTUAL_KEYS + LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " + "left=%d, top=%d, right=%d, bottom=%d", + x, y, + virtualKey.keyCode, virtualKey.scanCode, + virtualKey.hitLeft, virtualKey.hitTop, + virtualKey.hitRight, virtualKey.hitBottom); +#endif + + if (virtualKey.isHit(x, y)) { + return & virtualKey; + } + } + + return NULL; +} + +void TouchInputMapper::calculatePointerIds() { + uint32_t currentPointerCount = mCurrentTouch.pointerCount; + uint32_t lastPointerCount = mLastTouch.pointerCount; + + if (currentPointerCount == 0) { + // No pointers to assign. + mCurrentTouch.idBits.clear(); + } else if (lastPointerCount == 0) { + // All pointers are new. + mCurrentTouch.idBits.clear(); + for (uint32_t i = 0; i < currentPointerCount; i++) { + mCurrentTouch.pointers[i].id = i; + mCurrentTouch.idToIndex[i] = i; + mCurrentTouch.idBits.markBit(i); + } + } else if (currentPointerCount == 1 && lastPointerCount == 1) { + // Only one pointer and no change in count so it must have the same id as before. + uint32_t id = mLastTouch.pointers[0].id; + mCurrentTouch.pointers[0].id = id; + mCurrentTouch.idToIndex[id] = 0; + mCurrentTouch.idBits.value = BitSet32::valueForBit(id); + } else { + // General case. + // We build a heap of squared euclidean distances between current and last pointers + // associated with the current and last pointer indices. Then, we find the best + // match (by distance) for each current pointer. + PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; + + uint32_t heapSize = 0; + for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; + currentPointerIndex++) { + for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; + lastPointerIndex++) { + int64_t deltaX = mCurrentTouch.pointers[currentPointerIndex].x + - mLastTouch.pointers[lastPointerIndex].x; + int64_t deltaY = mCurrentTouch.pointers[currentPointerIndex].y + - mLastTouch.pointers[lastPointerIndex].y; + + uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); + + // Insert new element into the heap (sift up). + heap[heapSize].currentPointerIndex = currentPointerIndex; + heap[heapSize].lastPointerIndex = lastPointerIndex; + heap[heapSize].distance = distance; + heapSize += 1; + } + } + + // Heapify + for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) { + startIndex -= 1; + for (uint32_t parentIndex = startIndex; ;) { + uint32_t childIndex = parentIndex * 2 + 1; + if (childIndex >= heapSize) { + break; + } + + if (childIndex + 1 < heapSize + && heap[childIndex + 1].distance < heap[childIndex].distance) { + childIndex += 1; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + } + +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", + i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, + heap[i].distance); + } +#endif + + // Pull matches out by increasing order of distance. + // To avoid reassigning pointers that have already been matched, the loop keeps track + // of which last and current pointers have been matched using the matchedXXXBits variables. + // It also tracks the used pointer id bits. + BitSet32 matchedLastBits(0); + BitSet32 matchedCurrentBits(0); + BitSet32 usedIdBits(0); + bool first = true; + for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) { + for (;;) { + if (first) { + // The first time through the loop, we just consume the root element of + // the heap (the one with smallest distance). + first = false; + } else { + // Previous iterations consumed the root element of the heap. + // Pop root element off of the heap (sift down). + heapSize -= 1; + assert(heapSize > 0); + + // Sift down. + heap[0] = heap[heapSize]; + for (uint32_t parentIndex = 0; ;) { + uint32_t childIndex = parentIndex * 2 + 1; + if (childIndex >= heapSize) { + break; + } + + if (childIndex + 1 < heapSize + && heap[childIndex + 1].distance < heap[childIndex].distance) { + childIndex += 1; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", + i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, + heap[i].distance); + } +#endif + } + + uint32_t currentPointerIndex = heap[0].currentPointerIndex; + if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched + + uint32_t lastPointerIndex = heap[0].lastPointerIndex; + if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched + + matchedCurrentBits.markBit(currentPointerIndex); + matchedLastBits.markBit(lastPointerIndex); + + uint32_t id = mLastTouch.pointers[lastPointerIndex].id; + mCurrentTouch.pointers[currentPointerIndex].id = id; + mCurrentTouch.idToIndex[id] = currentPointerIndex; + usedIdBits.markBit(id); + +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld", + lastPointerIndex, currentPointerIndex, id, heap[0].distance); +#endif + break; + } + } + + // Assign fresh ids to new pointers. + if (currentPointerCount > lastPointerCount) { + for (uint32_t i = currentPointerCount - lastPointerCount; ;) { + uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit(); + uint32_t id = usedIdBits.firstUnmarkedBit(); + + mCurrentTouch.pointers[currentPointerIndex].id = id; + mCurrentTouch.idToIndex[id] = currentPointerIndex; + usedIdBits.markBit(id); + +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - assigned: cur=%d, id=%d", + currentPointerIndex, id); +#endif + + if (--i == 0) break; // done + matchedCurrentBits.markBit(currentPointerIndex); + } + } + + // Fix id bits. + mCurrentTouch.idBits = usedIdBits; + } +} + +/* Special hack for devices that have bad screen data: if one of the + * points has moved more than a screen height from the last position, + * then drop it. */ +bool TouchInputMapper::applyBadTouchFilter() { + // This hack requires valid axis parameters. + if (! mAxes.y.valid) { + return false; + } + + uint32_t pointerCount = mCurrentTouch.pointerCount; + + // Nothing to do if there are no points. + if (pointerCount == 0) { + return false; + } + + // Don't do anything if a finger is going down or up. We run + // here before assigning pointer IDs, so there isn't a good + // way to do per-finger matching. + if (pointerCount != mLastTouch.pointerCount) { + return false; + } + + // We consider a single movement across more than a 7/16 of + // the long size of the screen to be bad. This was a magic value + // determined by looking at the maximum distance it is feasible + // to actually move in one sample. + int32_t maxDeltaY = mAxes.y.getRange() * 7 / 16; + + // XXX The original code in InputDevice.java included commented out + // code for testing the X axis. Note that when we drop a point + // we don't actually restore the old X either. Strange. + // The old code also tries to track when bad points were previously + // detected but it turns out that due to the placement of a "break" + // at the end of the loop, we never set mDroppedBadPoint to true + // so it is effectively dead code. + // Need to figure out if the old code is busted or just overcomplicated + // but working as intended. + + // Look through all new points and see if any are farther than + // acceptable from all previous points. + for (uint32_t i = pointerCount; i-- > 0; ) { + int32_t y = mCurrentTouch.pointers[i].y; + int32_t closestY = INT_MAX; + int32_t closestDeltaY = 0; + +#if DEBUG_HACKS + LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y); +#endif + + for (uint32_t j = pointerCount; j-- > 0; ) { + int32_t lastY = mLastTouch.pointers[j].y; + int32_t deltaY = abs(y - lastY); + +#if DEBUG_HACKS + LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d", + j, lastY, deltaY); +#endif + + if (deltaY < maxDeltaY) { + goto SkipSufficientlyClosePoint; + } + if (deltaY < closestDeltaY) { + closestDeltaY = deltaY; + closestY = lastY; + } + } + + // Must not have found a close enough match. +#if DEBUG_HACKS + LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d", + i, y, closestY, closestDeltaY, maxDeltaY); +#endif + + mCurrentTouch.pointers[i].y = closestY; + return true; // XXX original code only corrects one point + + SkipSufficientlyClosePoint: ; + } + + // No change. + return false; +} + +/* Special hack for devices that have bad screen data: drop points where + * the coordinate value for one axis has jumped to the other pointer's location. + */ +bool TouchInputMapper::applyJumpyTouchFilter() { + // This hack requires valid axis parameters. + if (! mAxes.y.valid) { + return false; + } + + uint32_t pointerCount = mCurrentTouch.pointerCount; + if (mLastTouch.pointerCount != pointerCount) { +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Different pointer count %d -> %d", + mLastTouch.pointerCount, pointerCount); + for (uint32_t i = 0; i < pointerCount; i++) { + LOGD(" Pointer %d (%d, %d)", i, + mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y); + } +#endif + + if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) { + if (mLastTouch.pointerCount == 1 && pointerCount == 2) { + // Just drop the first few events going from 1 to 2 pointers. + // They're bad often enough that they're not worth considering. + mCurrentTouch.pointerCount = 1; + mJumpyTouchFilter.jumpyPointsDropped += 1; + +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Pointer 2 dropped"); +#endif + return true; + } else if (mLastTouch.pointerCount == 2 && pointerCount == 1) { + // The event when we go from 2 -> 1 tends to be messed up too + mCurrentTouch.pointerCount = 2; + mCurrentTouch.pointers[0] = mLastTouch.pointers[0]; + mCurrentTouch.pointers[1] = mLastTouch.pointers[1]; + mJumpyTouchFilter.jumpyPointsDropped += 1; + +#if DEBUG_HACKS + for (int32_t i = 0; i < 2; i++) { + LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i, + mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y); + } +#endif + return true; + } + } + // Reset jumpy points dropped on other transitions or if limit exceeded. + mJumpyTouchFilter.jumpyPointsDropped = 0; + +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Transition - drop limit reset"); +#endif + return false; + } + + // We have the same number of pointers as last time. + // A 'jumpy' point is one where the coordinate value for one axis + // has jumped to the other pointer's location. No need to do anything + // else if we only have one pointer. + if (pointerCount < 2) { + return false; + } + + if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) { + int jumpyEpsilon = mAxes.y.getRange() / JUMPY_EPSILON_DIVISOR; + + // We only replace the single worst jumpy point as characterized by pointer distance + // in a single axis. + int32_t badPointerIndex = -1; + int32_t badPointerReplacementIndex = -1; + int32_t badPointerDistance = INT_MIN; // distance to be corrected + + for (uint32_t i = pointerCount; i-- > 0; ) { + int32_t x = mCurrentTouch.pointers[i].x; + int32_t y = mCurrentTouch.pointers[i].y; + +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y); +#endif + + // Check if a touch point is too close to another's coordinates + bool dropX = false, dropY = false; + for (uint32_t j = 0; j < pointerCount; j++) { + if (i == j) { + continue; + } + + if (abs(x - mCurrentTouch.pointers[j].x) <= jumpyEpsilon) { + dropX = true; + break; + } + + if (abs(y - mCurrentTouch.pointers[j].y) <= jumpyEpsilon) { + dropY = true; + break; + } + } + if (! dropX && ! dropY) { + continue; // not jumpy + } + + // Find a replacement candidate by comparing with older points on the + // complementary (non-jumpy) axis. + int32_t distance = INT_MIN; // distance to be corrected + int32_t replacementIndex = -1; + + if (dropX) { + // X looks too close. Find an older replacement point with a close Y. + int32_t smallestDeltaY = INT_MAX; + for (uint32_t j = 0; j < pointerCount; j++) { + int32_t deltaY = abs(y - mLastTouch.pointers[j].y); + if (deltaY < smallestDeltaY) { + smallestDeltaY = deltaY; + replacementIndex = j; + } + } + distance = abs(x - mLastTouch.pointers[replacementIndex].x); + } else { + // Y looks too close. Find an older replacement point with a close X. + int32_t smallestDeltaX = INT_MAX; + for (uint32_t j = 0; j < pointerCount; j++) { + int32_t deltaX = abs(x - mLastTouch.pointers[j].x); + if (deltaX < smallestDeltaX) { + smallestDeltaX = deltaX; + replacementIndex = j; + } + } + distance = abs(y - mLastTouch.pointers[replacementIndex].y); + } + + // If replacing this pointer would correct a worse error than the previous ones + // considered, then use this replacement instead. + if (distance > badPointerDistance) { + badPointerIndex = i; + badPointerReplacementIndex = replacementIndex; + badPointerDistance = distance; + } + } + + // Correct the jumpy pointer if one was found. + if (badPointerIndex >= 0) { +#if DEBUG_HACKS + LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)", + badPointerIndex, + mLastTouch.pointers[badPointerReplacementIndex].x, + mLastTouch.pointers[badPointerReplacementIndex].y); +#endif + + mCurrentTouch.pointers[badPointerIndex].x = + mLastTouch.pointers[badPointerReplacementIndex].x; + mCurrentTouch.pointers[badPointerIndex].y = + mLastTouch.pointers[badPointerReplacementIndex].y; + mJumpyTouchFilter.jumpyPointsDropped += 1; + return true; + } + } + + mJumpyTouchFilter.jumpyPointsDropped = 0; + return false; +} + +/* Special hack for devices that have bad screen data: aggregate and + * compute averages of the coordinate data, to reduce the amount of + * jitter seen by applications. */ +void TouchInputMapper::applyAveragingTouchFilter() { + for (uint32_t currentIndex = 0; currentIndex < mCurrentTouch.pointerCount; currentIndex++) { + uint32_t id = mCurrentTouch.pointers[currentIndex].id; + int32_t x = mCurrentTouch.pointers[currentIndex].x; + int32_t y = mCurrentTouch.pointers[currentIndex].y; + int32_t pressure = mCurrentTouch.pointers[currentIndex].pressure; + + if (mLastTouch.idBits.hasBit(id)) { + // Pointer was down before and is still down now. + // Compute average over history trace. + uint32_t start = mAveragingTouchFilter.historyStart[id]; + uint32_t end = mAveragingTouchFilter.historyEnd[id]; + + int64_t deltaX = x - mAveragingTouchFilter.historyData[end].pointers[id].x; + int64_t deltaY = y - mAveragingTouchFilter.historyData[end].pointers[id].y; + uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); + +#if DEBUG_HACKS + LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld", + id, distance); +#endif + + if (distance < AVERAGING_DISTANCE_LIMIT) { + // Increment end index in preparation for recording new historical data. + end += 1; + if (end > AVERAGING_HISTORY_SIZE) { + end = 0; + } + + // If the end index has looped back to the start index then we have filled + // the historical trace up to the desired size so we drop the historical + // data at the start of the trace. + if (end == start) { + start += 1; + if (start > AVERAGING_HISTORY_SIZE) { + start = 0; + } + } + + // Add the raw data to the historical trace. + mAveragingTouchFilter.historyStart[id] = start; + mAveragingTouchFilter.historyEnd[id] = end; + mAveragingTouchFilter.historyData[end].pointers[id].x = x; + mAveragingTouchFilter.historyData[end].pointers[id].y = y; + mAveragingTouchFilter.historyData[end].pointers[id].pressure = pressure; + + // Average over all historical positions in the trace by total pressure. + int32_t averagedX = 0; + int32_t averagedY = 0; + int32_t totalPressure = 0; + for (;;) { + int32_t historicalX = mAveragingTouchFilter.historyData[start].pointers[id].x; + int32_t historicalY = mAveragingTouchFilter.historyData[start].pointers[id].y; + int32_t historicalPressure = mAveragingTouchFilter.historyData[start] + .pointers[id].pressure; + + averagedX += historicalX * historicalPressure; + averagedY += historicalY * historicalPressure; + totalPressure += historicalPressure; + + if (start == end) { + break; + } + + start += 1; + if (start > AVERAGING_HISTORY_SIZE) { + start = 0; + } + } + + averagedX /= totalPressure; + averagedY /= totalPressure; + +#if DEBUG_HACKS + LOGD("AveragingTouchFilter: Pointer id %d - " + "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure, + averagedX, averagedY); +#endif + + mCurrentTouch.pointers[currentIndex].x = averagedX; + mCurrentTouch.pointers[currentIndex].y = averagedY; + } else { +#if DEBUG_HACKS + LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id); +#endif + } + } else { +#if DEBUG_HACKS + LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id); +#endif + } + + // Reset pointer history. + mAveragingTouchFilter.historyStart[id] = 0; + mAveragingTouchFilter.historyEnd[id] = 0; + mAveragingTouchFilter.historyData[0].pointers[id].x = x; + mAveragingTouchFilter.historyData[0].pointers[id].y = y; + mAveragingTouchFilter.historyData[0].pointers[id].pressure = pressure; + } +} + +int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + { // acquire virtual key lock + AutoMutex _l(mVirtualKeyLock); + + if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) { + return AKEY_STATE_VIRTUAL; + } + + for (size_t i = 0; i < mVirtualKeys.size(); i++) { + const VirtualKey& virtualKey = mVirtualKeys[i]; + if (virtualKey.keyCode == keyCode) { + return AKEY_STATE_UP; + } + } + } // release virtual key lock + + return AKEY_STATE_UNKNOWN; +} + +int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + { // acquire virtual key lock + AutoMutex _l(mVirtualKeyLock); + + if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) { + return AKEY_STATE_VIRTUAL; + } + + for (size_t i = 0; i < mVirtualKeys.size(); i++) { + const VirtualKey& virtualKey = mVirtualKeys[i]; + if (virtualKey.scanCode == scanCode) { + return AKEY_STATE_UP; + } + } + } // release virtual key lock + + return AKEY_STATE_UNKNOWN; +} + +bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + { // acquire virtual key lock + AutoMutex _l(mVirtualKeyLock); + + for (size_t i = 0; i < mVirtualKeys.size(); i++) { + const VirtualKey& virtualKey = mVirtualKeys[i]; + + for (size_t i = 0; i < numCodes; i++) { + if (virtualKey.keyCode == keyCodes[i]) { + outFlags[i] = 1; + } + } + } + } // release virtual key lock + + return true; +} + + +// --- SingleTouchInputMapper --- + +SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : + TouchInputMapper(device, associatedDisplayId) { + initialize(); +} + +SingleTouchInputMapper::~SingleTouchInputMapper() { +} + +void SingleTouchInputMapper::initialize() { + mAccumulator.clear(); + + mDown = false; + mX = 0; + mY = 0; + mPressure = 0; + mSize = 0; +} + +void SingleTouchInputMapper::reset() { + TouchInputMapper::reset(); + + // Reinitialize. + initialize(); + } + +void SingleTouchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: + switch (rawEvent->scanCode) { + case BTN_TOUCH: + mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH; + mAccumulator.btnTouch = rawEvent->value != 0; + + sync(rawEvent->when); + mAccumulator.clear(); + break; + } + break; + + case EV_ABS: + switch (rawEvent->scanCode) { + case ABS_X: + mAccumulator.fields |= Accumulator::FIELD_ABS_X; + mAccumulator.absX = rawEvent->value; + break; + case ABS_Y: + mAccumulator.fields |= Accumulator::FIELD_ABS_Y; + mAccumulator.absY = rawEvent->value; + break; + case ABS_PRESSURE: + mAccumulator.fields |= Accumulator::FIELD_ABS_PRESSURE; + mAccumulator.absPressure = rawEvent->value; + break; + case ABS_TOOL_WIDTH: + mAccumulator.fields |= Accumulator::FIELD_ABS_TOOL_WIDTH; + mAccumulator.absToolWidth = rawEvent->value; + break; + } + break; + + case EV_SYN: + switch (rawEvent->scanCode) { + case SYN_REPORT: + if (mAccumulator.isDirty()) { + sync(rawEvent->when); + mAccumulator.clear(); + } + break; + } + break; + } +} + +void SingleTouchInputMapper::sync(nsecs_t when) { + /* Update device state */ + + uint32_t fields = mAccumulator.fields; + + if (fields & Accumulator::FIELD_BTN_TOUCH) { + mDown = mAccumulator.btnTouch; + } + + if (fields & Accumulator::FIELD_ABS_X) { + mX = mAccumulator.absX; + } + + if (fields & Accumulator::FIELD_ABS_Y) { + mY = mAccumulator.absY; + } + + if (fields & Accumulator::FIELD_ABS_PRESSURE) { + mPressure = mAccumulator.absPressure; + } + + if (fields & Accumulator::FIELD_ABS_TOOL_WIDTH) { + mSize = mAccumulator.absToolWidth; + } + + mCurrentTouch.clear(); + + if (mDown) { + mCurrentTouch.pointerCount = 1; + mCurrentTouch.pointers[0].id = 0; + mCurrentTouch.pointers[0].x = mX; + mCurrentTouch.pointers[0].y = mY; + mCurrentTouch.pointers[0].pressure = mPressure; + mCurrentTouch.pointers[0].size = mSize; + mCurrentTouch.pointers[0].touchMajor = mPressure; + mCurrentTouch.pointers[0].touchMinor = mPressure; + mCurrentTouch.pointers[0].toolMajor = mSize; + mCurrentTouch.pointers[0].toolMinor = mSize; + mCurrentTouch.pointers[0].orientation = 0; + mCurrentTouch.idToIndex[0] = 0; + mCurrentTouch.idBits.markBit(0); + } + + syncTouch(when, true); +} + +void SingleTouchInputMapper::configureAxes() { + TouchInputMapper::configureAxes(); + + // The axes are aliased to take into account the manner in which they are presented + // as part of the TouchData during the sync. + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mAxes.x); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mAxes.y); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mAxes.pressure); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mAxes.size); + + mAxes.touchMajor = mAxes.pressure; + mAxes.touchMinor = mAxes.pressure; + mAxes.toolMajor = mAxes.size; + mAxes.toolMinor = mAxes.size; +} + + +// --- MultiTouchInputMapper --- + +MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : + TouchInputMapper(device, associatedDisplayId) { + initialize(); +} + +MultiTouchInputMapper::~MultiTouchInputMapper() { +} + +void MultiTouchInputMapper::initialize() { + mAccumulator.clear(); +} + +void MultiTouchInputMapper::reset() { + TouchInputMapper::reset(); + + // Reinitialize. + initialize(); +} + +void MultiTouchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_ABS: { + uint32_t pointerIndex = mAccumulator.pointerCount; + Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex]; + + switch (rawEvent->scanCode) { + case ABS_MT_POSITION_X: + pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_X; + pointer->absMTPositionX = rawEvent->value; + break; + case ABS_MT_POSITION_Y: + pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y; + pointer->absMTPositionY = rawEvent->value; + break; + case ABS_MT_TOUCH_MAJOR: + pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR; + pointer->absMTTouchMajor = rawEvent->value; + break; + case ABS_MT_TOUCH_MINOR: + pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR; + pointer->absMTTouchMinor = rawEvent->value; + break; + case ABS_MT_WIDTH_MAJOR: + pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; + pointer->absMTWidthMajor = rawEvent->value; + break; + case ABS_MT_WIDTH_MINOR: + pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR; + pointer->absMTWidthMinor = rawEvent->value; + break; + case ABS_MT_ORIENTATION: + pointer->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION; + pointer->absMTOrientation = rawEvent->value; + break; + case ABS_MT_TRACKING_ID: + pointer->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID; + pointer->absMTTrackingId = rawEvent->value; + break; + } + break; + } + + case EV_SYN: + switch (rawEvent->scanCode) { + case SYN_MT_REPORT: { + // MultiTouch Sync: The driver has returned all data for *one* of the pointers. + uint32_t pointerIndex = mAccumulator.pointerCount; + + if (mAccumulator.pointers[pointerIndex].fields) { + if (pointerIndex == MAX_POINTERS) { + LOGW("MultiTouch device driver returned more than maximum of %d pointers.", + MAX_POINTERS); + } else { + pointerIndex += 1; + mAccumulator.pointerCount = pointerIndex; + } + } + + mAccumulator.pointers[pointerIndex].clear(); + break; + } + + case SYN_REPORT: + if (mAccumulator.isDirty()) { + sync(rawEvent->when); + mAccumulator.clear(); + } + break; + } + break; + } +} + +void MultiTouchInputMapper::sync(nsecs_t when) { + static const uint32_t REQUIRED_FIELDS = + Accumulator::FIELD_ABS_MT_POSITION_X + | Accumulator::FIELD_ABS_MT_POSITION_Y + | Accumulator::FIELD_ABS_MT_TOUCH_MAJOR + | Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; + + /* Update device state */ + + uint32_t inCount = mAccumulator.pointerCount; + uint32_t outCount = 0; + bool havePointerIds = true; + + mCurrentTouch.clear(); + + for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) { + uint32_t fields = mAccumulator.pointers[inIndex].fields; + + if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) { +#if DEBUG_POINTERS + LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d", + inIndex, fields); + continue; +#endif + } + + if (mAccumulator.pointers[inIndex].absMTTouchMajor <= 0) { + // Pointer is not down. Drop it. + continue; + } + + mCurrentTouch.pointers[outCount].x = mAccumulator.pointers[inIndex].absMTPositionX; + mCurrentTouch.pointers[outCount].y = mAccumulator.pointers[inIndex].absMTPositionY; + + mCurrentTouch.pointers[outCount].touchMajor = + mAccumulator.pointers[inIndex].absMTTouchMajor; + mCurrentTouch.pointers[outCount].touchMinor = + (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0 + ? mAccumulator.pointers[inIndex].absMTTouchMinor + : mAccumulator.pointers[inIndex].absMTTouchMajor; + + mCurrentTouch.pointers[outCount].toolMajor = + mAccumulator.pointers[inIndex].absMTWidthMajor; + mCurrentTouch.pointers[outCount].toolMinor = + (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0 + ? mAccumulator.pointers[inIndex].absMTWidthMinor + : mAccumulator.pointers[inIndex].absMTWidthMajor; + + mCurrentTouch.pointers[outCount].orientation = + (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) != 0 + ? mAccumulator.pointers[inIndex].absMTOrientation : 0; + + // Derive an approximation of pressure and size. + // FIXME assignment of pressure may be incorrect, probably better to let + // pressure = touch / width. Later on we pass width to MotionEvent as a size, which + // isn't quite right either. Should be using touch for that. + mCurrentTouch.pointers[outCount].pressure = mAccumulator.pointers[inIndex].absMTTouchMajor; + mCurrentTouch.pointers[outCount].size = mAccumulator.pointers[inIndex].absMTWidthMajor; + + if (havePointerIds) { + if (fields & Accumulator:: + FIELD_ABS_MT_TRACKING_ID) { + uint32_t id = uint32_t(mAccumulator.pointers[inIndex].absMTTrackingId); + + if (id > MAX_POINTER_ID) { +#if DEBUG_POINTERS + LOGD("Pointers: Ignoring driver provided pointer id %d because " + "it is larger than max supported id %d for optimizations", + id, MAX_POINTER_ID); +#endif + havePointerIds = false; + } + else { + mCurrentTouch.pointers[outCount].id = id; + mCurrentTouch.idToIndex[id] = outCount; + mCurrentTouch.idBits.markBit(id); + } + } else { + havePointerIds = false; + } + } + + outCount += 1; + } + + mCurrentTouch.pointerCount = outCount; + + syncTouch(when, havePointerIds); +} + +void MultiTouchInputMapper::configureAxes() { + TouchInputMapper::configureAxes(); + + // The axes are aliased to take into account the manner in which they are presented + // as part of the TouchData during the sync. + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mAxes.x); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mAxes.y); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mAxes.touchMajor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mAxes.touchMinor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mAxes.toolMajor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mAxes.toolMinor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mAxes.orientation); + + if (! mAxes.touchMinor.valid) { + mAxes.touchMinor = mAxes.touchMajor; + } + + if (! mAxes.toolMinor.valid) { + mAxes.toolMinor = mAxes.toolMajor; + } + + mAxes.pressure = mAxes.touchMajor; + mAxes.size = mAxes.toolMajor; +} + + } // namespace android