Merge pull request #6156 from hackbar/input-fix-trigger

Android: Handle duplicate axis, and ignore known bad axis.
This commit is contained in:
Anthony 2017-11-08 15:31:30 -08:00 committed by GitHub
commit 965b5b6b0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 9 deletions

View File

@ -39,6 +39,7 @@ import org.dolphinemu.dolphinemu.fragments.SaveLoadStateFragment;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter; import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.utils.Animations; 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_GCAdapter;
import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter; import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
import org.dolphinemu.dolphinemu.utils.Log; import org.dolphinemu.dolphinemu.utils.Log;
@ -57,6 +58,7 @@ public final class EmulationActivity extends AppCompatActivity
private EmulationFragment mEmulationFragment; private EmulationFragment mEmulationFragment;
private SharedPreferences mPreferences; private SharedPreferences mPreferences;
private ControllerMappingHelper mControllerMappingHelper;
// So that MainActivity knows which view to invalidate before the return animation. // So that MainActivity knows which view to invalidate before the return animation.
private int mPosition; private int mPosition;
@ -164,6 +166,7 @@ public final class EmulationActivity extends AppCompatActivity
mScreenPath = gameToEmulate.getStringExtra("ScreenPath"); mScreenPath = gameToEmulate.getStringExtra("ScreenPath");
mPosition = gameToEmulate.getIntExtra("GridPosition", -1); mPosition = gameToEmulate.getIntExtra("GridPosition", -1);
mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen"); mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen");
mControllerMappingHelper = new ControllerMappingHelper();
int themeId; int themeId;
if (mDeviceHasTouchScreen) if (mDeviceHasTouchScreen)
@ -729,7 +732,19 @@ public final class EmulationActivity extends AppCompatActivity
for (InputDevice.MotionRange range : motions) 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);
// If the input is still in the "flat" area, that means it's really zero.
// This is used to compensate for imprecision in joysticks.
if (Math.abs(value) > range.getFlat())
{
NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), axis, value);
}
else
{
NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), axis, 0.0f);
}
} }
return true; return true;

View File

@ -9,6 +9,7 @@ import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import org.dolphinemu.dolphinemu.model.settings.view.InputBindingSetting; import org.dolphinemu.dolphinemu.model.settings.view.InputBindingSetting;
import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper;
import org.dolphinemu.dolphinemu.utils.Log; import org.dolphinemu.dolphinemu.utils.Log;
import java.util.List; import java.util.List;
@ -21,6 +22,7 @@ public final class MotionAlertDialog extends AlertDialog
{ {
// The selected input preference // The selected input preference
private final InputBindingSetting setting; private final InputBindingSetting setting;
private final ControllerMappingHelper mControllerMappingHelper;
private boolean mWaitingForEvent = true; private boolean mWaitingForEvent = true;
/** /**
@ -34,6 +36,7 @@ public final class MotionAlertDialog extends AlertDialog
super(context); super(context);
this.setting = setting; this.setting = setting;
this.mControllerMappingHelper = new ControllerMappingHelper();
} }
public boolean onKeyEvent(int keyCode, KeyEvent event) public boolean onKeyEvent(int keyCode, KeyEvent event)
@ -42,8 +45,11 @@ public final class MotionAlertDialog extends AlertDialog
switch (event.getAction()) switch (event.getAction())
{ {
case KeyEvent.ACTION_DOWN: 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; return true;
default: default:
@ -69,13 +75,15 @@ public final class MotionAlertDialog extends AlertDialog
{ {
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0)
return false; return false;
if (event.getAction() != MotionEvent.ACTION_MOVE)
Log.debug("[MotionAlertDialog] Received motion event: " + event.getAction()); return false;
InputDevice input = event.getDevice(); InputDevice input = event.getDevice();
List<InputDevice.MotionRange> motionRanges = input.getMotionRanges(); List<InputDevice.MotionRange> motionRanges = input.getMotionRanges();
int numMovedAxis = 0; int numMovedAxis = 0;
float axisMoveValue = 0.0f;
InputDevice.MotionRange lastMovedRange = null; InputDevice.MotionRange lastMovedRange = null;
char lastMovedDir = '?'; char lastMovedDir = '?';
if (mWaitingForEvent) if (mWaitingForEvent)
@ -84,12 +92,23 @@ public final class MotionAlertDialog extends AlertDialog
for (InputDevice.MotionRange range : motionRanges) for (InputDevice.MotionRange range : motionRanges)
{ {
int axis = range.getAxis(); 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) if (Math.abs(value) > 0.5f)
{ {
numMovedAxis++; // It is common to have multiple axis with the same physical input. For example,
lastMovedRange = range; // shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE.
lastMovedDir = value < 0.0f ? '-' : '+'; // 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 ? '-' : '+';
}
} }
} }

View File

@ -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;
}
}