1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "Macros.h"
18
19 #include "JoystickInputMapper.h"
20
21 namespace android {
22
JoystickInputMapper(InputDevice * device)23 JoystickInputMapper::JoystickInputMapper(InputDevice* device) : InputMapper(device) {}
24
~JoystickInputMapper()25 JoystickInputMapper::~JoystickInputMapper() {}
26
getSources()27 uint32_t JoystickInputMapper::getSources() {
28 return AINPUT_SOURCE_JOYSTICK;
29 }
30
populateDeviceInfo(InputDeviceInfo * info)31 void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
32 InputMapper::populateDeviceInfo(info);
33
34 for (size_t i = 0; i < mAxes.size(); i++) {
35 const Axis& axis = mAxes.valueAt(i);
36 addMotionRange(axis.axisInfo.axis, axis, info);
37
38 if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
39 addMotionRange(axis.axisInfo.highAxis, axis, info);
40 }
41 }
42 }
43
addMotionRange(int32_t axisId,const Axis & axis,InputDeviceInfo * info)44 void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info) {
45 info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, axis.fuzz,
46 axis.resolution);
47 /* In order to ease the transition for developers from using the old axes
48 * to the newer, more semantically correct axes, we'll continue to register
49 * the old axes as duplicates of their corresponding new ones. */
50 int32_t compatAxis = getCompatAxis(axisId);
51 if (compatAxis >= 0) {
52 info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat,
53 axis.fuzz, axis.resolution);
54 }
55 }
56
57 /* A mapping from axes the joystick actually has to the axes that should be
58 * artificially created for compatibility purposes.
59 * Returns -1 if no compatibility axis is needed. */
getCompatAxis(int32_t axis)60 int32_t JoystickInputMapper::getCompatAxis(int32_t axis) {
61 switch (axis) {
62 case AMOTION_EVENT_AXIS_LTRIGGER:
63 return AMOTION_EVENT_AXIS_BRAKE;
64 case AMOTION_EVENT_AXIS_RTRIGGER:
65 return AMOTION_EVENT_AXIS_GAS;
66 }
67 return -1;
68 }
69
dump(std::string & dump)70 void JoystickInputMapper::dump(std::string& dump) {
71 dump += INDENT2 "Joystick Input Mapper:\n";
72
73 dump += INDENT3 "Axes:\n";
74 size_t numAxes = mAxes.size();
75 for (size_t i = 0; i < numAxes; i++) {
76 const Axis& axis = mAxes.valueAt(i);
77 const char* label = getAxisLabel(axis.axisInfo.axis);
78 if (label) {
79 dump += StringPrintf(INDENT4 "%s", label);
80 } else {
81 dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis);
82 }
83 if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
84 label = getAxisLabel(axis.axisInfo.highAxis);
85 if (label) {
86 dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue);
87 } else {
88 dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis,
89 axis.axisInfo.splitValue);
90 }
91 } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) {
92 dump += " (invert)";
93 }
94
95 dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n",
96 axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
97 dump += StringPrintf(INDENT4 " scale=%0.5f, offset=%0.5f, "
98 "highScale=%0.5f, highOffset=%0.5f\n",
99 axis.scale, axis.offset, axis.highScale, axis.highOffset);
100 dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, "
101 "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n",
102 mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
103 axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz,
104 axis.rawAxisInfo.resolution);
105 }
106 }
107
configure(nsecs_t when,const InputReaderConfiguration * config,uint32_t changes)108 void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
109 uint32_t changes) {
110 InputMapper::configure(when, config, changes);
111
112 if (!changes) { // first time only
113 // Collect all axes.
114 for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
115 if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) & INPUT_DEVICE_CLASS_JOYSTICK)) {
116 continue; // axis must be claimed by a different device
117 }
118
119 RawAbsoluteAxisInfo rawAxisInfo;
120 getAbsoluteAxisInfo(abs, &rawAxisInfo);
121 if (rawAxisInfo.valid) {
122 // Map axis.
123 AxisInfo axisInfo;
124 bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo);
125 if (!explicitlyMapped) {
126 // Axis is not explicitly mapped, will choose a generic axis later.
127 axisInfo.mode = AxisInfo::MODE_NORMAL;
128 axisInfo.axis = -1;
129 }
130
131 // Apply flat override.
132 int32_t rawFlat =
133 axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
134
135 // Calculate scaling factors and limits.
136 Axis axis;
137 if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
138 float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
139 float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
140 axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale,
141 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
142 rawAxisInfo.resolution * scale);
143 } else if (isCenteredAxis(axisInfo.axis)) {
144 float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
145 float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
146 axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale,
147 offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
148 rawAxisInfo.resolution * scale);
149 } else {
150 float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
151 axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale,
152 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
153 rawAxisInfo.resolution * scale);
154 }
155
156 // To eliminate noise while the joystick is at rest, filter out small variations
157 // in axis values up front.
158 axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f;
159
160 mAxes.add(abs, axis);
161 }
162 }
163
164 // If there are too many axes, start dropping them.
165 // Prefer to keep explicitly mapped axes.
166 if (mAxes.size() > PointerCoords::MAX_AXES) {
167 ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.",
168 getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES);
169 pruneAxes(true);
170 pruneAxes(false);
171 }
172
173 // Assign generic axis ids to remaining axes.
174 int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
175 size_t numAxes = mAxes.size();
176 for (size_t i = 0; i < numAxes; i++) {
177 Axis& axis = mAxes.editValueAt(i);
178 if (axis.axisInfo.axis < 0) {
179 while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 &&
180 haveAxis(nextGenericAxisId)) {
181 nextGenericAxisId += 1;
182 }
183
184 if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
185 axis.axisInfo.axis = nextGenericAxisId;
186 nextGenericAxisId += 1;
187 } else {
188 ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
189 "have already been assigned to other axes.",
190 getDeviceName().c_str(), mAxes.keyAt(i));
191 mAxes.removeItemsAt(i--);
192 numAxes -= 1;
193 }
194 }
195 }
196 }
197 }
198
haveAxis(int32_t axisId)199 bool JoystickInputMapper::haveAxis(int32_t axisId) {
200 size_t numAxes = mAxes.size();
201 for (size_t i = 0; i < numAxes; i++) {
202 const Axis& axis = mAxes.valueAt(i);
203 if (axis.axisInfo.axis == axisId ||
204 (axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) {
205 return true;
206 }
207 }
208 return false;
209 }
210
pruneAxes(bool ignoreExplicitlyMappedAxes)211 void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) {
212 size_t i = mAxes.size();
213 while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) {
214 if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) {
215 continue;
216 }
217 ALOGI("Discarding joystick '%s' axis %d because there are too many axes.",
218 getDeviceName().c_str(), mAxes.keyAt(i));
219 mAxes.removeItemsAt(i);
220 }
221 }
222
isCenteredAxis(int32_t axis)223 bool JoystickInputMapper::isCenteredAxis(int32_t axis) {
224 switch (axis) {
225 case AMOTION_EVENT_AXIS_X:
226 case AMOTION_EVENT_AXIS_Y:
227 case AMOTION_EVENT_AXIS_Z:
228 case AMOTION_EVENT_AXIS_RX:
229 case AMOTION_EVENT_AXIS_RY:
230 case AMOTION_EVENT_AXIS_RZ:
231 case AMOTION_EVENT_AXIS_HAT_X:
232 case AMOTION_EVENT_AXIS_HAT_Y:
233 case AMOTION_EVENT_AXIS_ORIENTATION:
234 case AMOTION_EVENT_AXIS_RUDDER:
235 case AMOTION_EVENT_AXIS_WHEEL:
236 return true;
237 default:
238 return false;
239 }
240 }
241
reset(nsecs_t when)242 void JoystickInputMapper::reset(nsecs_t when) {
243 // Recenter all axes.
244 size_t numAxes = mAxes.size();
245 for (size_t i = 0; i < numAxes; i++) {
246 Axis& axis = mAxes.editValueAt(i);
247 axis.resetValue();
248 }
249
250 InputMapper::reset(when);
251 }
252
process(const RawEvent * rawEvent)253 void JoystickInputMapper::process(const RawEvent* rawEvent) {
254 switch (rawEvent->type) {
255 case EV_ABS: {
256 ssize_t index = mAxes.indexOfKey(rawEvent->code);
257 if (index >= 0) {
258 Axis& axis = mAxes.editValueAt(index);
259 float newValue, highNewValue;
260 switch (axis.axisInfo.mode) {
261 case AxisInfo::MODE_INVERT:
262 newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) * axis.scale +
263 axis.offset;
264 highNewValue = 0.0f;
265 break;
266 case AxisInfo::MODE_SPLIT:
267 if (rawEvent->value < axis.axisInfo.splitValue) {
268 newValue = (axis.axisInfo.splitValue - rawEvent->value) * axis.scale +
269 axis.offset;
270 highNewValue = 0.0f;
271 } else if (rawEvent->value > axis.axisInfo.splitValue) {
272 newValue = 0.0f;
273 highNewValue =
274 (rawEvent->value - axis.axisInfo.splitValue) * axis.highScale +
275 axis.highOffset;
276 } else {
277 newValue = 0.0f;
278 highNewValue = 0.0f;
279 }
280 break;
281 default:
282 newValue = rawEvent->value * axis.scale + axis.offset;
283 highNewValue = 0.0f;
284 break;
285 }
286 axis.newValue = newValue;
287 axis.highNewValue = highNewValue;
288 }
289 break;
290 }
291
292 case EV_SYN:
293 switch (rawEvent->code) {
294 case SYN_REPORT:
295 sync(rawEvent->when, false /*force*/);
296 break;
297 }
298 break;
299 }
300 }
301
sync(nsecs_t when,bool force)302 void JoystickInputMapper::sync(nsecs_t when, bool force) {
303 if (!filterAxes(force)) {
304 return;
305 }
306
307 int32_t metaState = mContext->getGlobalMetaState();
308 int32_t buttonState = 0;
309
310 PointerProperties pointerProperties;
311 pointerProperties.clear();
312 pointerProperties.id = 0;
313 pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
314
315 PointerCoords pointerCoords;
316 pointerCoords.clear();
317
318 size_t numAxes = mAxes.size();
319 for (size_t i = 0; i < numAxes; i++) {
320 const Axis& axis = mAxes.valueAt(i);
321 setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue);
322 if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
323 setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis,
324 axis.highCurrentValue);
325 }
326 }
327
328 // Moving a joystick axis should not wake the device because joysticks can
329 // be fairly noisy even when not in use. On the other hand, pushing a gamepad
330 // button will likely wake the device.
331 // TODO: Use the input device configuration to control this behavior more finely.
332 uint32_t policyFlags = 0;
333
334 NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
335 AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags,
336 AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
337 MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
338 /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, 0,
339 /* videoFrames */ {});
340 getListener()->notifyMotion(&args);
341 }
342
setPointerCoordsAxisValue(PointerCoords * pointerCoords,int32_t axis,float value)343 void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
344 float value) {
345 pointerCoords->setAxisValue(axis, value);
346 /* In order to ease the transition for developers from using the old axes
347 * to the newer, more semantically correct axes, we'll continue to produce
348 * values for the old axes as mirrors of the value of their corresponding
349 * new axes. */
350 int32_t compatAxis = getCompatAxis(axis);
351 if (compatAxis >= 0) {
352 pointerCoords->setAxisValue(compatAxis, value);
353 }
354 }
355
filterAxes(bool force)356 bool JoystickInputMapper::filterAxes(bool force) {
357 bool atLeastOneSignificantChange = force;
358 size_t numAxes = mAxes.size();
359 for (size_t i = 0; i < numAxes; i++) {
360 Axis& axis = mAxes.editValueAt(i);
361 if (force ||
362 hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min,
363 axis.max)) {
364 axis.currentValue = axis.newValue;
365 atLeastOneSignificantChange = true;
366 }
367 if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
368 if (force ||
369 hasValueChangedSignificantly(axis.filter, axis.highNewValue, axis.highCurrentValue,
370 axis.min, axis.max)) {
371 axis.highCurrentValue = axis.highNewValue;
372 atLeastOneSignificantChange = true;
373 }
374 }
375 }
376 return atLeastOneSignificantChange;
377 }
378
hasValueChangedSignificantly(float filter,float newValue,float currentValue,float min,float max)379 bool JoystickInputMapper::hasValueChangedSignificantly(float filter, float newValue,
380 float currentValue, float min, float max) {
381 if (newValue != currentValue) {
382 // Filter out small changes in value unless the value is converging on the axis
383 // bounds or center point. This is intended to reduce the amount of information
384 // sent to applications by particularly noisy joysticks (such as PS3).
385 if (fabs(newValue - currentValue) > filter ||
386 hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min) ||
387 hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max) ||
388 hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) {
389 return true;
390 }
391 }
392 return false;
393 }
394
hasMovedNearerToValueWithinFilteredRange(float filter,float newValue,float currentValue,float thresholdValue)395 bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(float filter, float newValue,
396 float currentValue,
397 float thresholdValue) {
398 float newDistance = fabs(newValue - thresholdValue);
399 if (newDistance < filter) {
400 float oldDistance = fabs(currentValue - thresholdValue);
401 if (newDistance < oldDistance) {
402 return true;
403 }
404 }
405 return false;
406 }
407
408 } // namespace android
409