Merge pull request #6156 from hackbar/input-fix-trigger
Android: Handle duplicate axis, and ignore known bad axis.
This commit is contained in:
commit
965b5b6b0a
|
@ -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;
|
||||||
|
|
|
@ -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 ? '-' : '+';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue