Joystick tweaks.
Ensure that the joystick can always reach -1.0, 0.0 and 1.0 positions
even when noise filtering is applied. (Bug: 3514510)
Add support for a few more standard axes.
Add additional mapping modes for axes.
Some axes are inverted from standard interpretation
or are actually intended to be split into two distict axes
such as left/right trigger controls or accelerator/brake.
Add key layout file for a G25 racing wheel and XBox 360 controller
to tweak behavior. They work fine without them but the axis mappings
are not ideal.
Change-Id: Id4c86a0a272331c680039a9bde6815bb7eba44ab
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index d5f78a9..e2da740 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -352,14 +352,13 @@
return NAME_NOT_FOUND;
}
-status_t EventHub::mapAxis(int32_t deviceId, int scancode,
- int32_t* outAxis) const
+status_t EventHub::mapAxis(int32_t deviceId, int scancode, AxisInfo* outAxisInfo) const
{
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device && device->keyMap.haveKeyLayout()) {
- status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxis);
+ status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxisInfo);
if (err == NO_ERROR) {
return NO_ERROR;
}
@@ -369,14 +368,13 @@
device = getDeviceLocked(mBuiltInKeyboardId);
if (device && device->keyMap.haveKeyLayout()) {
- status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxis);
+ status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxisInfo);
if (err == NO_ERROR) {
return NO_ERROR;
}
}
}
- *outAxis = -1;
return NAME_NOT_FOUND;
}
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 99be802..7053a94 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -174,7 +174,7 @@
int32_t* outKeycode, uint32_t* outFlags) const = 0;
virtual status_t mapAxis(int32_t deviceId, int scancode,
- int32_t* outAxis) const = 0;
+ AxisInfo* outAxisInfo) const = 0;
// exclude a particular device from opening
// this can be used to ignore input devices for sensors
@@ -233,7 +233,7 @@
int32_t* outKeycode, uint32_t* outFlags) const;
virtual status_t mapAxis(int32_t deviceId, int scancode,
- int32_t* outAxis) const;
+ AxisInfo* outAxisInfo) const;
virtual void addExcludedDevice(const char* deviceName);
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 084264b..35e63d1 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -3863,7 +3863,10 @@
for (size_t i = 0; i < mAxes.size(); i++) {
const Axis& axis = mAxes.valueAt(i);
- info->addMotionRange(axis.axis, axis.min, axis.max, axis.flat, axis.fuzz);
+ info->addMotionRange(axis.axisInfo.axis, axis.min, axis.max, axis.flat, axis.fuzz);
+ if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ info->addMotionRange(axis.axisInfo.highAxis, axis.min, axis.max, axis.flat, axis.fuzz);
+ }
}
}
@@ -3874,18 +3877,29 @@
size_t numAxes = mAxes.size();
for (size_t i = 0; i < numAxes; i++) {
const Axis& axis = mAxes.valueAt(i);
- const char* label = getAxisLabel(axis.axis);
- char name[32];
+ const char* label = getAxisLabel(axis.axisInfo.axis);
if (label) {
- strncpy(name, label, sizeof(name));
- name[sizeof(name) - 1] = '\0';
+ dump.appendFormat(INDENT4 "%s", label);
} else {
- snprintf(name, sizeof(name), "%d", axis.axis);
+ dump.appendFormat(INDENT4 "%d", axis.axisInfo.axis);
}
- dump.appendFormat(INDENT4 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, "
- "scale=%0.3f, offset=%0.3f\n",
- name, axis.min, axis.max, axis.flat, axis.fuzz,
- axis.scale, axis.offset);
+ if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ label = getAxisLabel(axis.axisInfo.highAxis);
+ if (label) {
+ dump.appendFormat(" / %s (split at %d)", label, axis.axisInfo.splitValue);
+ } else {
+ dump.appendFormat(" / %d (split at %d)", axis.axisInfo.highAxis,
+ axis.axisInfo.splitValue);
+ }
+ } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) {
+ dump.append(" (invert)");
+ }
+
+ dump.appendFormat(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f\n",
+ axis.min, axis.max, axis.flat, axis.fuzz);
+ dump.appendFormat(INDENT4 " scale=%0.5f, offset=%0.5f, "
+ "highScale=%0.5f, highOffset=%0.5f\n",
+ axis.scale, axis.offset, axis.highScale, axis.highOffset);
dump.appendFormat(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, rawFlat=%d, rawFuzz=%d\n",
mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz);
@@ -3900,25 +3914,38 @@
RawAbsoluteAxisInfo rawAxisInfo;
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), abs, &rawAxisInfo);
if (rawAxisInfo.valid) {
- int32_t axisId;
- bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisId);
+ // Map axis.
+ AxisInfo axisInfo;
+ bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo);
if (!explicitlyMapped) {
// Axis is not explicitly mapped, will choose a generic axis later.
- axisId = -1;
+ axisInfo.mode = AxisInfo::MODE_NORMAL;
+ axisInfo.axis = -1;
}
+ // Apply flat override.
+ int32_t rawFlat = axisInfo.flatOverride < 0
+ ? rawAxisInfo.flat : axisInfo.flatOverride;
+
+ // Calculate scaling factors and limits.
Axis axis;
- if (isCenteredAxis(axisId)) {
+ if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
+ float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
+ axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
+ scale, 0.0f, highScale, 0.0f,
+ 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+ } else if (isCenteredAxis(axisInfo.axis)) {
float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
- axis.initialize(rawAxisInfo, axisId, explicitlyMapped,
- scale, offset, -1.0f, 1.0f,
- rawAxisInfo.flat * scale, rawAxisInfo.fuzz * scale);
+ axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
+ scale, offset, scale, offset,
+ -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
} else {
float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
- axis.initialize(rawAxisInfo, axisId, explicitlyMapped,
- scale, 0.0f, 0.0f, 1.0f,
- rawAxisInfo.flat * scale, rawAxisInfo.fuzz * scale);
+ axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
+ scale, 0.0f, scale, 0.0f,
+ 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
}
// To eliminate noise while the joystick is at rest, filter out small variations
@@ -3943,14 +3970,14 @@
size_t numAxes = mAxes.size();
for (size_t i = 0; i < numAxes; i++) {
Axis& axis = mAxes.editValueAt(i);
- if (axis.axis < 0) {
+ if (axis.axisInfo.axis < 0) {
while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16
&& haveAxis(nextGenericAxisId)) {
nextGenericAxisId += 1;
}
if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
- axis.axis = nextGenericAxisId;
+ axis.axisInfo.axis = nextGenericAxisId;
nextGenericAxisId += 1;
} else {
LOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
@@ -3963,10 +3990,13 @@
}
}
-bool JoystickInputMapper::haveAxis(int32_t axis) {
+bool JoystickInputMapper::haveAxis(int32_t axisId) {
size_t numAxes = mAxes.size();
for (size_t i = 0; i < numAxes; i++) {
- if (mAxes.valueAt(i).axis == axis) {
+ const Axis& axis = mAxes.valueAt(i);
+ if (axis.axisInfo.axis == axisId
+ || (axis.axisInfo.mode == AxisInfo::MODE_SPLIT
+ && axis.axisInfo.highAxis == axisId)) {
return true;
}
}
@@ -3996,6 +4026,8 @@
case AMOTION_EVENT_AXIS_HAT_X:
case AMOTION_EVENT_AXIS_HAT_Y:
case AMOTION_EVENT_AXIS_ORIENTATION:
+ case AMOTION_EVENT_AXIS_RUDDER:
+ case AMOTION_EVENT_AXIS_WHEEL:
return true;
default:
return false;
@@ -4009,7 +4041,7 @@
size_t numAxes = mAxes.size();
for (size_t i = 0; i < numAxes; i++) {
Axis& axis = mAxes.editValueAt(i);
- axis.newValue = 0;
+ axis.resetValue();
}
sync(when, true /*force*/);
@@ -4023,10 +4055,34 @@
ssize_t index = mAxes.indexOfKey(rawEvent->scanCode);
if (index >= 0) {
Axis& axis = mAxes.editValueAt(index);
- float newValue = rawEvent->value * axis.scale + axis.offset;
- if (newValue != axis.newValue) {
- axis.newValue = newValue;
+ float newValue, highNewValue;
+ switch (axis.axisInfo.mode) {
+ case AxisInfo::MODE_INVERT:
+ newValue = (axis.rawAxisInfo.maxValue - rawEvent->value)
+ * axis.scale + axis.offset;
+ highNewValue = 0.0f;
+ break;
+ case AxisInfo::MODE_SPLIT:
+ if (rawEvent->value < axis.axisInfo.splitValue) {
+ newValue = (axis.axisInfo.splitValue - rawEvent->value)
+ * axis.scale + axis.offset;
+ highNewValue = 0.0f;
+ } else if (rawEvent->value > axis.axisInfo.splitValue) {
+ newValue = 0.0f;
+ highNewValue = (rawEvent->value - axis.axisInfo.splitValue)
+ * axis.highScale + axis.highOffset;
+ } else {
+ newValue = 0.0f;
+ highNewValue = 0.0f;
+ }
+ break;
+ default:
+ newValue = rawEvent->value * axis.scale + axis.offset;
+ highNewValue = 0.0f;
+ break;
}
+ axis.newValue = newValue;
+ axis.highNewValue = highNewValue;
}
break;
}
@@ -4042,7 +4098,7 @@
}
void JoystickInputMapper::sync(nsecs_t when, bool force) {
- if (!force && !haveAxesChangedSignificantly()) {
+ if (!filterAxes(force)) {
return;
}
@@ -4053,9 +4109,11 @@
size_t numAxes = mAxes.size();
for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
- pointerCoords.setAxisValue(axis.axis, axis.newValue);
- axis.oldValue = axis.newValue;
+ const Axis& axis = mAxes.valueAt(i);
+ pointerCoords.setAxisValue(axis.axisInfo.axis, axis.currentValue);
+ if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ pointerCoords.setAxisValue(axis.axisInfo.highAxis, axis.highCurrentValue);
+ }
}
// Moving a joystick axis should not wake the devide because joysticks can
@@ -4070,12 +4128,49 @@
1, &pointerId, &pointerCoords, 0, 0, 0);
}
-bool JoystickInputMapper::haveAxesChangedSignificantly() {
+bool JoystickInputMapper::filterAxes(bool force) {
+ bool atLeastOneSignificantChange = force;
size_t numAxes = mAxes.size();
for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
- if (axis.newValue != axis.oldValue
- && fabs(axis.newValue - axis.oldValue) > axis.filter) {
+ Axis& axis = mAxes.editValueAt(i);
+ if (force || hasValueChangedSignificantly(axis.filter,
+ axis.newValue, axis.currentValue, axis.min, axis.max)) {
+ axis.currentValue = axis.newValue;
+ atLeastOneSignificantChange = true;
+ }
+ if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ if (force || hasValueChangedSignificantly(axis.filter,
+ axis.highNewValue, axis.highCurrentValue, axis.min, axis.max)) {
+ axis.highCurrentValue = axis.highNewValue;
+ atLeastOneSignificantChange = true;
+ }
+ }
+ }
+ return atLeastOneSignificantChange;
+}
+
+bool JoystickInputMapper::hasValueChangedSignificantly(
+ float filter, float newValue, float currentValue, float min, float max) {
+ if (newValue != currentValue) {
+ // Filter out small changes in value unless the value is converging on the axis
+ // bounds or center point. This is intended to reduce the amount of information
+ // sent to applications by particularly noisy joysticks (such as PS3).
+ if (fabs(newValue - currentValue) > filter
+ || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min)
+ || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max)
+ || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(
+ float filter, float newValue, float currentValue, float thresholdValue) {
+ float newDistance = fabs(newValue - thresholdValue);
+ if (newDistance < filter) {
+ float oldDistance = fabs(currentValue - thresholdValue);
+ if (newDistance < oldDistance) {
return true;
}
}
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 9b79690..b9e3494 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -999,38 +999,50 @@
private:
struct Axis {
RawAbsoluteAxisInfo rawAxisInfo;
+ AxisInfo axisInfo;
- int32_t axis; // axis id
bool explicitlyMapped; // true if the axis was explicitly assigned an axis id
float scale; // scale factor from raw to normalized values
float offset; // offset to add after scaling for normalization
+ float highScale; // scale factor from raw to normalized values of high split
+ float highOffset; // offset to add after scaling for normalization of high split
float min; // normalized inclusive minimum
float max; // normalized inclusive maximum
float flat; // normalized flat region size
float fuzz; // normalized error tolerance
- float oldValue; // previous value
- float newValue; // most recent value
-
float filter; // filter out small variations of this size
+ float currentValue; // current value
+ float newValue; // most recent value
+ float highCurrentValue; // current value of high split
+ float highNewValue; // most recent value of high split
- void initialize(const RawAbsoluteAxisInfo& rawAxisInfo,
- int32_t axis, bool explicitlyMapped, float scale, float offset,
+ void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
+ bool explicitlyMapped, float scale, float offset,
+ float highScale, float highOffset,
float min, float max, float flat, float fuzz) {
this->rawAxisInfo = rawAxisInfo;
- this->axis = axis;
+ this->axisInfo = axisInfo;
this->explicitlyMapped = explicitlyMapped;
this->scale = scale;
this->offset = offset;
+ this->highScale = highScale;
+ this->highOffset = highOffset;
this->min = min;
this->max = max;
this->flat = flat;
this->fuzz = fuzz;
this->filter = 0;
- this->oldValue = 0;
+ resetValue();
+ }
+
+ void resetValue() {
+ this->currentValue = 0;
this->newValue = 0;
+ this->highCurrentValue = 0;
+ this->highNewValue = 0;
}
};
@@ -1039,9 +1051,14 @@
void sync(nsecs_t when, bool force);
- bool haveAxis(int32_t axis);
+ bool haveAxis(int32_t axisId);
void pruneAxes(bool ignoreExplicitlyMappedAxes);
- bool haveAxesChangedSignificantly();
+ bool filterAxes(bool force);
+
+ static bool hasValueChangedSignificantly(float filter,
+ float newValue, float currentValue, float min, float max);
+ static bool hasMovedNearerToValueWithinFilteredRange(float filter,
+ float newValue, float currentValue, float thresholdValue);
static bool isCenteredAxis(int32_t axis);
};
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 1180b83..f7e1890 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -592,7 +592,7 @@
}
virtual status_t mapAxis(int32_t deviceId, int scancode,
- int32_t* outAxis) const {
+ AxisInfo* outAxisInfo) const {
return NAME_NOT_FOUND;
}