diff options
| author | Mike <7153163+hackbar@users.noreply.github.com> | 2017-10-31 22:43:30 -0700 |
|---|---|---|
| committer | Mike <7153163+hackbar@users.noreply.github.com> | 2017-11-07 17:39:56 -0800 |
| commit | 6787fcb71277e69f43fc32f4b232af916a043c7f (patch) | |
| tree | 6ad3002ba621b6837633256f12e647eb3bc19a7c /Source/Android/app/src/main/java | |
| parent | d9d4bd7eef7dd9af331be1ed3d3e6e737b284178 (diff) | |
Android: Handle duplicate axis, and fix known bad axis.
Android reports the same physical axis multiple times for analog
triggers, and this handles this case.
There are also some controllers with broken mappings (eg the analog
triggers on a PS4 DualShock 4). These axis don't center correctly.
There are also some controllers (again, the PS4) that send both a button
press and an axis movement. This ignores the buttons so we can use the
analog axis. Otherwise, since the button comes before the axis moves
far we would always take the button.
Diffstat (limited to 'Source/Android/app/src/main/java')
3 files changed, 96 insertions, 9 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 2adb2f209d..4688cbce05 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -39,6 +39,7 @@ import org.dolphinemu.dolphinemu.fragments.SaveLoadStateFragment; import org.dolphinemu.dolphinemu.ui.main.MainPresenter; import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.utils.Animations; +import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper; import org.dolphinemu.dolphinemu.utils.Java_GCAdapter; import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter; import org.dolphinemu.dolphinemu.utils.Log; @@ -57,6 +58,7 @@ public final class EmulationActivity extends AppCompatActivity private EmulationFragment mEmulationFragment; private SharedPreferences mPreferences; + private ControllerMappingHelper mControllerMappingHelper; // So that MainActivity knows which view to invalidate before the return animation. private int mPosition; @@ -164,6 +166,7 @@ public final class EmulationActivity extends AppCompatActivity mScreenPath = gameToEmulate.getStringExtra("ScreenPath"); mPosition = gameToEmulate.getIntExtra("GridPosition", -1); mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen"); + mControllerMappingHelper = new ControllerMappingHelper(); int themeId; if (mDeviceHasTouchScreen) @@ -729,7 +732,10 @@ public final class EmulationActivity extends AppCompatActivity for (InputDevice.MotionRange range : motions) { - NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), range.getAxis(), event.getAxisValue(range.getAxis())); + int axis = range.getAxis(); + float origValue = event.getAxisValue(axis); + float value = mControllerMappingHelper.scaleAxis(input, axis, origValue); + NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), axis, value); } return true; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java index 592aee6838..844d877ac6 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java @@ -9,6 +9,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import org.dolphinemu.dolphinemu.model.settings.view.InputBindingSetting; +import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper; import org.dolphinemu.dolphinemu.utils.Log; import java.util.List; @@ -21,6 +22,7 @@ public final class MotionAlertDialog extends AlertDialog { // The selected input preference private final InputBindingSetting setting; + private final ControllerMappingHelper mControllerMappingHelper; private boolean mWaitingForEvent = true; /** @@ -34,6 +36,7 @@ public final class MotionAlertDialog extends AlertDialog super(context); this.setting = setting; + this.mControllerMappingHelper = new ControllerMappingHelper(); } public boolean onKeyEvent(int keyCode, KeyEvent event) @@ -42,8 +45,11 @@ public final class MotionAlertDialog extends AlertDialog switch (event.getAction()) { case KeyEvent.ACTION_DOWN: - saveKeyInput(event); - + if (!mControllerMappingHelper.shouldKeyBeIgnored(event.getDevice(), keyCode)) + { + saveKeyInput(event); + } + // Even if we ignore the key, we still consume it. Thus return true regardless. return true; default: @@ -69,13 +75,15 @@ public final class MotionAlertDialog extends AlertDialog { if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) return false; - - Log.debug("[MotionAlertDialog] Received motion event: " + event.getAction()); + if (event.getAction() != MotionEvent.ACTION_MOVE) + return false; InputDevice input = event.getDevice(); + List<InputDevice.MotionRange> motionRanges = input.getMotionRanges(); int numMovedAxis = 0; + float axisMoveValue = 0.0f; InputDevice.MotionRange lastMovedRange = null; char lastMovedDir = '?'; if (mWaitingForEvent) @@ -84,12 +92,23 @@ public final class MotionAlertDialog extends AlertDialog for (InputDevice.MotionRange range : motionRanges) { int axis = range.getAxis(); - float value = event.getAxisValue(axis); + float origValue = event.getAxisValue(axis); + float value = mControllerMappingHelper.scaleAxis(input, axis, origValue); if (Math.abs(value) > 0.5f) { - numMovedAxis++; - lastMovedRange = range; - lastMovedDir = value < 0.0f ? '-' : '+'; + // It is common to have multiple axis with the same physical input. For example, + // shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE. + // To handle this, we ignore an axis motion that's the exact same as a motion + // we already saw. This way, we ignore axis with two names, but catch the case + // where a joystick is moved in two directions. + // ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html + if (value != axisMoveValue) + { + axisMoveValue = value; + numMovedAxis++; + lastMovedRange = range; + lastMovedDir = value < 0.0f ? '-' : '+'; + } } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ControllerMappingHelper.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ControllerMappingHelper.java new file mode 100644 index 0000000000..1cdc07604a --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ControllerMappingHelper.java @@ -0,0 +1,62 @@ +package org.dolphinemu.dolphinemu.utils; + +import android.view.InputDevice; +import android.view.KeyEvent; +import android.view.MotionEvent; + +/** Some controllers have incorrect mappings. This class has special-case fixes for them. */ +public class ControllerMappingHelper +{ + /** Some controllers report extra button presses that can be ignored. */ + public boolean shouldKeyBeIgnored(InputDevice inputDevice, int keyCode) + { + if (isDualShock4(inputDevice)) { + // The two analog triggers generate analog motion events as well as a keycode. + // We always prefer to use the analog values, so throw away the button press + // Even though the triggers are L/R2, without mappings they generate L/R1 events. + return keyCode == KeyEvent.KEYCODE_BUTTON_L1 || keyCode == KeyEvent.KEYCODE_BUTTON_R1; + } + return false; + } + + /** Scale an axis to be zero-centered with a proper range. */ + public float scaleAxis(InputDevice inputDevice, int axis, float value) + { + if (isDualShock4(inputDevice)) + { + // Android doesn't have correct mappings for this controller's triggers. It reports them + // as RX & RY, centered at -1.0, and with a range of [-1.0, 1.0] + // Scale them to properly zero-centered with a range of [0.0, 1.0]. + if (axis == MotionEvent.AXIS_RX || axis == MotionEvent.AXIS_RY) + { + return (value + 1) / 2.0f; + } + } + else if (isXboxOneWireless(inputDevice)) + { + // Same as the DualShock 4, the mappings are missing. + if (axis == MotionEvent.AXIS_Z || axis == MotionEvent.AXIS_RZ) + { + return (value + 1) / 2.0f; + } + if (axis == MotionEvent.AXIS_GENERIC_1) + { + // This axis is stuck at ~.5. Ignore it. + return 0.0f; + } + } + return value; + } + + private boolean isDualShock4(InputDevice inputDevice) + { + // Sony DualShock 4 controller + return inputDevice.getVendorId() == 0x54c && inputDevice.getProductId() == 0x9cc; + } + + private boolean isXboxOneWireless(InputDevice inputDevice) + { + // Microsoft Xbox One controller + return inputDevice.getVendorId() == 0x45e && inputDevice.getProductId() == 0x2e0; + } +} |
