|
|
|
|
@@ -1,7 +1,10 @@
|
|
|
|
|
package com.limelight.binding.input;
|
|
|
|
|
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
|
import android.hardware.input.InputManager;
|
|
|
|
|
import android.os.SystemClock;
|
|
|
|
|
import android.view.InputDevice;
|
|
|
|
|
import android.view.KeyEvent;
|
|
|
|
|
@@ -13,26 +16,10 @@ import com.limelight.nvstream.input.ControllerPacket;
|
|
|
|
|
import com.limelight.ui.GameGestures;
|
|
|
|
|
import com.limelight.utils.Vector2d;
|
|
|
|
|
|
|
|
|
|
public class ControllerHandler {
|
|
|
|
|
private short inputMap = 0x0000;
|
|
|
|
|
private byte leftTrigger = 0x00;
|
|
|
|
|
private byte rightTrigger = 0x00;
|
|
|
|
|
private short rightStickX = 0x0000;
|
|
|
|
|
private short rightStickY = 0x0000;
|
|
|
|
|
private short leftStickX = 0x0000;
|
|
|
|
|
private short leftStickY = 0x0000;
|
|
|
|
|
private int emulatingButtonFlags = 0;
|
|
|
|
|
|
|
|
|
|
// Used for OUYA bumper state tracking since they force all buttons
|
|
|
|
|
// up when the OUYA button goes down. We watch the last time we get
|
|
|
|
|
// a bumper up and compare that to our maximum delay when we receive
|
|
|
|
|
// a Start button press to see if we should activate one of our
|
|
|
|
|
// emulated button combos.
|
|
|
|
|
private long lastLbUpTime = 0;
|
|
|
|
|
private long lastRbUpTime = 0;
|
|
|
|
|
public class ControllerHandler implements InputManager.InputDeviceListener {
|
|
|
|
|
|
|
|
|
|
private static final int MAXIMUM_BUMPER_UP_DELAY_MS = 100;
|
|
|
|
|
|
|
|
|
|
private long startDownTime = 0;
|
|
|
|
|
private static final int START_DOWN_TIME_KEYB_MS = 750;
|
|
|
|
|
|
|
|
|
|
private static final int MINIMUM_BUTTON_DOWN_TIME_MS = 25;
|
|
|
|
|
@@ -45,17 +32,21 @@ public class ControllerHandler {
|
|
|
|
|
|
|
|
|
|
private Vector2d inputVector = new Vector2d();
|
|
|
|
|
|
|
|
|
|
private HashMap<String, ControllerMapping> mappings = new HashMap<String, ControllerMapping>();
|
|
|
|
|
private HashMap<String, ControllerContext> contexts = new HashMap<String, ControllerContext>();
|
|
|
|
|
|
|
|
|
|
private NvConnection conn;
|
|
|
|
|
private double stickDeadzone;
|
|
|
|
|
private final ControllerMapping defaultMapping = new ControllerMapping();
|
|
|
|
|
private final ControllerContext defaultContext = new ControllerContext();
|
|
|
|
|
private GameGestures gestures;
|
|
|
|
|
private boolean hasGameController;
|
|
|
|
|
|
|
|
|
|
private boolean multiControllerEnabled;
|
|
|
|
|
private short currentControllers;
|
|
|
|
|
|
|
|
|
|
public ControllerHandler(NvConnection conn, GameGestures gestures, int deadzonePercentage) {
|
|
|
|
|
public ControllerHandler(NvConnection conn, GameGestures gestures, boolean multiControllerEnabled, int deadzonePercentage) {
|
|
|
|
|
this.conn = conn;
|
|
|
|
|
this.gestures = gestures;
|
|
|
|
|
this.multiControllerEnabled = multiControllerEnabled;
|
|
|
|
|
|
|
|
|
|
// HACK: For now we're hardcoding a 10% deadzone. Some deadzone
|
|
|
|
|
// is required for controller batching support to work.
|
|
|
|
|
@@ -82,15 +73,16 @@ public class ControllerHandler {
|
|
|
|
|
|
|
|
|
|
this.stickDeadzone = (double)deadzonePercentage / 100.0;
|
|
|
|
|
|
|
|
|
|
// Initialize the default mapping for events with no device
|
|
|
|
|
defaultMapping.leftStickXAxis = MotionEvent.AXIS_X;
|
|
|
|
|
defaultMapping.leftStickYAxis = MotionEvent.AXIS_Y;
|
|
|
|
|
defaultMapping.leftStickDeadzoneRadius = (float) stickDeadzone;
|
|
|
|
|
defaultMapping.rightStickXAxis = MotionEvent.AXIS_Z;
|
|
|
|
|
defaultMapping.rightStickYAxis = MotionEvent.AXIS_RZ;
|
|
|
|
|
defaultMapping.rightStickDeadzoneRadius = (float) stickDeadzone;
|
|
|
|
|
defaultMapping.leftTriggerAxis = MotionEvent.AXIS_BRAKE;
|
|
|
|
|
defaultMapping.rightTriggerAxis = MotionEvent.AXIS_GAS;
|
|
|
|
|
// Initialize the default context for events with no device
|
|
|
|
|
defaultContext.leftStickXAxis = MotionEvent.AXIS_X;
|
|
|
|
|
defaultContext.leftStickYAxis = MotionEvent.AXIS_Y;
|
|
|
|
|
defaultContext.leftStickDeadzoneRadius = (float) stickDeadzone;
|
|
|
|
|
defaultContext.rightStickXAxis = MotionEvent.AXIS_Z;
|
|
|
|
|
defaultContext.rightStickYAxis = MotionEvent.AXIS_RZ;
|
|
|
|
|
defaultContext.rightStickDeadzoneRadius = (float) stickDeadzone;
|
|
|
|
|
defaultContext.leftTriggerAxis = MotionEvent.AXIS_BRAKE;
|
|
|
|
|
defaultContext.rightTriggerAxis = MotionEvent.AXIS_GAS;
|
|
|
|
|
defaultContext.controllerNumber = (short) 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static InputDevice.MotionRange getMotionRangeForJoystickAxis(InputDevice dev, int axis) {
|
|
|
|
|
@@ -105,20 +97,64 @@ public class ControllerHandler {
|
|
|
|
|
|
|
|
|
|
return range;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private short assignNewControllerNumber() {
|
|
|
|
|
for (short i = 0; i < 4; i++) {
|
|
|
|
|
if ((currentControllers & (1 << i)) == 0) {
|
|
|
|
|
// Found an unused controller value
|
|
|
|
|
currentControllers |= (1 << i);
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onInputDeviceAdded(int deviceId) {
|
|
|
|
|
// Nothing happening here yet
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onInputDeviceRemoved(int deviceId) {
|
|
|
|
|
for (Map.Entry<String, ControllerContext> device : contexts.entrySet()) {
|
|
|
|
|
if (device.getValue().id == deviceId) {
|
|
|
|
|
LimeLog.info("Removed controller: "+device.getValue().name);
|
|
|
|
|
releaseControllerNumber(device.getValue().controllerNumber);
|
|
|
|
|
contexts.remove(device.getKey());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onInputDeviceChanged(int deviceId) {
|
|
|
|
|
// Remove and re-add
|
|
|
|
|
onInputDeviceRemoved(deviceId);
|
|
|
|
|
onInputDeviceAdded(deviceId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void releaseControllerNumber(int controllerNumber) {
|
|
|
|
|
LimeLog.info("Controller number "+controllerNumber+" is now available");
|
|
|
|
|
currentControllers &= ~(1 << controllerNumber);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ControllerMapping createMappingForDevice(InputDevice dev) {
|
|
|
|
|
ControllerMapping mapping = new ControllerMapping();
|
|
|
|
|
private ControllerContext createContextForDevice(InputDevice dev) {
|
|
|
|
|
ControllerContext context = new ControllerContext();
|
|
|
|
|
String devName = dev.getName();
|
|
|
|
|
|
|
|
|
|
LimeLog.info("Creating controller mapping for device: "+devName);
|
|
|
|
|
LimeLog.info("Creating controller context for device: "+devName);
|
|
|
|
|
|
|
|
|
|
mapping.leftStickXAxis = MotionEvent.AXIS_X;
|
|
|
|
|
mapping.leftStickYAxis = MotionEvent.AXIS_Y;
|
|
|
|
|
if (getMotionRangeForJoystickAxis(dev, mapping.leftStickXAxis) != null &&
|
|
|
|
|
getMotionRangeForJoystickAxis(dev, mapping.leftStickYAxis) != null) {
|
|
|
|
|
context.name = devName;
|
|
|
|
|
context.id = dev.getId();
|
|
|
|
|
|
|
|
|
|
context.leftStickXAxis = MotionEvent.AXIS_X;
|
|
|
|
|
context.leftStickYAxis = MotionEvent.AXIS_Y;
|
|
|
|
|
if (getMotionRangeForJoystickAxis(dev, context.leftStickXAxis) != null &&
|
|
|
|
|
getMotionRangeForJoystickAxis(dev, context.leftStickYAxis) != null) {
|
|
|
|
|
// This is a gamepad
|
|
|
|
|
hasGameController = true;
|
|
|
|
|
mapping.hasJoystickAxes = true;
|
|
|
|
|
context.hasJoystickAxes = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InputDevice.MotionRange leftTriggerRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_LTRIGGER);
|
|
|
|
|
@@ -128,14 +164,14 @@ public class ControllerHandler {
|
|
|
|
|
if (leftTriggerRange != null && rightTriggerRange != null)
|
|
|
|
|
{
|
|
|
|
|
// Some controllers use LTRIGGER and RTRIGGER (like Ouya)
|
|
|
|
|
mapping.leftTriggerAxis = MotionEvent.AXIS_LTRIGGER;
|
|
|
|
|
mapping.rightTriggerAxis = MotionEvent.AXIS_RTRIGGER;
|
|
|
|
|
context.leftTriggerAxis = MotionEvent.AXIS_LTRIGGER;
|
|
|
|
|
context.rightTriggerAxis = MotionEvent.AXIS_RTRIGGER;
|
|
|
|
|
}
|
|
|
|
|
else if (brakeRange != null && gasRange != null)
|
|
|
|
|
{
|
|
|
|
|
// Others use GAS and BRAKE (like Moga)
|
|
|
|
|
mapping.leftTriggerAxis = MotionEvent.AXIS_BRAKE;
|
|
|
|
|
mapping.rightTriggerAxis = MotionEvent.AXIS_GAS;
|
|
|
|
|
context.leftTriggerAxis = MotionEvent.AXIS_BRAKE;
|
|
|
|
|
context.rightTriggerAxis = MotionEvent.AXIS_GAS;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
@@ -144,34 +180,34 @@ public class ControllerHandler {
|
|
|
|
|
if (rxRange != null && ryRange != null && devName != null) {
|
|
|
|
|
if (devName.contains("Xbox") || devName.contains("XBox") || devName.contains("X-Box")) {
|
|
|
|
|
// Xbox controllers use RX and RY for right stick
|
|
|
|
|
mapping.rightStickXAxis = MotionEvent.AXIS_RX;
|
|
|
|
|
mapping.rightStickYAxis = MotionEvent.AXIS_RY;
|
|
|
|
|
context.rightStickXAxis = MotionEvent.AXIS_RX;
|
|
|
|
|
context.rightStickYAxis = MotionEvent.AXIS_RY;
|
|
|
|
|
|
|
|
|
|
// Xbox controllers use Z and RZ for triggers
|
|
|
|
|
mapping.leftTriggerAxis = MotionEvent.AXIS_Z;
|
|
|
|
|
mapping.rightTriggerAxis = MotionEvent.AXIS_RZ;
|
|
|
|
|
mapping.triggersIdleNegative = true;
|
|
|
|
|
mapping.isXboxController = true;
|
|
|
|
|
context.leftTriggerAxis = MotionEvent.AXIS_Z;
|
|
|
|
|
context.rightTriggerAxis = MotionEvent.AXIS_RZ;
|
|
|
|
|
context.triggersIdleNegative = true;
|
|
|
|
|
context.isXboxController = true;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// DS4 controller uses RX and RY for triggers
|
|
|
|
|
mapping.leftTriggerAxis = MotionEvent.AXIS_RX;
|
|
|
|
|
mapping.rightTriggerAxis = MotionEvent.AXIS_RY;
|
|
|
|
|
mapping.triggersIdleNegative = true;
|
|
|
|
|
context.leftTriggerAxis = MotionEvent.AXIS_RX;
|
|
|
|
|
context.rightTriggerAxis = MotionEvent.AXIS_RY;
|
|
|
|
|
context.triggersIdleNegative = true;
|
|
|
|
|
|
|
|
|
|
mapping.isDualShock4 = true;
|
|
|
|
|
context.isDualShock4 = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.rightStickXAxis == -1 && mapping.rightStickYAxis == -1) {
|
|
|
|
|
if (context.rightStickXAxis == -1 && context.rightStickYAxis == -1) {
|
|
|
|
|
InputDevice.MotionRange zRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_Z);
|
|
|
|
|
InputDevice.MotionRange rzRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RZ);
|
|
|
|
|
|
|
|
|
|
// Most other controllers use Z and RZ for the right stick
|
|
|
|
|
if (zRange != null && rzRange != null) {
|
|
|
|
|
mapping.rightStickXAxis = MotionEvent.AXIS_Z;
|
|
|
|
|
mapping.rightStickYAxis = MotionEvent.AXIS_RZ;
|
|
|
|
|
context.rightStickXAxis = MotionEvent.AXIS_Z;
|
|
|
|
|
context.rightStickYAxis = MotionEvent.AXIS_RZ;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
InputDevice.MotionRange rxRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RX);
|
|
|
|
|
@@ -179,8 +215,8 @@ public class ControllerHandler {
|
|
|
|
|
|
|
|
|
|
// Try RX and RY now
|
|
|
|
|
if (rxRange != null && ryRange != null) {
|
|
|
|
|
mapping.rightStickXAxis = MotionEvent.AXIS_RX;
|
|
|
|
|
mapping.rightStickYAxis = MotionEvent.AXIS_RY;
|
|
|
|
|
context.rightStickXAxis = MotionEvent.AXIS_RX;
|
|
|
|
|
context.rightStickYAxis = MotionEvent.AXIS_RY;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -189,30 +225,30 @@ public class ControllerHandler {
|
|
|
|
|
InputDevice.MotionRange hatXRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_HAT_X);
|
|
|
|
|
InputDevice.MotionRange hatYRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_HAT_Y);
|
|
|
|
|
if (hatXRange != null && hatYRange != null) {
|
|
|
|
|
mapping.hatXAxis = MotionEvent.AXIS_HAT_X;
|
|
|
|
|
mapping.hatYAxis = MotionEvent.AXIS_HAT_Y;
|
|
|
|
|
context.hatXAxis = MotionEvent.AXIS_HAT_X;
|
|
|
|
|
context.hatYAxis = MotionEvent.AXIS_HAT_Y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) {
|
|
|
|
|
mapping.leftStickDeadzoneRadius = (float) stickDeadzone;
|
|
|
|
|
if (context.leftStickXAxis != -1 && context.leftStickYAxis != -1) {
|
|
|
|
|
context.leftStickDeadzoneRadius = (float) stickDeadzone;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) {
|
|
|
|
|
mapping.rightStickDeadzoneRadius = (float) stickDeadzone;
|
|
|
|
|
if (context.rightStickXAxis != -1 && context.rightStickYAxis != -1) {
|
|
|
|
|
context.rightStickDeadzoneRadius = (float) stickDeadzone;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.leftTriggerAxis != -1 && mapping.rightTriggerAxis != -1) {
|
|
|
|
|
InputDevice.MotionRange ltRange = getMotionRangeForJoystickAxis(dev, mapping.leftTriggerAxis);
|
|
|
|
|
InputDevice.MotionRange rtRange = getMotionRangeForJoystickAxis(dev, mapping.rightTriggerAxis);
|
|
|
|
|
if (context.leftTriggerAxis != -1 && context.rightTriggerAxis != -1) {
|
|
|
|
|
InputDevice.MotionRange ltRange = getMotionRangeForJoystickAxis(dev, context.leftTriggerAxis);
|
|
|
|
|
InputDevice.MotionRange rtRange = getMotionRangeForJoystickAxis(dev, context.rightTriggerAxis);
|
|
|
|
|
|
|
|
|
|
// It's important to have a valid deadzone so controller packet batching works properly
|
|
|
|
|
mapping.triggerDeadzone = Math.max(Math.abs(ltRange.getFlat()), Math.abs(rtRange.getFlat()));
|
|
|
|
|
context.triggerDeadzone = Math.max(Math.abs(ltRange.getFlat()), Math.abs(rtRange.getFlat()));
|
|
|
|
|
|
|
|
|
|
// For triggers without (valid) deadzones, we'll use 13% (around XInput's default)
|
|
|
|
|
if (mapping.triggerDeadzone < 0.13f ||
|
|
|
|
|
mapping.triggerDeadzone > 0.30f)
|
|
|
|
|
if (context.triggerDeadzone < 0.13f ||
|
|
|
|
|
context.triggerDeadzone > 0.30f)
|
|
|
|
|
{
|
|
|
|
|
mapping.triggerDeadzone = 0.13f;
|
|
|
|
|
context.triggerDeadzone = 0.13f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -226,71 +262,85 @@ public class ControllerHandler {
|
|
|
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
|
|
|
|
boolean[] hasStartKey = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_MENU, 0);
|
|
|
|
|
if (!hasStartKey[0] && !hasStartKey[1]) {
|
|
|
|
|
mapping.backIsStart = true;
|
|
|
|
|
context.backIsStart = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The ASUS Gamepad has triggers that sit far forward and are prone to false presses
|
|
|
|
|
// so we increase the deadzone on them to minimize this
|
|
|
|
|
mapping.triggerDeadzone = 0.30f;
|
|
|
|
|
context.triggerDeadzone = 0.30f;
|
|
|
|
|
}
|
|
|
|
|
// Classify this device as a remote by name
|
|
|
|
|
else if (devName.contains("Fire TV Remote") || devName.contains("Nexus Remote")) {
|
|
|
|
|
// It's only a remote if it doesn't any sticks
|
|
|
|
|
if (!mapping.hasJoystickAxes) {
|
|
|
|
|
mapping.isRemote = true;
|
|
|
|
|
if (!context.hasJoystickAxes) {
|
|
|
|
|
context.isRemote = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// NYKO Playpad has a fake hat that mimics the left stick for some reason
|
|
|
|
|
else if (devName.contains("NYKO PLAYPAD")) {
|
|
|
|
|
mapping.hatXAxis = -1;
|
|
|
|
|
mapping.hatYAxis = -1;
|
|
|
|
|
context.hatXAxis = -1;
|
|
|
|
|
context.hatYAxis = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LimeLog.info("Analog stick deadzone: "+mapping.leftStickDeadzoneRadius+" "+mapping.rightStickDeadzoneRadius);
|
|
|
|
|
LimeLog.info("Trigger deadzone: "+mapping.triggerDeadzone);
|
|
|
|
|
|
|
|
|
|
return mapping;
|
|
|
|
|
LimeLog.info("Analog stick deadzone: "+context.leftStickDeadzoneRadius+" "+context.rightStickDeadzoneRadius);
|
|
|
|
|
LimeLog.info("Trigger deadzone: "+context.triggerDeadzone);
|
|
|
|
|
|
|
|
|
|
if (devName != null && devName.equals("gpio-keys")) {
|
|
|
|
|
// This is the back button on Shield portable consoles
|
|
|
|
|
context.controllerNumber = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (multiControllerEnabled) {
|
|
|
|
|
context.controllerNumber = assignNewControllerNumber();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
context.controllerNumber = 0;
|
|
|
|
|
}
|
|
|
|
|
LimeLog.info("Assigned as controller "+context.controllerNumber);
|
|
|
|
|
|
|
|
|
|
return context;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ControllerMapping getMappingForDevice(InputDevice dev) {
|
|
|
|
|
// Unknown devices use the default mapping
|
|
|
|
|
private ControllerContext getContextForDevice(InputDevice dev) {
|
|
|
|
|
// Unknown devices use the default context
|
|
|
|
|
if (dev == null) {
|
|
|
|
|
return defaultMapping;
|
|
|
|
|
return defaultContext;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String descriptor = dev.getDescriptor();
|
|
|
|
|
|
|
|
|
|
// Return the existing mapping if it exists
|
|
|
|
|
ControllerMapping mapping = mappings.get(descriptor);
|
|
|
|
|
if (mapping != null) {
|
|
|
|
|
return mapping;
|
|
|
|
|
// Return the existing context if it exists
|
|
|
|
|
ControllerContext context = contexts.get(descriptor);
|
|
|
|
|
if (context != null) {
|
|
|
|
|
return context;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise create a new mapping
|
|
|
|
|
mapping = createMappingForDevice(dev);
|
|
|
|
|
mappings.put(descriptor, mapping);
|
|
|
|
|
// Otherwise create a new context
|
|
|
|
|
context = createContextForDevice(dev);
|
|
|
|
|
contexts.put(descriptor, context);
|
|
|
|
|
|
|
|
|
|
return mapping;
|
|
|
|
|
return context;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void sendControllerInputPacket() {
|
|
|
|
|
conn.sendControllerInput(inputMap, leftTrigger, rightTrigger,
|
|
|
|
|
leftStickX, leftStickY, rightStickX, rightStickY);
|
|
|
|
|
private void sendControllerInputPacket(ControllerContext context) {
|
|
|
|
|
conn.sendControllerInput(context.controllerNumber, context.inputMap,
|
|
|
|
|
context.leftTrigger, context.rightTrigger,
|
|
|
|
|
context.leftStickX, context.leftStickY,
|
|
|
|
|
context.rightStickX, context.rightStickY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return a valid keycode, 0 to consume, or -1 to not consume the event
|
|
|
|
|
// Device MAY BE NULL
|
|
|
|
|
private int handleRemapping(ControllerMapping mapping, KeyEvent event) {
|
|
|
|
|
private int handleRemapping(ControllerContext context, KeyEvent event) {
|
|
|
|
|
// For remotes, don't capture the back button
|
|
|
|
|
if (mapping.isRemote) {
|
|
|
|
|
if (context.isRemote) {
|
|
|
|
|
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.isDualShock4) {
|
|
|
|
|
if (context.isDualShock4) {
|
|
|
|
|
switch (event.getKeyCode()) {
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_Y:
|
|
|
|
|
return KeyEvent.KEYCODE_BUTTON_L1;
|
|
|
|
|
@@ -329,7 +379,7 @@ public class ControllerHandler {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.hatXAxis != -1 && mapping.hatYAxis != -1) {
|
|
|
|
|
if (context.hatXAxis != -1 && context.hatYAxis != -1) {
|
|
|
|
|
switch (event.getKeyCode()) {
|
|
|
|
|
// These are duplicate dpad events for hat input
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_LEFT:
|
|
|
|
|
@@ -340,9 +390,9 @@ public class ControllerHandler {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (mapping.hatXAxis == -1 &&
|
|
|
|
|
mapping.hatYAxis == -1 &&
|
|
|
|
|
mapping.isXboxController &&
|
|
|
|
|
else if (context.hatXAxis == -1 &&
|
|
|
|
|
context.hatYAxis == -1 &&
|
|
|
|
|
context.isXboxController &&
|
|
|
|
|
event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) {
|
|
|
|
|
// If there's not a proper Xbox controller mapping, we'll translate the raw d-pad
|
|
|
|
|
// scan codes into proper key codes
|
|
|
|
|
@@ -375,9 +425,9 @@ public class ControllerHandler {
|
|
|
|
|
if (keyCode == KeyEvent.KEYCODE_BUTTON_START ||
|
|
|
|
|
keyCode == KeyEvent.KEYCODE_MENU) {
|
|
|
|
|
// Ensure that we never use back as start if we have a real start
|
|
|
|
|
mapping.backIsStart = false;
|
|
|
|
|
context.backIsStart = false;
|
|
|
|
|
}
|
|
|
|
|
else if (mapping.backIsStart && keyCode == KeyEvent.KEYCODE_BACK) {
|
|
|
|
|
else if (context.backIsStart && keyCode == KeyEvent.KEYCODE_BACK) {
|
|
|
|
|
// Emulate the start button with back
|
|
|
|
|
return KeyEvent.KEYCODE_BUTTON_START;
|
|
|
|
|
}
|
|
|
|
|
@@ -402,101 +452,101 @@ public class ControllerHandler {
|
|
|
|
|
// evaluates the deadzone.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void handleAxisSet(ControllerMapping mapping, float lsX, float lsY, float rsX,
|
|
|
|
|
private void handleAxisSet(ControllerContext context, float lsX, float lsY, float rsX,
|
|
|
|
|
float rsY, float lt, float rt, float hatX, float hatY) {
|
|
|
|
|
|
|
|
|
|
if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) {
|
|
|
|
|
if (context.leftStickXAxis != -1 && context.leftStickYAxis != -1) {
|
|
|
|
|
Vector2d leftStickVector = populateCachedVector(lsX, lsY);
|
|
|
|
|
|
|
|
|
|
handleDeadZone(leftStickVector, mapping.leftStickDeadzoneRadius);
|
|
|
|
|
handleDeadZone(leftStickVector, context.leftStickDeadzoneRadius);
|
|
|
|
|
|
|
|
|
|
leftStickX = (short) (leftStickVector.getX() * 0x7FFE);
|
|
|
|
|
leftStickY = (short) (-leftStickVector.getY() * 0x7FFE);
|
|
|
|
|
context.leftStickX = (short) (leftStickVector.getX() * 0x7FFE);
|
|
|
|
|
context.leftStickY = (short) (-leftStickVector.getY() * 0x7FFE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) {
|
|
|
|
|
if (context.rightStickXAxis != -1 && context.rightStickYAxis != -1) {
|
|
|
|
|
Vector2d rightStickVector = populateCachedVector(rsX, rsY);
|
|
|
|
|
|
|
|
|
|
handleDeadZone(rightStickVector, mapping.rightStickDeadzoneRadius);
|
|
|
|
|
handleDeadZone(rightStickVector, context.rightStickDeadzoneRadius);
|
|
|
|
|
|
|
|
|
|
rightStickX = (short) (rightStickVector.getX() * 0x7FFE);
|
|
|
|
|
rightStickY = (short) (-rightStickVector.getY() * 0x7FFE);
|
|
|
|
|
context.rightStickX = (short) (rightStickVector.getX() * 0x7FFE);
|
|
|
|
|
context.rightStickY = (short) (-rightStickVector.getY() * 0x7FFE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.leftTriggerAxis != -1 && mapping.rightTriggerAxis != -1) {
|
|
|
|
|
if (mapping.triggersIdleNegative) {
|
|
|
|
|
if (context.leftTriggerAxis != -1 && context.rightTriggerAxis != -1) {
|
|
|
|
|
if (context.triggersIdleNegative) {
|
|
|
|
|
lt = (lt + 1) / 2;
|
|
|
|
|
rt = (rt + 1) / 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lt <= mapping.triggerDeadzone) {
|
|
|
|
|
if (lt <= context.triggerDeadzone) {
|
|
|
|
|
lt = 0;
|
|
|
|
|
}
|
|
|
|
|
if (rt <= mapping.triggerDeadzone) {
|
|
|
|
|
if (rt <= context.triggerDeadzone) {
|
|
|
|
|
rt = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leftTrigger = (byte)(lt * 0xFF);
|
|
|
|
|
rightTrigger = (byte)(rt * 0xFF);
|
|
|
|
|
context.leftTrigger = (byte)(lt * 0xFF);
|
|
|
|
|
context.rightTrigger = (byte)(rt * 0xFF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.hatXAxis != -1 && mapping.hatYAxis != -1) {
|
|
|
|
|
inputMap &= ~(ControllerPacket.LEFT_FLAG | ControllerPacket.RIGHT_FLAG);
|
|
|
|
|
if (context.hatXAxis != -1 && context.hatYAxis != -1) {
|
|
|
|
|
context.inputMap &= ~(ControllerPacket.LEFT_FLAG | ControllerPacket.RIGHT_FLAG);
|
|
|
|
|
if (hatX < -0.5) {
|
|
|
|
|
inputMap |= ControllerPacket.LEFT_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.LEFT_FLAG;
|
|
|
|
|
}
|
|
|
|
|
else if (hatX > 0.5) {
|
|
|
|
|
inputMap |= ControllerPacket.RIGHT_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.RIGHT_FLAG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inputMap &= ~(ControllerPacket.UP_FLAG | ControllerPacket.DOWN_FLAG);
|
|
|
|
|
context.inputMap &= ~(ControllerPacket.UP_FLAG | ControllerPacket.DOWN_FLAG);
|
|
|
|
|
if (hatY < -0.5) {
|
|
|
|
|
inputMap |= ControllerPacket.UP_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.UP_FLAG;
|
|
|
|
|
}
|
|
|
|
|
else if (hatY > 0.5) {
|
|
|
|
|
inputMap |= ControllerPacket.DOWN_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.DOWN_FLAG;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sendControllerInputPacket();
|
|
|
|
|
sendControllerInputPacket(context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean handleMotionEvent(MotionEvent event) {
|
|
|
|
|
ControllerMapping mapping = getMappingForDevice(event.getDevice());
|
|
|
|
|
ControllerContext context = getContextForDevice(event.getDevice());
|
|
|
|
|
float lsX = 0, lsY = 0, rsX = 0, rsY = 0, rt = 0, lt = 0, hatX = 0, hatY = 0;
|
|
|
|
|
|
|
|
|
|
// We purposefully ignore the historical values in the motion event as it makes
|
|
|
|
|
// the controller feel sluggish for some users.
|
|
|
|
|
|
|
|
|
|
if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) {
|
|
|
|
|
lsX = event.getAxisValue(mapping.leftStickXAxis);
|
|
|
|
|
lsY = event.getAxisValue(mapping.leftStickYAxis);
|
|
|
|
|
if (context.leftStickXAxis != -1 && context.leftStickYAxis != -1) {
|
|
|
|
|
lsX = event.getAxisValue(context.leftStickXAxis);
|
|
|
|
|
lsY = event.getAxisValue(context.leftStickYAxis);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) {
|
|
|
|
|
rsX = event.getAxisValue(mapping.rightStickXAxis);
|
|
|
|
|
rsY = event.getAxisValue(mapping.rightStickYAxis);
|
|
|
|
|
if (context.rightStickXAxis != -1 && context.rightStickYAxis != -1) {
|
|
|
|
|
rsX = event.getAxisValue(context.rightStickXAxis);
|
|
|
|
|
rsY = event.getAxisValue(context.rightStickYAxis);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.leftTriggerAxis != -1 && mapping.rightTriggerAxis != -1) {
|
|
|
|
|
lt = event.getAxisValue(mapping.leftTriggerAxis);
|
|
|
|
|
rt = event.getAxisValue(mapping.rightTriggerAxis);
|
|
|
|
|
if (context.leftTriggerAxis != -1 && context.rightTriggerAxis != -1) {
|
|
|
|
|
lt = event.getAxisValue(context.leftTriggerAxis);
|
|
|
|
|
rt = event.getAxisValue(context.rightTriggerAxis);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mapping.hatXAxis != -1 && mapping.hatYAxis != -1) {
|
|
|
|
|
if (context.hatXAxis != -1 && context.hatYAxis != -1) {
|
|
|
|
|
hatX = event.getAxisValue(MotionEvent.AXIS_HAT_X);
|
|
|
|
|
hatY = event.getAxisValue(MotionEvent.AXIS_HAT_Y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleAxisSet(mapping, lsX, lsY, rsX, rsY, lt, rt, hatX, hatY);
|
|
|
|
|
handleAxisSet(context, lsX, lsY, rsX, rsY, lt, rt, hatX, hatY);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean handleButtonUp(KeyEvent event) {
|
|
|
|
|
ControllerMapping mapping = getMappingForDevice(event.getDevice());
|
|
|
|
|
ControllerContext context = getContextForDevice(event.getDevice());
|
|
|
|
|
|
|
|
|
|
int keyCode = handleRemapping(mapping, event);
|
|
|
|
|
int keyCode = handleRemapping(context, event);
|
|
|
|
|
if (keyCode == 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
@@ -515,78 +565,78 @@ public class ControllerHandler {
|
|
|
|
|
|
|
|
|
|
switch (keyCode) {
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_MODE:
|
|
|
|
|
inputMap &= ~ControllerPacket.SPECIAL_BUTTON_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.SPECIAL_BUTTON_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_START:
|
|
|
|
|
case KeyEvent.KEYCODE_MENU:
|
|
|
|
|
if (SystemClock.uptimeMillis() - startDownTime > ControllerHandler.START_DOWN_TIME_KEYB_MS) {
|
|
|
|
|
if (SystemClock.uptimeMillis() - context.startDownTime > ControllerHandler.START_DOWN_TIME_KEYB_MS) {
|
|
|
|
|
gestures.showKeyboard();
|
|
|
|
|
}
|
|
|
|
|
inputMap &= ~ControllerPacket.PLAY_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.PLAY_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BACK:
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
|
|
|
|
inputMap &= ~ControllerPacket.BACK_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.BACK_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_LEFT:
|
|
|
|
|
inputMap &= ~ControllerPacket.LEFT_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.LEFT_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
|
|
|
|
inputMap &= ~ControllerPacket.RIGHT_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.RIGHT_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_UP:
|
|
|
|
|
inputMap &= ~ControllerPacket.UP_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.UP_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_DOWN:
|
|
|
|
|
inputMap &= ~ControllerPacket.DOWN_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.DOWN_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_B:
|
|
|
|
|
inputMap &= ~ControllerPacket.B_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.B_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_CENTER:
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_A:
|
|
|
|
|
inputMap &= ~ControllerPacket.A_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.A_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_X:
|
|
|
|
|
inputMap &= ~ControllerPacket.X_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.X_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_Y:
|
|
|
|
|
inputMap &= ~ControllerPacket.Y_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.Y_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_L1:
|
|
|
|
|
inputMap &= ~ControllerPacket.LB_FLAG;
|
|
|
|
|
lastLbUpTime = SystemClock.uptimeMillis();
|
|
|
|
|
context.inputMap &= ~ControllerPacket.LB_FLAG;
|
|
|
|
|
context.lastLbUpTime = SystemClock.uptimeMillis();
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_R1:
|
|
|
|
|
inputMap &= ~ControllerPacket.RB_FLAG;
|
|
|
|
|
lastRbUpTime = SystemClock.uptimeMillis();
|
|
|
|
|
context.inputMap &= ~ControllerPacket.RB_FLAG;
|
|
|
|
|
context.lastRbUpTime = SystemClock.uptimeMillis();
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_THUMBL:
|
|
|
|
|
inputMap &= ~ControllerPacket.LS_CLK_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.LS_CLK_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_THUMBR:
|
|
|
|
|
inputMap &= ~ControllerPacket.RS_CLK_FLAG;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.RS_CLK_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_L2:
|
|
|
|
|
leftTrigger = 0;
|
|
|
|
|
context.leftTrigger = 0;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_R2:
|
|
|
|
|
rightTrigger = 0;
|
|
|
|
|
context.rightTrigger = 0;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if we're emulating the select button
|
|
|
|
|
if ((emulatingButtonFlags & ControllerHandler.EMULATING_SELECT) != 0)
|
|
|
|
|
if ((context.emulatingButtonFlags & ControllerHandler.EMULATING_SELECT) != 0)
|
|
|
|
|
{
|
|
|
|
|
// If either start or LB is up, select comes up too
|
|
|
|
|
if ((inputMap & ControllerPacket.PLAY_FLAG) == 0 ||
|
|
|
|
|
(inputMap & ControllerPacket.LB_FLAG) == 0)
|
|
|
|
|
if ((context.inputMap & ControllerPacket.PLAY_FLAG) == 0 ||
|
|
|
|
|
(context.inputMap & ControllerPacket.LB_FLAG) == 0)
|
|
|
|
|
{
|
|
|
|
|
inputMap &= ~ControllerPacket.BACK_FLAG;
|
|
|
|
|
|
|
|
|
|
emulatingButtonFlags &= ~ControllerHandler.EMULATING_SELECT;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.BACK_FLAG;
|
|
|
|
|
|
|
|
|
|
context.emulatingButtonFlags &= ~ControllerHandler.EMULATING_SELECT;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(EMULATED_SELECT_UP_DELAY_MS);
|
|
|
|
|
@@ -595,16 +645,16 @@ public class ControllerHandler {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if we're emulating the special button
|
|
|
|
|
if ((emulatingButtonFlags & ControllerHandler.EMULATING_SPECIAL) != 0)
|
|
|
|
|
if ((context.emulatingButtonFlags & ControllerHandler.EMULATING_SPECIAL) != 0)
|
|
|
|
|
{
|
|
|
|
|
// If either start or select and RB is up, the special button comes up too
|
|
|
|
|
if ((inputMap & ControllerPacket.PLAY_FLAG) == 0 ||
|
|
|
|
|
((inputMap & ControllerPacket.BACK_FLAG) == 0 &&
|
|
|
|
|
(inputMap & ControllerPacket.RB_FLAG) == 0))
|
|
|
|
|
if ((context.inputMap & ControllerPacket.PLAY_FLAG) == 0 ||
|
|
|
|
|
((context.inputMap & ControllerPacket.BACK_FLAG) == 0 &&
|
|
|
|
|
(context.inputMap & ControllerPacket.RB_FLAG) == 0))
|
|
|
|
|
{
|
|
|
|
|
inputMap &= ~ControllerPacket.SPECIAL_BUTTON_FLAG;
|
|
|
|
|
|
|
|
|
|
emulatingButtonFlags &= ~ControllerHandler.EMULATING_SPECIAL;
|
|
|
|
|
context.inputMap &= ~ControllerPacket.SPECIAL_BUTTON_FLAG;
|
|
|
|
|
|
|
|
|
|
context.emulatingButtonFlags &= ~ControllerHandler.EMULATING_SPECIAL;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(EMULATED_SPECIAL_UP_DELAY_MS);
|
|
|
|
|
@@ -612,112 +662,115 @@ public class ControllerHandler {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sendControllerInputPacket();
|
|
|
|
|
sendControllerInputPacket(context);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean handleButtonDown(KeyEvent event) {
|
|
|
|
|
ControllerMapping mapping = getMappingForDevice(event.getDevice());
|
|
|
|
|
ControllerContext context = getContextForDevice(event.getDevice());
|
|
|
|
|
|
|
|
|
|
int keyCode = handleRemapping(mapping, event);
|
|
|
|
|
int keyCode = handleRemapping(context, event);
|
|
|
|
|
if (keyCode == 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (keyCode) {
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_MODE:
|
|
|
|
|
inputMap |= ControllerPacket.SPECIAL_BUTTON_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.SPECIAL_BUTTON_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_START:
|
|
|
|
|
case KeyEvent.KEYCODE_MENU:
|
|
|
|
|
if (event.getRepeatCount() == 0) {
|
|
|
|
|
startDownTime = SystemClock.uptimeMillis();
|
|
|
|
|
context.startDownTime = SystemClock.uptimeMillis();
|
|
|
|
|
}
|
|
|
|
|
inputMap |= ControllerPacket.PLAY_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.PLAY_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BACK:
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
|
|
|
|
inputMap |= ControllerPacket.BACK_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.BACK_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_LEFT:
|
|
|
|
|
inputMap |= ControllerPacket.LEFT_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.LEFT_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
|
|
|
|
inputMap |= ControllerPacket.RIGHT_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.RIGHT_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_UP:
|
|
|
|
|
inputMap |= ControllerPacket.UP_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.UP_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_DOWN:
|
|
|
|
|
inputMap |= ControllerPacket.DOWN_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.DOWN_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_B:
|
|
|
|
|
inputMap |= ControllerPacket.B_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.B_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_DPAD_CENTER:
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_A:
|
|
|
|
|
inputMap |= ControllerPacket.A_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.A_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_X:
|
|
|
|
|
inputMap |= ControllerPacket.X_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.X_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_Y:
|
|
|
|
|
inputMap |= ControllerPacket.Y_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.Y_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_L1:
|
|
|
|
|
inputMap |= ControllerPacket.LB_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.LB_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_R1:
|
|
|
|
|
inputMap |= ControllerPacket.RB_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.RB_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_THUMBL:
|
|
|
|
|
inputMap |= ControllerPacket.LS_CLK_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.LS_CLK_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_THUMBR:
|
|
|
|
|
inputMap |= ControllerPacket.RS_CLK_FLAG;
|
|
|
|
|
context.inputMap |= ControllerPacket.RS_CLK_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_L2:
|
|
|
|
|
leftTrigger = (byte)0xFF;
|
|
|
|
|
context.leftTrigger = (byte)0xFF;
|
|
|
|
|
break;
|
|
|
|
|
case KeyEvent.KEYCODE_BUTTON_R2:
|
|
|
|
|
rightTrigger = (byte)0xFF;
|
|
|
|
|
context.rightTrigger = (byte)0xFF;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start+LB acts like select for controllers with one button
|
|
|
|
|
if ((inputMap & ControllerPacket.PLAY_FLAG) != 0 &&
|
|
|
|
|
((inputMap & ControllerPacket.LB_FLAG) != 0 ||
|
|
|
|
|
SystemClock.uptimeMillis() - lastLbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS))
|
|
|
|
|
if ((context.inputMap & ControllerPacket.PLAY_FLAG) != 0 &&
|
|
|
|
|
((context.inputMap & ControllerPacket.LB_FLAG) != 0 ||
|
|
|
|
|
SystemClock.uptimeMillis() - context.lastLbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS))
|
|
|
|
|
{
|
|
|
|
|
inputMap &= ~(ControllerPacket.PLAY_FLAG | ControllerPacket.LB_FLAG);
|
|
|
|
|
inputMap |= ControllerPacket.BACK_FLAG;
|
|
|
|
|
|
|
|
|
|
emulatingButtonFlags |= ControllerHandler.EMULATING_SELECT;
|
|
|
|
|
context.inputMap &= ~(ControllerPacket.PLAY_FLAG | ControllerPacket.LB_FLAG);
|
|
|
|
|
context.inputMap |= ControllerPacket.BACK_FLAG;
|
|
|
|
|
|
|
|
|
|
context.emulatingButtonFlags |= ControllerHandler.EMULATING_SELECT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We detect select+start or start+RB as the special button combo
|
|
|
|
|
if (((inputMap & ControllerPacket.RB_FLAG) != 0 ||
|
|
|
|
|
(SystemClock.uptimeMillis() - lastRbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS) ||
|
|
|
|
|
(inputMap & ControllerPacket.BACK_FLAG) != 0) &&
|
|
|
|
|
(inputMap & ControllerPacket.PLAY_FLAG) != 0)
|
|
|
|
|
if (((context.inputMap & ControllerPacket.RB_FLAG) != 0 ||
|
|
|
|
|
(SystemClock.uptimeMillis() - context.lastRbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS) ||
|
|
|
|
|
(context.inputMap & ControllerPacket.BACK_FLAG) != 0) &&
|
|
|
|
|
(context.inputMap & ControllerPacket.PLAY_FLAG) != 0)
|
|
|
|
|
{
|
|
|
|
|
inputMap &= ~(ControllerPacket.BACK_FLAG | ControllerPacket.PLAY_FLAG | ControllerPacket.RB_FLAG);
|
|
|
|
|
inputMap |= ControllerPacket.SPECIAL_BUTTON_FLAG;
|
|
|
|
|
|
|
|
|
|
emulatingButtonFlags |= ControllerHandler.EMULATING_SPECIAL;
|
|
|
|
|
context.inputMap &= ~(ControllerPacket.BACK_FLAG | ControllerPacket.PLAY_FLAG | ControllerPacket.RB_FLAG);
|
|
|
|
|
context.inputMap |= ControllerPacket.SPECIAL_BUTTON_FLAG;
|
|
|
|
|
|
|
|
|
|
context.emulatingButtonFlags |= ControllerHandler.EMULATING_SPECIAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send a new input packet if this is the first instance of a button down event
|
|
|
|
|
// or anytime if we're emulating a button
|
|
|
|
|
if (event.getRepeatCount() == 0 || emulatingButtonFlags != 0) {
|
|
|
|
|
sendControllerInputPacket();
|
|
|
|
|
if (event.getRepeatCount() == 0 || context.emulatingButtonFlags != 0) {
|
|
|
|
|
sendControllerInputPacket(context);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ControllerMapping {
|
|
|
|
|
|
|
|
|
|
class ControllerContext {
|
|
|
|
|
public String name;
|
|
|
|
|
public int id;
|
|
|
|
|
|
|
|
|
|
public int leftStickXAxis = -1;
|
|
|
|
|
public int leftStickYAxis = -1;
|
|
|
|
|
public float leftStickDeadzoneRadius;
|
|
|
|
|
@@ -739,5 +792,26 @@ public class ControllerHandler {
|
|
|
|
|
public boolean backIsStart;
|
|
|
|
|
public boolean isRemote;
|
|
|
|
|
public boolean hasJoystickAxes;
|
|
|
|
|
|
|
|
|
|
public short controllerNumber;
|
|
|
|
|
|
|
|
|
|
public short inputMap = 0x0000;
|
|
|
|
|
public byte leftTrigger = 0x00;
|
|
|
|
|
public byte rightTrigger = 0x00;
|
|
|
|
|
public short rightStickX = 0x0000;
|
|
|
|
|
public short rightStickY = 0x0000;
|
|
|
|
|
public short leftStickX = 0x0000;
|
|
|
|
|
public short leftStickY = 0x0000;
|
|
|
|
|
public int emulatingButtonFlags = 0;
|
|
|
|
|
|
|
|
|
|
// Used for OUYA bumper state tracking since they force all buttons
|
|
|
|
|
// up when the OUYA button goes down. We watch the last time we get
|
|
|
|
|
// a bumper up and compare that to our maximum delay when we receive
|
|
|
|
|
// a Start button press to see if we should activate one of our
|
|
|
|
|
// emulated button combos.
|
|
|
|
|
public long lastLbUpTime = 0;
|
|
|
|
|
public long lastRbUpTime = 0;
|
|
|
|
|
|
|
|
|
|
public long startDownTime = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|