Add initial gamepad support.
Change-Id: I0439648f6eb5405f200e4223c915eb3a418b32b9
This commit is contained in:
165
api/current.xml
165
api/current.xml
@ -174296,6 +174296,171 @@
|
|||||||
visibility="public"
|
visibility="public"
|
||||||
>
|
>
|
||||||
</field>
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_A"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="96"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_B"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="97"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_C"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="98"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_L1"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="102"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_L2"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="104"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_MODE"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="110"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_R1"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="103"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_R2"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="105"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_SELECT"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="109"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_START"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="108"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_THUMBL"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="106"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_THUMBR"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="107"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_X"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="99"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_Y"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="100"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="KEYCODE_BUTTON_Z"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="101"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
<field name="KEYCODE_C"
|
<field name="KEYCODE_C"
|
||||||
type="int"
|
type="int"
|
||||||
transient="false"
|
transient="false"
|
||||||
|
@ -124,11 +124,27 @@ public class KeyEvent implements Parcelable {
|
|||||||
public static final int KEYCODE_PAGE_DOWN = 93;
|
public static final int KEYCODE_PAGE_DOWN = 93;
|
||||||
public static final int KEYCODE_PICTSYMBOLS = 94; // switch symbol-sets (Emoji,Kao-moji)
|
public static final int KEYCODE_PICTSYMBOLS = 94; // switch symbol-sets (Emoji,Kao-moji)
|
||||||
public static final int KEYCODE_SWITCH_CHARSET = 95; // switch char-sets (Kanji,Katakana)
|
public static final int KEYCODE_SWITCH_CHARSET = 95; // switch char-sets (Kanji,Katakana)
|
||||||
|
public static final int KEYCODE_BUTTON_A = 96;
|
||||||
|
public static final int KEYCODE_BUTTON_B = 97;
|
||||||
|
public static final int KEYCODE_BUTTON_C = 98;
|
||||||
|
public static final int KEYCODE_BUTTON_X = 99;
|
||||||
|
public static final int KEYCODE_BUTTON_Y = 100;
|
||||||
|
public static final int KEYCODE_BUTTON_Z = 101;
|
||||||
|
public static final int KEYCODE_BUTTON_L1 = 102;
|
||||||
|
public static final int KEYCODE_BUTTON_R1 = 103;
|
||||||
|
public static final int KEYCODE_BUTTON_L2 = 104;
|
||||||
|
public static final int KEYCODE_BUTTON_R2 = 105;
|
||||||
|
public static final int KEYCODE_BUTTON_THUMBL = 106;
|
||||||
|
public static final int KEYCODE_BUTTON_THUMBR = 107;
|
||||||
|
public static final int KEYCODE_BUTTON_START = 108;
|
||||||
|
public static final int KEYCODE_BUTTON_SELECT = 109;
|
||||||
|
public static final int KEYCODE_BUTTON_MODE = 110;
|
||||||
|
|
||||||
// NOTE: If you add a new keycode here you must also add it to:
|
// NOTE: If you add a new keycode here you must also add it to:
|
||||||
// isSystem()
|
// isSystem()
|
||||||
// native/include/android/keycodes.h
|
// native/include/android/keycodes.h
|
||||||
// frameworks/base/include/ui/KeycodeLabels.h
|
// frameworks/base/include/ui/KeycodeLabels.h
|
||||||
|
// external/webkit/WebKit/android/plugins/ANPKeyCodes.h
|
||||||
// tools/puppet_master/PuppetMaster/nav_keys.py
|
// tools/puppet_master/PuppetMaster/nav_keys.py
|
||||||
// frameworks/base/core/res/res/values/attrs.xml
|
// frameworks/base/core/res/res/values/attrs.xml
|
||||||
// commands/monkey/Monkey.java
|
// commands/monkey/Monkey.java
|
||||||
|
@ -920,6 +920,21 @@
|
|||||||
<enum name="KEYCODE_PAGE_DOWN" value="93" />
|
<enum name="KEYCODE_PAGE_DOWN" value="93" />
|
||||||
<enum name="KEYCODE_PICTSYMBOLS" value="94" />
|
<enum name="KEYCODE_PICTSYMBOLS" value="94" />
|
||||||
<enum name="KEYCODE_SWITCH_CHARSET" value="95" />
|
<enum name="KEYCODE_SWITCH_CHARSET" value="95" />
|
||||||
|
<enum name="KEYCODE_BUTTON_A" value="96" />
|
||||||
|
<enum name="KEYCODE_BUTTON_B" value="97" />
|
||||||
|
<enum name="KEYCODE_BUTTON_C" value="98" />
|
||||||
|
<enum name="KEYCODE_BUTTON_X" value="99" />
|
||||||
|
<enum name="KEYCODE_BUTTON_Y" value="100" />
|
||||||
|
<enum name="KEYCODE_BUTTON_Z" value="101" />
|
||||||
|
<enum name="KEYCODE_BUTTON_L1" value="102" />
|
||||||
|
<enum name="KEYCODE_BUTTON_R1" value="103" />
|
||||||
|
<enum name="KEYCODE_BUTTON_L2" value="104" />
|
||||||
|
<enum name="KEYCODE_BUTTON_R2" value="105" />
|
||||||
|
<enum name="KEYCODE_BUTTON_THUMBL" value="106" />
|
||||||
|
<enum name="KEYCODE_BUTTON_THUMBR" value="107" />
|
||||||
|
<enum name="KEYCODE_BUTTON_START" value="108" />
|
||||||
|
<enum name="KEYCODE_BUTTON_SELECT" value="109" />
|
||||||
|
<enum name="KEYCODE_BUTTON_MODE" value="110" />
|
||||||
</attr>
|
</attr>
|
||||||
|
|
||||||
<!-- ***************************************************************** -->
|
<!-- ***************************************************************** -->
|
||||||
|
338
include/ui/InputDevice.h
Normal file
338
include/ui/InputDevice.h
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
/*
|
||||||
|
* 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 <ui/EventHub.h>
|
||||||
|
#include <ui/Input.h>
|
||||||
|
#include <utils/KeyedVector.h>
|
||||||
|
#include <utils/threads.h>
|
||||||
|
#include <utils/Timers.h>
|
||||||
|
#include <utils/RefBase.h>
|
||||||
|
#include <utils/String8.h>
|
||||||
|
#include <utils/BitSet.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/* 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_WIDTH_MAJOR = 8,
|
||||||
|
FIELD_ABS_MT_TRACKING_ID = 16
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t pointerCount;
|
||||||
|
struct Pointer {
|
||||||
|
uint32_t fields;
|
||||||
|
|
||||||
|
int32_t absMTPositionX;
|
||||||
|
int32_t absMTPositionY;
|
||||||
|
int32_t absMTTouchMajor;
|
||||||
|
int32_t absMTWidthMajor;
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
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<VirtualKey> virtualKeys;
|
||||||
|
|
||||||
|
struct Parameters {
|
||||||
|
bool useBadTouchFilter;
|
||||||
|
bool useJumpyTouchFilter;
|
||||||
|
bool useAveragingTouchFilter;
|
||||||
|
|
||||||
|
AbsoluteAxisInfo xAxis;
|
||||||
|
AbsoluteAxisInfo yAxis;
|
||||||
|
AbsoluteAxisInfo pressureAxis;
|
||||||
|
AbsoluteAxisInfo sizeAxis;
|
||||||
|
} 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;
|
||||||
|
} 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
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <ui/EventHub.h>
|
#include <ui/EventHub.h>
|
||||||
#include <ui/Input.h>
|
#include <ui/Input.h>
|
||||||
|
#include <ui/InputDevice.h>
|
||||||
#include <ui/InputDispatcher.h>
|
#include <ui/InputDispatcher.h>
|
||||||
#include <utils/KeyedVector.h>
|
#include <utils/KeyedVector.h>
|
||||||
#include <utils/threads.h>
|
#include <utils/threads.h>
|
||||||
@ -30,311 +31,8 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
/* Maximum pointer id value supported.
|
|
||||||
* (This is limited by our use of BitSet32 to track pointer assignments.) */
|
|
||||||
#define MAX_POINTER_ID 32
|
|
||||||
|
|
||||||
/* Maximum number of historical samples to average. */
|
|
||||||
#define AVERAGING_HISTORY_SIZE 5
|
|
||||||
|
|
||||||
|
|
||||||
namespace android {
|
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_WIDTH_MAJOR = 8,
|
|
||||||
FIELD_ABS_MT_TRACKING_ID = 16
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t pointerCount;
|
|
||||||
struct Pointer {
|
|
||||||
uint32_t fields;
|
|
||||||
|
|
||||||
int32_t absMTPositionX;
|
|
||||||
int32_t absMTPositionY;
|
|
||||||
int32_t absMTTouchMajor;
|
|
||||||
int32_t absMTWidthMajor;
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TouchData {
|
|
||||||
uint32_t pointerCount;
|
|
||||||
PointerData pointers[MAX_POINTERS];
|
|
||||||
BitSet32 idBits;
|
|
||||||
uint32_t idToIndex[MAX_POINTER_ID];
|
|
||||||
|
|
||||||
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<VirtualKey> virtualKeys;
|
|
||||||
|
|
||||||
struct Parameters {
|
|
||||||
bool useBadTouchFilter;
|
|
||||||
bool useJumpyTouchFilter;
|
|
||||||
bool useAveragingTouchFilter;
|
|
||||||
|
|
||||||
AbsoluteAxisInfo xAxis;
|
|
||||||
AbsoluteAxisInfo yAxis;
|
|
||||||
AbsoluteAxisInfo pressureAxis;
|
|
||||||
AbsoluteAxisInfo sizeAxis;
|
|
||||||
} 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;
|
|
||||||
} 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); }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Input reader policy interface.
|
* Input reader policy interface.
|
||||||
*
|
*
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
#ifndef _UI_KEYCODE_LABELS_H
|
#ifndef _UI_KEYCODE_LABELS_H
|
||||||
#define _UI_KEYCODE_LABELS_H
|
#define _UI_KEYCODE_LABELS_H
|
||||||
|
|
||||||
|
#include <android/keycodes.h>
|
||||||
|
|
||||||
struct KeycodeLabel {
|
struct KeycodeLabel {
|
||||||
const char *literal;
|
const char *literal;
|
||||||
int value;
|
int value;
|
||||||
@ -118,117 +120,28 @@ static const KeycodeLabel KEYCODES[] = {
|
|||||||
{ "PAGE_DOWN", 93 },
|
{ "PAGE_DOWN", 93 },
|
||||||
{ "PICTSYMBOLS", 94 },
|
{ "PICTSYMBOLS", 94 },
|
||||||
{ "SWITCH_CHARSET", 95 },
|
{ "SWITCH_CHARSET", 95 },
|
||||||
|
{ "BUTTON_A", 96 },
|
||||||
|
{ "BUTTON_B", 97 },
|
||||||
|
{ "BUTTON_C", 98 },
|
||||||
|
{ "BUTTON_X", 99 },
|
||||||
|
{ "BUTTON_Y", 100 },
|
||||||
|
{ "BUTTON_Z", 101 },
|
||||||
|
{ "BUTTON_L1", 102 },
|
||||||
|
{ "BUTTON_R1", 103 },
|
||||||
|
{ "BUTTON_L2", 104 },
|
||||||
|
{ "BUTTON_R2", 105 },
|
||||||
|
{ "BUTTON_THUMBL", 106 },
|
||||||
|
{ "BUTTON_THUMBR", 107 },
|
||||||
|
{ "BUTTON_START", 108 },
|
||||||
|
{ "BUTTON_SELECT", 109 },
|
||||||
|
{ "BUTTON_MODE", 110 },
|
||||||
|
|
||||||
// NOTE: If you add a new keycode here you must also add it to:
|
// NOTE: If you add a new keycode here you must also add it to several other files.
|
||||||
// (enum KeyCode, in this file)
|
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
|
||||||
// frameworks/base/core/java/android/view/KeyEvent.java
|
|
||||||
// tools/puppet_master/PuppetMaster.nav_keys.py
|
|
||||||
// frameworks/base/core/res/res/values/attrs.xml
|
|
||||||
|
|
||||||
{ NULL, 0 }
|
{ NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
// These constants need to match the above mappings.
|
|
||||||
typedef enum KeyCode {
|
|
||||||
kKeyCodeUnknown = 0,
|
|
||||||
|
|
||||||
kKeyCodeSoftLeft = 1,
|
|
||||||
kKeyCodeSoftRight = 2,
|
|
||||||
kKeyCodeHome = 3,
|
|
||||||
kKeyCodeBack = 4,
|
|
||||||
kKeyCodeCall = 5,
|
|
||||||
kKeyCodeEndCall = 6,
|
|
||||||
kKeyCode0 = 7,
|
|
||||||
kKeyCode1 = 8,
|
|
||||||
kKeyCode2 = 9,
|
|
||||||
kKeyCode3 = 10,
|
|
||||||
kKeyCode4 = 11,
|
|
||||||
kKeyCode5 = 12,
|
|
||||||
kKeyCode6 = 13,
|
|
||||||
kKeyCode7 = 14,
|
|
||||||
kKeyCode8 = 15,
|
|
||||||
kKeyCode9 = 16,
|
|
||||||
kKeyCodeStar = 17,
|
|
||||||
kKeyCodePound = 18,
|
|
||||||
kKeyCodeDpadUp = 19,
|
|
||||||
kKeyCodeDpadDown = 20,
|
|
||||||
kKeyCodeDpadLeft = 21,
|
|
||||||
kKeyCodeDpadRight = 22,
|
|
||||||
kKeyCodeDpadCenter = 23,
|
|
||||||
kKeyCodeVolumeUp = 24,
|
|
||||||
kKeyCodeVolumeDown = 25,
|
|
||||||
kKeyCodePower = 26,
|
|
||||||
kKeyCodeCamera = 27,
|
|
||||||
kKeyCodeClear = 28,
|
|
||||||
kKeyCodeA = 29,
|
|
||||||
kKeyCodeB = 30,
|
|
||||||
kKeyCodeC = 31,
|
|
||||||
kKeyCodeD = 32,
|
|
||||||
kKeyCodeE = 33,
|
|
||||||
kKeyCodeF = 34,
|
|
||||||
kKeyCodeG = 35,
|
|
||||||
kKeyCodeH = 36,
|
|
||||||
kKeyCodeI = 37,
|
|
||||||
kKeyCodeJ = 38,
|
|
||||||
kKeyCodeK = 39,
|
|
||||||
kKeyCodeL = 40,
|
|
||||||
kKeyCodeM = 41,
|
|
||||||
kKeyCodeN = 42,
|
|
||||||
kKeyCodeO = 43,
|
|
||||||
kKeyCodeP = 44,
|
|
||||||
kKeyCodeQ = 45,
|
|
||||||
kKeyCodeR = 46,
|
|
||||||
kKeyCodeS = 47,
|
|
||||||
kKeyCodeT = 48,
|
|
||||||
kKeyCodeU = 49,
|
|
||||||
kKeyCodeV = 50,
|
|
||||||
kKeyCodeW = 51,
|
|
||||||
kKeyCodeX = 52,
|
|
||||||
kKeyCodeY = 53,
|
|
||||||
kKeyCodeZ = 54,
|
|
||||||
kKeyCodeComma = 55,
|
|
||||||
kKeyCodePeriod = 56,
|
|
||||||
kKeyCodeAltLeft = 57,
|
|
||||||
kKeyCodeAltRight = 58,
|
|
||||||
kKeyCodeShiftLeft = 59,
|
|
||||||
kKeyCodeShiftRight = 60,
|
|
||||||
kKeyCodeTab = 61,
|
|
||||||
kKeyCodeSpace = 62,
|
|
||||||
kKeyCodeSym = 63,
|
|
||||||
kKeyCodeExplorer = 64,
|
|
||||||
kKeyCodeEnvelope = 65,
|
|
||||||
kKeyCodeNewline = 66,
|
|
||||||
kKeyCodeDel = 67,
|
|
||||||
kKeyCodeGrave = 68,
|
|
||||||
kKeyCodeMinus = 69,
|
|
||||||
kKeyCodeEquals = 70,
|
|
||||||
kKeyCodeLeftBracket = 71,
|
|
||||||
kKeyCodeRightBracket = 72,
|
|
||||||
kKeyCodeBackslash = 73,
|
|
||||||
kKeyCodeSemicolon = 74,
|
|
||||||
kKeyCodeApostrophe = 75,
|
|
||||||
kKeyCodeSlash = 76,
|
|
||||||
kKeyCodeAt = 77,
|
|
||||||
kKeyCodeNum = 78,
|
|
||||||
kKeyCodeHeadSetHook = 79,
|
|
||||||
kKeyCodeFocus = 80,
|
|
||||||
kKeyCodePlus = 81,
|
|
||||||
kKeyCodeMenu = 82,
|
|
||||||
kKeyCodeNotification = 83,
|
|
||||||
kKeyCodeSearch = 84,
|
|
||||||
kKeyCodePlayPause = 85,
|
|
||||||
kKeyCodeStop = 86,
|
|
||||||
kKeyCodeNextSong = 87,
|
|
||||||
kKeyCodePreviousSong = 88,
|
|
||||||
kKeyCodeRewind = 89,
|
|
||||||
kKeyCodeForward = 90,
|
|
||||||
kKeyCodeMute = 91,
|
|
||||||
kKeyCodePageUp = 92,
|
|
||||||
kKeyCodePageDown = 93,
|
|
||||||
kKeyCodePictSymbols = 94,
|
|
||||||
kKeyCodeSwitchCharset = 95
|
|
||||||
} KeyCode;
|
|
||||||
|
|
||||||
static const KeycodeLabel FLAGS[] = {
|
static const KeycodeLabel FLAGS[] = {
|
||||||
{ "WAKE", 0x00000001 },
|
{ "WAKE", 0x00000001 },
|
||||||
{ "WAKE_DROPPED", 0x00000002 },
|
{ "WAKE_DROPPED", 0x00000002 },
|
||||||
|
@ -12,6 +12,7 @@ LOCAL_SRC_FILES:= \
|
|||||||
KeyLayoutMap.cpp \
|
KeyLayoutMap.cpp \
|
||||||
KeyCharacterMap.cpp \
|
KeyCharacterMap.cpp \
|
||||||
Input.cpp \
|
Input.cpp \
|
||||||
|
InputDevice.cpp \
|
||||||
InputDispatcher.cpp \
|
InputDispatcher.cpp \
|
||||||
InputManager.cpp \
|
InputManager.cpp \
|
||||||
InputReader.cpp \
|
InputReader.cpp \
|
||||||
@ -38,3 +39,13 @@ ifeq ($(TARGET_SIMULATOR),true)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
|
||||||
|
|
||||||
|
# Include subdirectory makefiles
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
|
||||||
|
# team really wants is to build the stuff defined by this makefile.
|
||||||
|
ifeq (,$(ONE_SHOT_MAKEFILE))
|
||||||
|
include $(call first-makefiles-under,$(LOCAL_PATH))
|
||||||
|
endif
|
||||||
|
@ -54,6 +54,9 @@
|
|||||||
*/
|
*/
|
||||||
#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
|
#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
|
||||||
|
|
||||||
|
/* this macro computes the number of bytes needed to represent a bit array of the specified size */
|
||||||
|
#define sizeof_bit_array(bits) ((bits + 7) / 8)
|
||||||
|
|
||||||
#define ID_MASK 0x0000ffff
|
#define ID_MASK 0x0000ffff
|
||||||
#define SEQ_MASK 0x7fff0000
|
#define SEQ_MASK 0x7fff0000
|
||||||
#define SEQ_SHIFT 16
|
#define SEQ_SHIFT 16
|
||||||
@ -182,7 +185,7 @@ int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t deviceClasses,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
|
int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
|
||||||
uint8_t key_bitmask[(KEY_MAX + 7) / 8];
|
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
|
||||||
memset(key_bitmask, 0, sizeof(key_bitmask));
|
memset(key_bitmask, 0, sizeof(key_bitmask));
|
||||||
if (ioctl(mFDs[id_to_index(device->id)].fd,
|
if (ioctl(mFDs[id_to_index(device->id)].fd,
|
||||||
EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
|
EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
|
||||||
@ -218,7 +221,7 @@ int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const
|
|||||||
Vector<int32_t> scanCodes;
|
Vector<int32_t> scanCodes;
|
||||||
device->layoutMap->findScancodes(keyCode, &scanCodes);
|
device->layoutMap->findScancodes(keyCode, &scanCodes);
|
||||||
|
|
||||||
uint8_t key_bitmask[(KEY_MAX + 7) / 8];
|
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
|
||||||
memset(key_bitmask, 0, sizeof(key_bitmask));
|
memset(key_bitmask, 0, sizeof(key_bitmask));
|
||||||
if (ioctl(mFDs[id_to_index(device->id)].fd,
|
if (ioctl(mFDs[id_to_index(device->id)].fd,
|
||||||
EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
|
EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
|
||||||
@ -264,7 +267,7 @@ int32_t EventHub::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_
|
|||||||
}
|
}
|
||||||
|
|
||||||
int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
|
int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
|
||||||
uint8_t sw_bitmask[(SW_MAX + 7) / 8];
|
uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
|
||||||
memset(sw_bitmask, 0, sizeof(sw_bitmask));
|
memset(sw_bitmask, 0, sizeof(sw_bitmask));
|
||||||
if (ioctl(mFDs[id_to_index(device->id)].fd,
|
if (ioctl(mFDs[id_to_index(device->id)].fd,
|
||||||
EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
|
EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
|
||||||
@ -409,7 +412,7 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
|
|||||||
LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n",
|
LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n",
|
||||||
iev.code, *outKeycode, *outFlags, err);
|
iev.code, *outKeycode, *outFlags, err);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
*outKeycode = 0;
|
*outKeycode = AKEYCODE_UNKNOWN;
|
||||||
*outFlags = 0;
|
*outFlags = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -509,6 +512,26 @@ bool EventHub::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFla
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) {
|
||||||
|
const uint8_t* end = array + endIndex;
|
||||||
|
array += startIndex;
|
||||||
|
while (array != end) {
|
||||||
|
if (*(array++) != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int32_t GAMEPAD_KEYCODES[] = {
|
||||||
|
AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C,
|
||||||
|
AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z,
|
||||||
|
AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1,
|
||||||
|
AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2,
|
||||||
|
AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,
|
||||||
|
AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE
|
||||||
|
};
|
||||||
|
|
||||||
int EventHub::open_device(const char *deviceName)
|
int EventHub::open_device(const char *deviceName)
|
||||||
{
|
{
|
||||||
int version;
|
int version;
|
||||||
@ -626,27 +649,27 @@ int EventHub::open_device(const char *deviceName)
|
|||||||
mFDs[mFDCount].fd = fd;
|
mFDs[mFDCount].fd = fd;
|
||||||
mFDs[mFDCount].events = POLLIN;
|
mFDs[mFDCount].events = POLLIN;
|
||||||
|
|
||||||
// figure out the kinds of events the device reports
|
// Figure out the kinds of events the device reports.
|
||||||
|
|
||||||
// See if this is a keyboard, and classify it. Note that we only
|
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
|
||||||
// consider up through the function keys; we don't want to include
|
|
||||||
// ones after that (play cd etc) so we don't mistakenly consider a
|
|
||||||
// controller to be a keyboard.
|
|
||||||
uint8_t key_bitmask[(KEY_MAX+7)/8];
|
|
||||||
memset(key_bitmask, 0, sizeof(key_bitmask));
|
memset(key_bitmask, 0, sizeof(key_bitmask));
|
||||||
|
|
||||||
LOGV("Getting keys...");
|
LOGV("Getting keys...");
|
||||||
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
|
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
|
||||||
//LOGI("MAP\n");
|
//LOGI("MAP\n");
|
||||||
//for (int i=0; i<((KEY_MAX+7)/8); i++) {
|
//for (int i = 0; i < sizeof(key_bitmask); i++) {
|
||||||
// LOGI("%d: 0x%02x\n", i, key_bitmask[i]);
|
// LOGI("%d: 0x%02x\n", i, key_bitmask[i]);
|
||||||
//}
|
//}
|
||||||
for (int i=0; i<((BTN_MISC+7)/8); i++) {
|
|
||||||
if (key_bitmask[i] != 0) {
|
// See if this is a keyboard. Ignore everything in the button range except for
|
||||||
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
|
// gamepads which are also considered keyboards.
|
||||||
break;
|
if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))
|
||||||
}
|
|| containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD),
|
||||||
}
|
sizeof_bit_array(BTN_DIGI))
|
||||||
if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
|
|| containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),
|
||||||
|
sizeof_bit_array(KEY_MAX + 1))) {
|
||||||
|
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
|
||||||
|
|
||||||
device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
|
device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
|
||||||
if (device->keyBitmask != NULL) {
|
if (device->keyBitmask != NULL) {
|
||||||
memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
|
memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
|
||||||
@ -658,39 +681,39 @@ int EventHub::open_device(const char *deviceName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if this is a trackball.
|
// See if this is a trackball (or mouse).
|
||||||
if (test_bit(BTN_MOUSE, key_bitmask)) {
|
if (test_bit(BTN_MOUSE, key_bitmask)) {
|
||||||
uint8_t rel_bitmask[(REL_MAX+7)/8];
|
uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)];
|
||||||
memset(rel_bitmask, 0, sizeof(rel_bitmask));
|
memset(rel_bitmask, 0, sizeof(rel_bitmask));
|
||||||
LOGV("Getting relative controllers...");
|
LOGV("Getting relative controllers...");
|
||||||
if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0)
|
if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) {
|
||||||
{
|
|
||||||
if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) {
|
if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) {
|
||||||
device->classes |= INPUT_DEVICE_CLASS_TRACKBALL;
|
device->classes |= INPUT_DEVICE_CLASS_TRACKBALL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t abs_bitmask[(ABS_MAX+7)/8];
|
// See if this is a touch pad.
|
||||||
|
uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)];
|
||||||
memset(abs_bitmask, 0, sizeof(abs_bitmask));
|
memset(abs_bitmask, 0, sizeof(abs_bitmask));
|
||||||
LOGV("Getting absolute controllers...");
|
LOGV("Getting absolute controllers...");
|
||||||
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask);
|
if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) {
|
||||||
|
// Is this a new modern multi-touch driver?
|
||||||
|
if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask)
|
||||||
|
&& test_bit(ABS_MT_POSITION_X, abs_bitmask)
|
||||||
|
&& test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
|
||||||
|
device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT;
|
||||||
|
|
||||||
// Is this a new modern multi-touch driver?
|
// Is this an old style single-touch driver?
|
||||||
if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask)
|
} else if (test_bit(BTN_TOUCH, key_bitmask)
|
||||||
&& test_bit(ABS_MT_POSITION_X, abs_bitmask)
|
&& test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
|
||||||
&& test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
|
device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN;
|
||||||
device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT;
|
}
|
||||||
|
|
||||||
// Is this an old style single-touch driver?
|
|
||||||
} else if (test_bit(BTN_TOUCH, key_bitmask)
|
|
||||||
&& test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
|
|
||||||
device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef EV_SW
|
#ifdef EV_SW
|
||||||
// figure out the switches this device reports
|
// figure out the switches this device reports
|
||||||
uint8_t sw_bitmask[(SW_MAX+7)/8];
|
uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
|
||||||
memset(sw_bitmask, 0, sizeof(sw_bitmask));
|
memset(sw_bitmask, 0, sizeof(sw_bitmask));
|
||||||
if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) {
|
if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) {
|
||||||
for (int i=0; i<EV_SW; i++) {
|
for (int i=0; i<EV_SW; i++) {
|
||||||
@ -726,7 +749,10 @@ int EventHub::open_device(const char *deviceName)
|
|||||||
"%s/usr/keylayout/%s", root, "qwerty.kl");
|
"%s/usr/keylayout/%s", root, "qwerty.kl");
|
||||||
defaultKeymap = true;
|
defaultKeymap = true;
|
||||||
}
|
}
|
||||||
device->layoutMap->load(keylayoutFilename);
|
status_t status = device->layoutMap->load(keylayoutFilename);
|
||||||
|
if (status) {
|
||||||
|
LOGE("Error %d loading key layout.", status);
|
||||||
|
}
|
||||||
|
|
||||||
// tell the world about the devname (the descriptive name)
|
// tell the world about the devname (the descriptive name)
|
||||||
if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
|
if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
|
||||||
@ -746,19 +772,27 @@ int EventHub::open_device(const char *deviceName)
|
|||||||
property_set(propName, name);
|
property_set(propName, name);
|
||||||
|
|
||||||
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
|
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
|
||||||
if (hasKeycode(device, kKeyCodeQ)) {
|
if (hasKeycode(device, AKEYCODE_Q)) {
|
||||||
device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
|
device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if this has a DPAD.
|
// See if this device has a DPAD.
|
||||||
if (hasKeycode(device, kKeyCodeDpadUp) &&
|
if (hasKeycode(device, AKEYCODE_DPAD_UP) &&
|
||||||
hasKeycode(device, kKeyCodeDpadDown) &&
|
hasKeycode(device, AKEYCODE_DPAD_DOWN) &&
|
||||||
hasKeycode(device, kKeyCodeDpadLeft) &&
|
hasKeycode(device, AKEYCODE_DPAD_LEFT) &&
|
||||||
hasKeycode(device, kKeyCodeDpadRight) &&
|
hasKeycode(device, AKEYCODE_DPAD_RIGHT) &&
|
||||||
hasKeycode(device, kKeyCodeDpadCenter)) {
|
hasKeycode(device, AKEYCODE_DPAD_CENTER)) {
|
||||||
device->classes |= INPUT_DEVICE_CLASS_DPAD;
|
device->classes |= INPUT_DEVICE_CLASS_DPAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See if this device has a gamepad.
|
||||||
|
for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES); i++) {
|
||||||
|
if (hasKeycode(device, GAMEPAD_KEYCODES[i])) {
|
||||||
|
device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
|
LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
|
||||||
device->id, name, propName, keylayoutFilename);
|
device->id, name, propName, keylayoutFilename);
|
||||||
}
|
}
|
||||||
|
@ -22,26 +22,26 @@ void InputEvent::initialize(int32_t deviceId, int32_t nature) {
|
|||||||
|
|
||||||
bool KeyEvent::hasDefaultAction(int32_t keyCode) {
|
bool KeyEvent::hasDefaultAction(int32_t keyCode) {
|
||||||
switch (keyCode) {
|
switch (keyCode) {
|
||||||
case KEYCODE_HOME:
|
case AKEYCODE_HOME:
|
||||||
case KEYCODE_BACK:
|
case AKEYCODE_BACK:
|
||||||
case KEYCODE_CALL:
|
case AKEYCODE_CALL:
|
||||||
case KEYCODE_ENDCALL:
|
case AKEYCODE_ENDCALL:
|
||||||
case KEYCODE_VOLUME_UP:
|
case AKEYCODE_VOLUME_UP:
|
||||||
case KEYCODE_VOLUME_DOWN:
|
case AKEYCODE_VOLUME_DOWN:
|
||||||
case KEYCODE_POWER:
|
case AKEYCODE_POWER:
|
||||||
case KEYCODE_CAMERA:
|
case AKEYCODE_CAMERA:
|
||||||
case KEYCODE_HEADSETHOOK:
|
case AKEYCODE_HEADSETHOOK:
|
||||||
case KEYCODE_MENU:
|
case AKEYCODE_MENU:
|
||||||
case KEYCODE_NOTIFICATION:
|
case AKEYCODE_NOTIFICATION:
|
||||||
case KEYCODE_FOCUS:
|
case AKEYCODE_FOCUS:
|
||||||
case KEYCODE_SEARCH:
|
case AKEYCODE_SEARCH:
|
||||||
case KEYCODE_MEDIA_PLAY_PAUSE:
|
case AKEYCODE_MEDIA_PLAY_PAUSE:
|
||||||
case KEYCODE_MEDIA_STOP:
|
case AKEYCODE_MEDIA_STOP:
|
||||||
case KEYCODE_MEDIA_NEXT:
|
case AKEYCODE_MEDIA_NEXT:
|
||||||
case KEYCODE_MEDIA_PREVIOUS:
|
case AKEYCODE_MEDIA_PREVIOUS:
|
||||||
case KEYCODE_MEDIA_REWIND:
|
case AKEYCODE_MEDIA_REWIND:
|
||||||
case KEYCODE_MEDIA_FAST_FORWARD:
|
case AKEYCODE_MEDIA_FAST_FORWARD:
|
||||||
case KEYCODE_MUTE:
|
case AKEYCODE_MUTE:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,26 +54,26 @@ bool KeyEvent::hasDefaultAction() const {
|
|||||||
|
|
||||||
bool KeyEvent::isSystemKey(int32_t keyCode) {
|
bool KeyEvent::isSystemKey(int32_t keyCode) {
|
||||||
switch (keyCode) {
|
switch (keyCode) {
|
||||||
case KEYCODE_MENU:
|
case AKEYCODE_MENU:
|
||||||
case KEYCODE_SOFT_RIGHT:
|
case AKEYCODE_SOFT_RIGHT:
|
||||||
case KEYCODE_HOME:
|
case AKEYCODE_HOME:
|
||||||
case KEYCODE_BACK:
|
case AKEYCODE_BACK:
|
||||||
case KEYCODE_CALL:
|
case AKEYCODE_CALL:
|
||||||
case KEYCODE_ENDCALL:
|
case AKEYCODE_ENDCALL:
|
||||||
case KEYCODE_VOLUME_UP:
|
case AKEYCODE_VOLUME_UP:
|
||||||
case KEYCODE_VOLUME_DOWN:
|
case AKEYCODE_VOLUME_DOWN:
|
||||||
case KEYCODE_MUTE:
|
case AKEYCODE_MUTE:
|
||||||
case KEYCODE_POWER:
|
case AKEYCODE_POWER:
|
||||||
case KEYCODE_HEADSETHOOK:
|
case AKEYCODE_HEADSETHOOK:
|
||||||
case KEYCODE_MEDIA_PLAY_PAUSE:
|
case AKEYCODE_MEDIA_PLAY_PAUSE:
|
||||||
case KEYCODE_MEDIA_STOP:
|
case AKEYCODE_MEDIA_STOP:
|
||||||
case KEYCODE_MEDIA_NEXT:
|
case AKEYCODE_MEDIA_NEXT:
|
||||||
case KEYCODE_MEDIA_PREVIOUS:
|
case AKEYCODE_MEDIA_PREVIOUS:
|
||||||
case KEYCODE_MEDIA_REWIND:
|
case AKEYCODE_MEDIA_REWIND:
|
||||||
case KEYCODE_MEDIA_FAST_FORWARD:
|
case AKEYCODE_MEDIA_FAST_FORWARD:
|
||||||
case KEYCODE_CAMERA:
|
case AKEYCODE_CAMERA:
|
||||||
case KEYCODE_FOCUS:
|
case AKEYCODE_FOCUS:
|
||||||
case KEYCODE_SEARCH:
|
case AKEYCODE_SEARCH:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
729
libs/ui/InputDevice.cpp
Normal file
729
libs/ui/InputDevice.cpp
Normal file
@ -0,0 +1,729 @@
|
|||||||
|
//
|
||||||
|
// 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 <cutils/log.h>
|
||||||
|
#include <ui/InputDevice.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
/* 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<typename T>
|
||||||
|
inline static T abs(const T& value) {
|
||||||
|
return value < 0 ? - value : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline static T min(const T& a, const T& b) {
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline static void swap(T& a, T& b) {
|
||||||
|
T temp = a;
|
||||||
|
a = b;
|
||||||
|
b = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- 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 = META_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
|
@ -40,10 +40,10 @@ namespace android {
|
|||||||
|
|
||||||
// TODO, this needs to be somewhere else, perhaps in the policy
|
// TODO, this needs to be somewhere else, perhaps in the policy
|
||||||
static inline bool isMovementKey(int32_t keyCode) {
|
static inline bool isMovementKey(int32_t keyCode) {
|
||||||
return keyCode == KEYCODE_DPAD_UP
|
return keyCode == AKEYCODE_DPAD_UP
|
||||||
|| keyCode == KEYCODE_DPAD_DOWN
|
|| keyCode == AKEYCODE_DPAD_DOWN
|
||||||
|| keyCode == KEYCODE_DPAD_LEFT
|
|| keyCode == AKEYCODE_DPAD_LEFT
|
||||||
|| keyCode == KEYCODE_DPAD_RIGHT;
|
|| keyCode == AKEYCODE_DPAD_RIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline nsecs_t now() {
|
static inline nsecs_t now() {
|
||||||
|
@ -33,18 +33,6 @@
|
|||||||
/** Amount that trackball needs to move in order to generate a key event. */
|
/** Amount that trackball needs to move in order to generate a key event. */
|
||||||
#define TRACKBALL_MOVEMENT_THRESHOLD 6
|
#define TRACKBALL_MOVEMENT_THRESHOLD 6
|
||||||
|
|
||||||
/* 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 {
|
namespace android {
|
||||||
|
|
||||||
@ -71,19 +59,19 @@ inline static void swap(T& a, T& b) {
|
|||||||
int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
|
int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
|
||||||
int32_t mask;
|
int32_t mask;
|
||||||
switch (keyCode) {
|
switch (keyCode) {
|
||||||
case KEYCODE_ALT_LEFT:
|
case AKEYCODE_ALT_LEFT:
|
||||||
mask = META_ALT_LEFT_ON;
|
mask = META_ALT_LEFT_ON;
|
||||||
break;
|
break;
|
||||||
case KEYCODE_ALT_RIGHT:
|
case AKEYCODE_ALT_RIGHT:
|
||||||
mask = META_ALT_RIGHT_ON;
|
mask = META_ALT_RIGHT_ON;
|
||||||
break;
|
break;
|
||||||
case KEYCODE_SHIFT_LEFT:
|
case AKEYCODE_SHIFT_LEFT:
|
||||||
mask = META_SHIFT_LEFT_ON;
|
mask = META_SHIFT_LEFT_ON;
|
||||||
break;
|
break;
|
||||||
case KEYCODE_SHIFT_RIGHT:
|
case AKEYCODE_SHIFT_RIGHT:
|
||||||
mask = META_SHIFT_RIGHT_ON;
|
mask = META_SHIFT_RIGHT_ON;
|
||||||
break;
|
break;
|
||||||
case KEYCODE_SYM:
|
case AKEYCODE_SYM:
|
||||||
mask = META_SYM_ON;
|
mask = META_SYM_ON;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -107,10 +95,10 @@ int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
|
|||||||
static const int32_t keyCodeRotationMap[][4] = {
|
static const int32_t keyCodeRotationMap[][4] = {
|
||||||
// key codes enumerated counter-clockwise with the original (unrotated) key first
|
// key codes enumerated counter-clockwise with the original (unrotated) key first
|
||||||
// no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
|
// no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
|
||||||
{ KEYCODE_DPAD_DOWN, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_UP, KEYCODE_DPAD_LEFT },
|
{ AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT },
|
||||||
{ KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_UP, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_DOWN },
|
{ AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN },
|
||||||
{ KEYCODE_DPAD_UP, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_RIGHT },
|
{ AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT },
|
||||||
{ KEYCODE_DPAD_LEFT, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_UP },
|
{ AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP },
|
||||||
};
|
};
|
||||||
static const int keyCodeRotationMapSize =
|
static const int keyCodeRotationMapSize =
|
||||||
sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
|
sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
|
||||||
@ -127,668 +115,6 @@ int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- 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 = META_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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// --- InputReader ---
|
// --- InputReader ---
|
||||||
|
|
||||||
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
|
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
|
||||||
@ -927,32 +253,30 @@ void InputReader::handleKey(const RawEvent* rawEvent) {
|
|||||||
bool down = rawEvent->value != 0;
|
bool down = rawEvent->value != 0;
|
||||||
int32_t scanCode = rawEvent->scanCode;
|
int32_t scanCode = rawEvent->scanCode;
|
||||||
|
|
||||||
if (device->isKeyboard() && (scanCode < BTN_FIRST || scanCode > BTN_LAST)) {
|
if (device->isSingleTouchScreen()) {
|
||||||
int32_t keyCode = rawEvent->keyCode;
|
|
||||||
onKey(rawEvent->when, device, down, keyCode, scanCode, rawEvent->flags);
|
|
||||||
} else if (device->isSingleTouchScreen()) {
|
|
||||||
switch (rawEvent->scanCode) {
|
switch (rawEvent->scanCode) {
|
||||||
case BTN_TOUCH:
|
case BTN_TOUCH:
|
||||||
device->singleTouchScreen.accumulator.fields |=
|
device->singleTouchScreen.accumulator.fields |=
|
||||||
InputDevice::SingleTouchScreenState::Accumulator::FIELD_BTN_TOUCH;
|
InputDevice::SingleTouchScreenState::Accumulator::FIELD_BTN_TOUCH;
|
||||||
device->singleTouchScreen.accumulator.btnTouch = down;
|
device->singleTouchScreen.accumulator.btnTouch = down;
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
} else if (device->isTrackball()) {
|
}
|
||||||
|
|
||||||
|
if (device->isTrackball()) {
|
||||||
switch (rawEvent->scanCode) {
|
switch (rawEvent->scanCode) {
|
||||||
case BTN_MOUSE:
|
case BTN_MOUSE:
|
||||||
device->trackball.accumulator.fields |=
|
device->trackball.accumulator.fields |=
|
||||||
InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE;
|
InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE;
|
||||||
device->trackball.accumulator.btnMouse = down;
|
device->trackball.accumulator.btnMouse = down;
|
||||||
|
return;
|
||||||
// send the down immediately
|
|
||||||
// XXX this emulates the old behavior of KeyInputQueue, unclear whether it is
|
|
||||||
// necessary or if we can wait until the next sync
|
|
||||||
onTrackballStateChanged(rawEvent->when, device);
|
|
||||||
device->trackball.accumulator.clear();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (device->isKeyboard()) {
|
||||||
|
int32_t keyCode = rawEvent->keyCode;
|
||||||
|
onKey(rawEvent->when, device, down, keyCode, scanCode, rawEvent->flags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputReader::handleRelativeMotion(const RawEvent* rawEvent) {
|
void InputReader::handleRelativeMotion(const RawEvent* rawEvent) {
|
||||||
|
@ -76,7 +76,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
|
|||||||
const int32_t nature = INPUT_EVENT_NATURE_KEY;
|
const int32_t nature = INPUT_EVENT_NATURE_KEY;
|
||||||
const int32_t action = KEY_EVENT_ACTION_DOWN;
|
const int32_t action = KEY_EVENT_ACTION_DOWN;
|
||||||
const int32_t flags = KEY_EVENT_FLAG_FROM_SYSTEM;
|
const int32_t flags = KEY_EVENT_FLAG_FROM_SYSTEM;
|
||||||
const int32_t keyCode = KEYCODE_ENTER;
|
const int32_t keyCode = AKEYCODE_ENTER;
|
||||||
const int32_t scanCode = 13;
|
const int32_t scanCode = 13;
|
||||||
const int32_t metaState = META_ALT_LEFT_ON | META_ALT_ON;
|
const int32_t metaState = META_ALT_LEFT_ON | META_ALT_ON;
|
||||||
const int32_t repeatCount = 1;
|
const int32_t repeatCount = 1;
|
||||||
|
@ -122,3 +122,13 @@ LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp
|
|||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
# Include subdirectory makefiles
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
|
||||||
|
# team really wants is to build the stuff defined by this makefile.
|
||||||
|
ifeq (,$(ONE_SHOT_MAKEFILE))
|
||||||
|
include $(call first-makefiles-under,$(LOCAL_PATH))
|
||||||
|
endif
|
@ -68,7 +68,10 @@ enum {
|
|||||||
INPUT_DEVICE_CLASS_TOUCHSCREEN_MT= 0x00000010,
|
INPUT_DEVICE_CLASS_TOUCHSCREEN_MT= 0x00000010,
|
||||||
|
|
||||||
/* The input device is a directional pad. */
|
/* The input device is a directional pad. */
|
||||||
INPUT_DEVICE_CLASS_DPAD = 0x00000020
|
INPUT_DEVICE_CLASS_DPAD = 0x00000020,
|
||||||
|
|
||||||
|
/* The input device is a gamepad (implies keyboard). */
|
||||||
|
INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -41,114 +41,122 @@ extern "C" {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Key codes.
|
* Key codes.
|
||||||
*
|
|
||||||
* XXX: The declarations in <ui/KeycodeLabel.h> should be updated to use these instead.
|
|
||||||
* We should probably move this into android/keycodes.h and add some new API for
|
|
||||||
* getting labels so that we can remove the other tables also in KeycodeLabel.h.
|
|
||||||
*/
|
*/
|
||||||
enum {
|
enum {
|
||||||
KEYCODE_UNKNOWN = 0,
|
AKEYCODE_UNKNOWN = 0,
|
||||||
KEYCODE_SOFT_LEFT = 1,
|
AKEYCODE_SOFT_LEFT = 1,
|
||||||
KEYCODE_SOFT_RIGHT = 2,
|
AKEYCODE_SOFT_RIGHT = 2,
|
||||||
KEYCODE_HOME = 3,
|
AKEYCODE_HOME = 3,
|
||||||
KEYCODE_BACK = 4,
|
AKEYCODE_BACK = 4,
|
||||||
KEYCODE_CALL = 5,
|
AKEYCODE_CALL = 5,
|
||||||
KEYCODE_ENDCALL = 6,
|
AKEYCODE_ENDCALL = 6,
|
||||||
KEYCODE_0 = 7,
|
AKEYCODE_0 = 7,
|
||||||
KEYCODE_1 = 8,
|
AKEYCODE_1 = 8,
|
||||||
KEYCODE_2 = 9,
|
AKEYCODE_2 = 9,
|
||||||
KEYCODE_3 = 10,
|
AKEYCODE_3 = 10,
|
||||||
KEYCODE_4 = 11,
|
AKEYCODE_4 = 11,
|
||||||
KEYCODE_5 = 12,
|
AKEYCODE_5 = 12,
|
||||||
KEYCODE_6 = 13,
|
AKEYCODE_6 = 13,
|
||||||
KEYCODE_7 = 14,
|
AKEYCODE_7 = 14,
|
||||||
KEYCODE_8 = 15,
|
AKEYCODE_8 = 15,
|
||||||
KEYCODE_9 = 16,
|
AKEYCODE_9 = 16,
|
||||||
KEYCODE_STAR = 17,
|
AKEYCODE_STAR = 17,
|
||||||
KEYCODE_POUND = 18,
|
AKEYCODE_POUND = 18,
|
||||||
KEYCODE_DPAD_UP = 19,
|
AKEYCODE_DPAD_UP = 19,
|
||||||
KEYCODE_DPAD_DOWN = 20,
|
AKEYCODE_DPAD_DOWN = 20,
|
||||||
KEYCODE_DPAD_LEFT = 21,
|
AKEYCODE_DPAD_LEFT = 21,
|
||||||
KEYCODE_DPAD_RIGHT = 22,
|
AKEYCODE_DPAD_RIGHT = 22,
|
||||||
KEYCODE_DPAD_CENTER = 23,
|
AKEYCODE_DPAD_CENTER = 23,
|
||||||
KEYCODE_VOLUME_UP = 24,
|
AKEYCODE_VOLUME_UP = 24,
|
||||||
KEYCODE_VOLUME_DOWN = 25,
|
AKEYCODE_VOLUME_DOWN = 25,
|
||||||
KEYCODE_POWER = 26,
|
AKEYCODE_POWER = 26,
|
||||||
KEYCODE_CAMERA = 27,
|
AKEYCODE_CAMERA = 27,
|
||||||
KEYCODE_CLEAR = 28,
|
AKEYCODE_CLEAR = 28,
|
||||||
KEYCODE_A = 29,
|
AKEYCODE_A = 29,
|
||||||
KEYCODE_B = 30,
|
AKEYCODE_B = 30,
|
||||||
KEYCODE_C = 31,
|
AKEYCODE_C = 31,
|
||||||
KEYCODE_D = 32,
|
AKEYCODE_D = 32,
|
||||||
KEYCODE_E = 33,
|
AKEYCODE_E = 33,
|
||||||
KEYCODE_F = 34,
|
AKEYCODE_F = 34,
|
||||||
KEYCODE_G = 35,
|
AKEYCODE_G = 35,
|
||||||
KEYCODE_H = 36,
|
AKEYCODE_H = 36,
|
||||||
KEYCODE_I = 37,
|
AKEYCODE_I = 37,
|
||||||
KEYCODE_J = 38,
|
AKEYCODE_J = 38,
|
||||||
KEYCODE_K = 39,
|
AKEYCODE_K = 39,
|
||||||
KEYCODE_L = 40,
|
AKEYCODE_L = 40,
|
||||||
KEYCODE_M = 41,
|
AKEYCODE_M = 41,
|
||||||
KEYCODE_N = 42,
|
AKEYCODE_N = 42,
|
||||||
KEYCODE_O = 43,
|
AKEYCODE_O = 43,
|
||||||
KEYCODE_P = 44,
|
AKEYCODE_P = 44,
|
||||||
KEYCODE_Q = 45,
|
AKEYCODE_Q = 45,
|
||||||
KEYCODE_R = 46,
|
AKEYCODE_R = 46,
|
||||||
KEYCODE_S = 47,
|
AKEYCODE_S = 47,
|
||||||
KEYCODE_T = 48,
|
AKEYCODE_T = 48,
|
||||||
KEYCODE_U = 49,
|
AKEYCODE_U = 49,
|
||||||
KEYCODE_V = 50,
|
AKEYCODE_V = 50,
|
||||||
KEYCODE_W = 51,
|
AKEYCODE_W = 51,
|
||||||
KEYCODE_X = 52,
|
AKEYCODE_X = 52,
|
||||||
KEYCODE_Y = 53,
|
AKEYCODE_Y = 53,
|
||||||
KEYCODE_Z = 54,
|
AKEYCODE_Z = 54,
|
||||||
KEYCODE_COMMA = 55,
|
AKEYCODE_COMMA = 55,
|
||||||
KEYCODE_PERIOD = 56,
|
AKEYCODE_PERIOD = 56,
|
||||||
KEYCODE_ALT_LEFT = 57,
|
AKEYCODE_ALT_LEFT = 57,
|
||||||
KEYCODE_ALT_RIGHT = 58,
|
AKEYCODE_ALT_RIGHT = 58,
|
||||||
KEYCODE_SHIFT_LEFT = 59,
|
AKEYCODE_SHIFT_LEFT = 59,
|
||||||
KEYCODE_SHIFT_RIGHT = 60,
|
AKEYCODE_SHIFT_RIGHT = 60,
|
||||||
KEYCODE_TAB = 61,
|
AKEYCODE_TAB = 61,
|
||||||
KEYCODE_SPACE = 62,
|
AKEYCODE_SPACE = 62,
|
||||||
KEYCODE_SYM = 63,
|
AKEYCODE_SYM = 63,
|
||||||
KEYCODE_EXPLORER = 64,
|
AKEYCODE_EXPLORER = 64,
|
||||||
KEYCODE_ENVELOPE = 65,
|
AKEYCODE_ENVELOPE = 65,
|
||||||
KEYCODE_ENTER = 66,
|
AKEYCODE_ENTER = 66,
|
||||||
KEYCODE_DEL = 67,
|
AKEYCODE_DEL = 67,
|
||||||
KEYCODE_GRAVE = 68,
|
AKEYCODE_GRAVE = 68,
|
||||||
KEYCODE_MINUS = 69,
|
AKEYCODE_MINUS = 69,
|
||||||
KEYCODE_EQUALS = 70,
|
AKEYCODE_EQUALS = 70,
|
||||||
KEYCODE_LEFT_BRACKET = 71,
|
AKEYCODE_LEFT_BRACKET = 71,
|
||||||
KEYCODE_RIGHT_BRACKET = 72,
|
AKEYCODE_RIGHT_BRACKET = 72,
|
||||||
KEYCODE_BACKSLASH = 73,
|
AKEYCODE_BACKSLASH = 73,
|
||||||
KEYCODE_SEMICOLON = 74,
|
AKEYCODE_SEMICOLON = 74,
|
||||||
KEYCODE_APOSTROPHE = 75,
|
AKEYCODE_APOSTROPHE = 75,
|
||||||
KEYCODE_SLASH = 76,
|
AKEYCODE_SLASH = 76,
|
||||||
KEYCODE_AT = 77,
|
AKEYCODE_AT = 77,
|
||||||
KEYCODE_NUM = 78,
|
AKEYCODE_NUM = 78,
|
||||||
KEYCODE_HEADSETHOOK = 79,
|
AKEYCODE_HEADSETHOOK = 79,
|
||||||
KEYCODE_FOCUS = 80, // *Camera* focus
|
AKEYCODE_FOCUS = 80, // *Camera* focus
|
||||||
KEYCODE_PLUS = 81,
|
AKEYCODE_PLUS = 81,
|
||||||
KEYCODE_MENU = 82,
|
AKEYCODE_MENU = 82,
|
||||||
KEYCODE_NOTIFICATION = 83,
|
AKEYCODE_NOTIFICATION = 83,
|
||||||
KEYCODE_SEARCH = 84,
|
AKEYCODE_SEARCH = 84,
|
||||||
KEYCODE_MEDIA_PLAY_PAUSE= 85,
|
AKEYCODE_MEDIA_PLAY_PAUSE= 85,
|
||||||
KEYCODE_MEDIA_STOP = 86,
|
AKEYCODE_MEDIA_STOP = 86,
|
||||||
KEYCODE_MEDIA_NEXT = 87,
|
AKEYCODE_MEDIA_NEXT = 87,
|
||||||
KEYCODE_MEDIA_PREVIOUS = 88,
|
AKEYCODE_MEDIA_PREVIOUS = 88,
|
||||||
KEYCODE_MEDIA_REWIND = 89,
|
AKEYCODE_MEDIA_REWIND = 89,
|
||||||
KEYCODE_MEDIA_FAST_FORWARD = 90,
|
AKEYCODE_MEDIA_FAST_FORWARD = 90,
|
||||||
KEYCODE_MUTE = 91,
|
AKEYCODE_MUTE = 91,
|
||||||
KEYCODE_PAGE_UP = 92,
|
AKEYCODE_PAGE_UP = 92,
|
||||||
KEYCODE_PAGE_DOWN = 93
|
AKEYCODE_PAGE_DOWN = 93,
|
||||||
|
AKEYCODE_PICTSYMBOLS = 94,
|
||||||
|
AKEYCODE_SWITCH_CHARSET = 95,
|
||||||
|
AKEYCODE_BUTTON_A = 96,
|
||||||
|
AKEYCODE_BUTTON_B = 97,
|
||||||
|
AKEYCODE_BUTTON_C = 98,
|
||||||
|
AKEYCODE_BUTTON_X = 99,
|
||||||
|
AKEYCODE_BUTTON_Y = 100,
|
||||||
|
AKEYCODE_BUTTON_Z = 101,
|
||||||
|
AKEYCODE_BUTTON_L1 = 102,
|
||||||
|
AKEYCODE_BUTTON_R1 = 103,
|
||||||
|
AKEYCODE_BUTTON_L2 = 104,
|
||||||
|
AKEYCODE_BUTTON_R2 = 105,
|
||||||
|
AKEYCODE_BUTTON_THUMBL = 106,
|
||||||
|
AKEYCODE_BUTTON_THUMBR = 107,
|
||||||
|
AKEYCODE_BUTTON_START = 108,
|
||||||
|
AKEYCODE_BUTTON_SELECT = 109,
|
||||||
|
AKEYCODE_BUTTON_MODE = 110,
|
||||||
|
|
||||||
/* NOTE: If you add a new keycode here you must also add it to:
|
// NOTE: If you add a new keycode here you must also add it to several other files.
|
||||||
* native/include/android/keycodes.h
|
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
|
||||||
* frameworks/base/include/ui/KeycodeLabels.h
|
|
||||||
* frameworks/base/core/java/android/view/KeyEvent.java
|
|
||||||
* tools/puppet_master/PuppetMaster.nav_keys.py
|
|
||||||
* frameworks/base/core/res/res/values/attrs.xml
|
|
||||||
*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -405,28 +405,28 @@ NativeInputManager::~NativeInputManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
|
bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
|
||||||
return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL;
|
return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NativeInputManager::isPolicyKey(int32_t keyCode, bool isScreenOn) {
|
bool NativeInputManager::isPolicyKey(int32_t keyCode, bool isScreenOn) {
|
||||||
// Special keys that the WindowManagerPolicy might care about.
|
// Special keys that the WindowManagerPolicy might care about.
|
||||||
switch (keyCode) {
|
switch (keyCode) {
|
||||||
case KEYCODE_VOLUME_UP:
|
case AKEYCODE_VOLUME_UP:
|
||||||
case KEYCODE_VOLUME_DOWN:
|
case AKEYCODE_VOLUME_DOWN:
|
||||||
case KEYCODE_ENDCALL:
|
case AKEYCODE_ENDCALL:
|
||||||
case KEYCODE_POWER:
|
case AKEYCODE_POWER:
|
||||||
case KEYCODE_CALL:
|
case AKEYCODE_CALL:
|
||||||
case KEYCODE_HOME:
|
case AKEYCODE_HOME:
|
||||||
case KEYCODE_MENU:
|
case AKEYCODE_MENU:
|
||||||
case KEYCODE_SEARCH:
|
case AKEYCODE_SEARCH:
|
||||||
// media keys
|
// media keys
|
||||||
case KEYCODE_HEADSETHOOK:
|
case AKEYCODE_HEADSETHOOK:
|
||||||
case KEYCODE_MEDIA_PLAY_PAUSE:
|
case AKEYCODE_MEDIA_PLAY_PAUSE:
|
||||||
case KEYCODE_MEDIA_STOP:
|
case AKEYCODE_MEDIA_STOP:
|
||||||
case KEYCODE_MEDIA_NEXT:
|
case AKEYCODE_MEDIA_NEXT:
|
||||||
case KEYCODE_MEDIA_PREVIOUS:
|
case AKEYCODE_MEDIA_PREVIOUS:
|
||||||
case KEYCODE_MEDIA_REWIND:
|
case AKEYCODE_MEDIA_REWIND:
|
||||||
case KEYCODE_MEDIA_FAST_FORWARD:
|
case AKEYCODE_MEDIA_FAST_FORWARD:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
// We need to pass all keys to the policy in the following cases:
|
// We need to pass all keys to the policy in the following cases:
|
||||||
|
Reference in New Issue
Block a user