mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-19 02:53:05 +00:00
Add multiple controller support
This commit is contained in:
parent
3a0c1db168
commit
da7904a767
@ -193,7 +193,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
// Initialize the connection
|
||||
conn = new NvConnection(host, uniqueId, Game.this, config, PlatformBinding.getCryptoProvider(this));
|
||||
keybTranslator = new KeyboardTranslator(conn);
|
||||
controllerHandler = new ControllerHandler(conn, this, prefConfig.deadzonePercentage);
|
||||
controllerHandler = new ControllerHandler(conn, this, prefConfig.multiController, prefConfig.deadzonePercentage);
|
||||
|
||||
SurfaceHolder sh = sv.getHolder();
|
||||
if (prefConfig.stretchVideo || !decoderRenderer.isHardwareAccelerated()) {
|
||||
|
@ -14,25 +14,9 @@ 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;
|
||||
|
||||
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 +29,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 nextControllerNumber;
|
||||
|
||||
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 +70,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) {
|
||||
@ -106,19 +95,19 @@ public class ControllerHandler {
|
||||
return range;
|
||||
}
|
||||
|
||||
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.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 +117,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 +133,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 +168,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 +178,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 +215,82 @@ 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 (multiControllerEnabled) {
|
||||
context.controllerNumber = nextControllerNumber;
|
||||
nextControllerNumber = (short)((nextControllerNumber + 1) % 4);
|
||||
}
|
||||
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 +329,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 +340,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 +375,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 +402,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 +515,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 +595,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 +612,112 @@ 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 int leftStickXAxis = -1;
|
||||
public int leftStickYAxis = -1;
|
||||
public float leftStickDeadzoneRadius;
|
||||
@ -739,5 +739,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;
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ public class PreferenceConfiguration {
|
||||
private static final String LANGUAGE_PREF_STRING = "list_languages";
|
||||
private static final String LIST_MODE_PREF_STRING = "checkbox_list_mode";
|
||||
private static final String SMALL_ICONS_PREF_STRING = "checkbox_small_icon_mode";
|
||||
private static final String MULTI_CONTROLLER_PREF_STRING = "checkbox_multi_controller";
|
||||
|
||||
private static final int BITRATE_DEFAULT_720_30 = 5;
|
||||
private static final int BITRATE_DEFAULT_720_60 = 10;
|
||||
@ -32,6 +33,7 @@ public class PreferenceConfiguration {
|
||||
private static final int DEFAULT_DEADZONE = 15;
|
||||
public static final String DEFAULT_LANGUAGE = "default";
|
||||
private static final boolean DEFAULT_LIST_MODE = false;
|
||||
private static final boolean DEFAULT_MULTI_CONTROLLER = true;
|
||||
|
||||
public static final int FORCE_HARDWARE_DECODER = -1;
|
||||
public static final int AUTOSELECT_DECODER = 0;
|
||||
@ -43,7 +45,7 @@ public class PreferenceConfiguration {
|
||||
public int deadzonePercentage;
|
||||
public boolean stretchVideo, enableSops, playHostAudio, disableWarnings;
|
||||
public String language;
|
||||
public boolean listMode, smallIconMode;
|
||||
public boolean listMode, smallIconMode, multiController;
|
||||
|
||||
public static int getDefaultBitrate(String resFpsString) {
|
||||
if (resFpsString.equals("720p30")) {
|
||||
@ -156,6 +158,7 @@ public class PreferenceConfiguration {
|
||||
config.playHostAudio = prefs.getBoolean(HOST_AUDIO_PREF_STRING, DEFAULT_HOST_AUDIO);
|
||||
config.listMode = prefs.getBoolean(LIST_MODE_PREF_STRING, DEFAULT_LIST_MODE);
|
||||
config.smallIconMode = prefs.getBoolean(SMALL_ICONS_PREF_STRING, getDefaultSmallMode(context));
|
||||
config.multiController = prefs.getBoolean(MULTI_CONTROLLER_PREF_STRING, DEFAULT_MULTI_CONTROLLER);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
@ -95,6 +95,8 @@
|
||||
<string name="summary_checkbox_disable_warnings">Disable on-screen connection warning messages while streaming</string>
|
||||
|
||||
<string name="category_gamepad_settings">Gamepad Settings</string>
|
||||
<string name="title_checkbox_multi_controller">Enable multiple controller support</string>
|
||||
<string name="summary_checkbox_multi_controller">When unchecked, all controllers appear as controller one</string>
|
||||
<string name="title_seekbar_deadzone">Adjust analog stick deadzone</string>
|
||||
<string name="suffix_seekbar_deadzone">%</string>
|
||||
|
||||
|
@ -26,14 +26,19 @@
|
||||
android:summary="@string/summary_checkbox_disable_warnings"
|
||||
android:defaultValue="false" />
|
||||
</PreferenceCategory>
|
||||
<!--PreferenceCategory android:title="@string/category_gamepad_settings">
|
||||
<com.limelight.preferences.SeekBarPreference
|
||||
<PreferenceCategory android:title="@string/category_gamepad_settings">
|
||||
<!--com.limelight.preferences.SeekBarPreference
|
||||
android:key="seekbar_deadzone"
|
||||
android:defaultValue="15"
|
||||
android:max="50"
|
||||
android:text="@string/suffix_seekbar_deadzone"
|
||||
android:title="@string/title_seekbar_deadzone"/>
|
||||
</PreferenceCategory-->
|
||||
android:title="@string/title_seekbar_deadzone"/-->
|
||||
<CheckBoxPreference
|
||||
android:key="checkbox_multi_controller"
|
||||
android:title="@string/title_checkbox_multi_controller"
|
||||
android:summary="@string/summary_checkbox_multi_controller"
|
||||
android:defaultValue="true" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/category_host_settings">
|
||||
<CheckBoxPreference
|
||||
android:key="checkbox_enable_sops"
|
||||
|
Loading…
x
Reference in New Issue
Block a user