mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-21 12:03:02 +00:00
Rewrite controller code to better handle previously unseen controllers
This commit is contained in:
parent
20635a3012
commit
8a40892865
@ -1,5 +1,7 @@
|
|||||||
package com.limelight.binding.input;
|
package com.limelight.binding.input;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
@ -16,28 +18,148 @@ public class ControllerHandler {
|
|||||||
private short leftStickX = 0x0000;
|
private short leftStickX = 0x0000;
|
||||||
private short leftStickY = 0x0000;
|
private short leftStickY = 0x0000;
|
||||||
|
|
||||||
|
private HashMap<String, ControllerMapping> mappings = new HashMap<String, ControllerMapping>();
|
||||||
|
|
||||||
private NvConnection conn;
|
private NvConnection conn;
|
||||||
|
|
||||||
public ControllerHandler(NvConnection conn) {
|
public ControllerHandler(NvConnection conn) {
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ControllerMapping createMappingForDevice(InputDevice dev) {
|
||||||
|
ControllerMapping mapping = new ControllerMapping();
|
||||||
|
|
||||||
|
mapping.leftStickXAxis = MotionEvent.AXIS_X;
|
||||||
|
mapping.leftStickYAxis = MotionEvent.AXIS_Y;
|
||||||
|
|
||||||
|
InputDevice.MotionRange leftTriggerRange = dev.getMotionRange(MotionEvent.AXIS_LTRIGGER);
|
||||||
|
InputDevice.MotionRange rightTriggerRange = dev.getMotionRange(MotionEvent.AXIS_RTRIGGER);
|
||||||
|
InputDevice.MotionRange brakeRange = dev.getMotionRange(MotionEvent.AXIS_BRAKE);
|
||||||
|
InputDevice.MotionRange gasRange = dev.getMotionRange(MotionEvent.AXIS_GAS);
|
||||||
|
if (leftTriggerRange != null && rightTriggerRange != null)
|
||||||
|
{
|
||||||
|
// Some controllers use LTRIGGER and RTRIGGER (like Ouya)
|
||||||
|
mapping.leftTriggerAxis = MotionEvent.AXIS_LTRIGGER;
|
||||||
|
mapping.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;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InputDevice.MotionRange rxRange = dev.getMotionRange(MotionEvent.AXIS_RX);
|
||||||
|
InputDevice.MotionRange ryRange = dev.getMotionRange(MotionEvent.AXIS_RY);
|
||||||
|
if (rxRange != null && ryRange != null) {
|
||||||
|
String devName = dev.getName();
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Xbox controllers use Z and RZ for triggers
|
||||||
|
mapping.leftTriggerAxis = MotionEvent.AXIS_Z;
|
||||||
|
mapping.rightTriggerAxis = MotionEvent.AXIS_RZ;
|
||||||
|
mapping.triggersIdleNegative = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// DS4 controller uses RX and RY for triggers
|
||||||
|
mapping.leftTriggerAxis = MotionEvent.AXIS_RX;
|
||||||
|
mapping.rightTriggerAxis = MotionEvent.AXIS_RY;
|
||||||
|
mapping.triggersIdleNegative = true;
|
||||||
|
|
||||||
|
mapping.isDualShock4 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapping.rightStickXAxis == -1 && mapping.rightStickYAxis == -1) {
|
||||||
|
InputDevice.MotionRange zRange = dev.getMotionRange(MotionEvent.AXIS_Z);
|
||||||
|
InputDevice.MotionRange rzRange = dev.getMotionRange(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;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
InputDevice.MotionRange rxRange = dev.getMotionRange(MotionEvent.AXIS_RX);
|
||||||
|
InputDevice.MotionRange ryRange = dev.getMotionRange(MotionEvent.AXIS_RY);
|
||||||
|
|
||||||
|
// Try RX and RY now
|
||||||
|
if (rxRange != null && ryRange != null) {
|
||||||
|
mapping.rightStickXAxis = MotionEvent.AXIS_RX;
|
||||||
|
mapping.rightStickYAxis = MotionEvent.AXIS_RY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some devices have "hats" for d-pads
|
||||||
|
InputDevice.MotionRange hatXRange = dev.getMotionRange(MotionEvent.AXIS_HAT_X);
|
||||||
|
InputDevice.MotionRange hatYRange = dev.getMotionRange(MotionEvent.AXIS_HAT_Y);
|
||||||
|
if (hatXRange != null && hatYRange != null) {
|
||||||
|
mapping.hatXAxis = MotionEvent.AXIS_HAT_X;
|
||||||
|
mapping.hatYAxis = MotionEvent.AXIS_HAT_Y;
|
||||||
|
|
||||||
|
mapping.hatXDeadzone = hatXRange.getFlat();
|
||||||
|
mapping.hatYDeadzone = hatYRange.getFlat();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) {
|
||||||
|
InputDevice.MotionRange lsXRange = dev.getMotionRange(mapping.leftStickXAxis);
|
||||||
|
InputDevice.MotionRange lsYRange = dev.getMotionRange(mapping.leftStickYAxis);
|
||||||
|
if (lsXRange != null) {
|
||||||
|
mapping.leftStickXAxisDeadzone = lsXRange.getFlat();
|
||||||
|
}
|
||||||
|
if (lsYRange != null) {
|
||||||
|
mapping.leftStickYAxisDeadzone = lsYRange.getFlat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) {
|
||||||
|
InputDevice.MotionRange rsXRange = dev.getMotionRange(mapping.rightStickXAxis);
|
||||||
|
InputDevice.MotionRange rsYRange = dev.getMotionRange(mapping.rightStickYAxis);
|
||||||
|
if (rsXRange != null) {
|
||||||
|
mapping.rightStickXAxisDeadzone = rsXRange.getFlat();
|
||||||
|
}
|
||||||
|
if (rsYRange != null) {
|
||||||
|
mapping.rightStickYAxisDeadzone = rsYRange.getFlat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ControllerMapping getMappingForDevice(InputDevice dev) {
|
||||||
|
// Unknown devices can't be handled
|
||||||
|
if (dev == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String descriptor = dev.getDescriptor();
|
||||||
|
|
||||||
|
// Return the existing mapping if it exists
|
||||||
|
ControllerMapping mapping = mappings.get(descriptor);
|
||||||
|
if (mapping != null) {
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise create a new mapping
|
||||||
|
mapping = createMappingForDevice(dev);
|
||||||
|
mappings.put(descriptor, mapping);
|
||||||
|
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
private void sendControllerInputPacket() {
|
private void sendControllerInputPacket() {
|
||||||
conn.sendControllerInput(inputMap, leftTrigger, rightTrigger,
|
conn.sendControllerInput(inputMap, leftTrigger, rightTrigger,
|
||||||
leftStickX, leftStickY, rightStickX, rightStickY);
|
leftStickX, leftStickY, rightStickX, rightStickY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isDualShock4(InputDevice dev) {
|
private int handleRemapping(ControllerMapping mapping, int keyCode) {
|
||||||
return (dev.getMotionRange(MotionEvent.AXIS_RX) != null &&
|
if (mapping.isDualShock4) {
|
||||||
dev.getMotionRange(MotionEvent.AXIS_RY) != null &&
|
|
||||||
dev.getMotionRange(MotionEvent.AXIS_HAT_X) != null &&
|
|
||||||
dev.getMotionRange(MotionEvent.AXIS_HAT_Y) != null &&
|
|
||||||
dev.getMotionRange(MotionEvent.AXIS_RZ) != null &&
|
|
||||||
dev.getMotionRange(MotionEvent.AXIS_RY) != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int handleRemapping(InputDevice dev, int keyCode) {
|
|
||||||
if (isDualShock4(dev)) {
|
|
||||||
switch (keyCode) {
|
switch (keyCode) {
|
||||||
case KeyEvent.KEYCODE_BUTTON_Y:
|
case KeyEvent.KEYCODE_BUTTON_Y:
|
||||||
return KeyEvent.KEYCODE_BUTTON_L1;
|
return KeyEvent.KEYCODE_BUTTON_L1;
|
||||||
@ -73,8 +195,12 @@ public class ControllerHandler {
|
|||||||
case KeyEvent.KEYCODE_BUTTON_R1:
|
case KeyEvent.KEYCODE_BUTTON_R1:
|
||||||
case KeyEvent.KEYCODE_BUTTON_L1:
|
case KeyEvent.KEYCODE_BUTTON_L1:
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// These are duplicate dpad events
|
if (mapping.hatXAxis != -1 && mapping.hatYAxis != -1) {
|
||||||
|
switch (keyCode) {
|
||||||
|
// These are duplicate dpad events for hat input
|
||||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||||
@ -88,106 +214,86 @@ public class ControllerHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean handleMotionEvent(MotionEvent event) {
|
public boolean handleMotionEvent(MotionEvent event) {
|
||||||
InputDevice dev = event.getDevice();
|
ControllerMapping mapping = getMappingForDevice(event.getDevice());
|
||||||
if (dev == null) {
|
if (mapping == null) {
|
||||||
System.err.println("Unknown device");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float LS_X = event.getAxisValue(MotionEvent.AXIS_X);
|
// Handle left stick events outside of the deadzone
|
||||||
float LS_Y = event.getAxisValue(MotionEvent.AXIS_Y);
|
if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) {
|
||||||
|
float LS_X = event.getAxisValue(mapping.leftStickXAxis);
|
||||||
float RS_X, RS_Y, L2, R2;
|
float LS_Y = event.getAxisValue(mapping.leftStickYAxis);
|
||||||
|
if (LS_X >= -mapping.leftStickXAxisDeadzone && LS_X <= mapping.leftStickXAxisDeadzone) {
|
||||||
InputDevice.MotionRange leftTriggerRange = dev.getMotionRange(MotionEvent.AXIS_LTRIGGER);
|
LS_X = 0;
|
||||||
InputDevice.MotionRange rightTriggerRange = dev.getMotionRange(MotionEvent.AXIS_RTRIGGER);
|
|
||||||
if (leftTriggerRange != null && rightTriggerRange != null)
|
|
||||||
{
|
|
||||||
// Ouya controller
|
|
||||||
L2 = event.getAxisValue(MotionEvent.AXIS_LTRIGGER);
|
|
||||||
R2 = event.getAxisValue(MotionEvent.AXIS_RTRIGGER);
|
|
||||||
RS_X = event.getAxisValue(MotionEvent.AXIS_Z);
|
|
||||||
RS_Y = event.getAxisValue(MotionEvent.AXIS_RZ);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
InputDevice.MotionRange brakeRange = dev.getMotionRange(MotionEvent.AXIS_BRAKE);
|
|
||||||
InputDevice.MotionRange gasRange = dev.getMotionRange(MotionEvent.AXIS_GAS);
|
|
||||||
InputDevice.MotionRange rxRange = dev.getMotionRange(MotionEvent.AXIS_RX);
|
|
||||||
InputDevice.MotionRange ryRange = dev.getMotionRange(MotionEvent.AXIS_RY);
|
|
||||||
if (brakeRange != null && gasRange != null)
|
|
||||||
{
|
|
||||||
// Moga controller
|
|
||||||
RS_X = event.getAxisValue(MotionEvent.AXIS_Z);
|
|
||||||
RS_Y = event.getAxisValue(MotionEvent.AXIS_RZ);
|
|
||||||
L2 = event.getAxisValue(MotionEvent.AXIS_BRAKE);
|
|
||||||
R2 = event.getAxisValue(MotionEvent.AXIS_GAS);
|
|
||||||
}
|
}
|
||||||
else if (rxRange != null && ryRange != null)
|
if (LS_Y >= -mapping.leftStickYAxisDeadzone && LS_Y <= mapping.leftStickYAxisDeadzone) {
|
||||||
{
|
LS_Y = 0;
|
||||||
// DS4 controller
|
|
||||||
RS_X = event.getAxisValue(MotionEvent.AXIS_Z);
|
|
||||||
RS_Y = event.getAxisValue(MotionEvent.AXIS_RZ);
|
|
||||||
L2 = (event.getAxisValue(MotionEvent.AXIS_RX) + 1) / 2;
|
|
||||||
R2 = (event.getAxisValue(MotionEvent.AXIS_RY) + 1) / 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Xbox controller
|
|
||||||
RS_X = event.getAxisValue(MotionEvent.AXIS_RX);
|
|
||||||
RS_Y = event.getAxisValue(MotionEvent.AXIS_RY);
|
|
||||||
L2 = (event.getAxisValue(MotionEvent.AXIS_Z) + 1) / 2;
|
|
||||||
R2 = (event.getAxisValue(MotionEvent.AXIS_RZ) + 1) / 2;
|
|
||||||
}
|
}
|
||||||
|
leftStickX = (short)Math.round(LS_X * 0x7FFF);
|
||||||
|
leftStickY = (short)Math.round(-LS_Y * 0x7FFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDevice.MotionRange hatXRange = dev.getMotionRange(MotionEvent.AXIS_HAT_X);
|
// Handle right stick events outside of the deadzone
|
||||||
InputDevice.MotionRange hatYRange = dev.getMotionRange(MotionEvent.AXIS_HAT_Y);
|
if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) {
|
||||||
if (hatXRange != null && hatYRange != null)
|
float RS_X = event.getAxisValue(mapping.rightStickXAxis);
|
||||||
{
|
float RS_Y = event.getAxisValue(mapping.rightStickYAxis);
|
||||||
// Xbox and DS4 D-pad
|
if (RS_X >= -mapping.rightStickXAxisDeadzone && RS_X <= mapping.rightStickXAxisDeadzone) {
|
||||||
float hatX, hatY;
|
RS_X = 0;
|
||||||
|
}
|
||||||
|
if (RS_Y >= -mapping.rightStickYAxisDeadzone && RS_Y <= mapping.rightStickYAxisDeadzone) {
|
||||||
|
RS_Y = 0;
|
||||||
|
}
|
||||||
|
rightStickX = (short)Math.round(RS_X * 0x7FFF);
|
||||||
|
rightStickY = (short)Math.round(-RS_Y * 0x7FFF);
|
||||||
|
}
|
||||||
|
|
||||||
hatX = event.getAxisValue(MotionEvent.AXIS_HAT_X);
|
// Handle controllers with analog triggers
|
||||||
hatY = event.getAxisValue(MotionEvent.AXIS_HAT_Y);
|
if (mapping.leftTriggerAxis != -1 && mapping.rightTriggerAxis != -1) {
|
||||||
|
float L2 = event.getAxisValue(mapping.leftTriggerAxis);
|
||||||
|
float R2 = event.getAxisValue(mapping.rightTriggerAxis);
|
||||||
|
|
||||||
|
if (mapping.triggersIdleNegative) {
|
||||||
|
L2 = (L2 + 1) / 2;
|
||||||
|
R2 = (R2 + 1) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
leftTrigger = (byte)Math.round(L2 * 0xFF);
|
||||||
|
rightTrigger = (byte)Math.round(R2 * 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hats emulate d-pad events
|
||||||
|
if (mapping.hatXAxis != -1 && mapping.hatYAxis != -1) {
|
||||||
|
float hatX = event.getAxisValue(MotionEvent.AXIS_HAT_X);
|
||||||
|
float hatY = event.getAxisValue(MotionEvent.AXIS_HAT_Y);
|
||||||
|
|
||||||
inputMap &= ~(ControllerPacket.LEFT_FLAG | ControllerPacket.RIGHT_FLAG);
|
inputMap &= ~(ControllerPacket.LEFT_FLAG | ControllerPacket.RIGHT_FLAG);
|
||||||
inputMap &= ~(ControllerPacket.UP_FLAG | ControllerPacket.DOWN_FLAG);
|
if (hatX < -(0.5 + mapping.hatXDeadzone)) {
|
||||||
if (hatX < -0.5) {
|
|
||||||
inputMap |= ControllerPacket.LEFT_FLAG;
|
inputMap |= ControllerPacket.LEFT_FLAG;
|
||||||
}
|
}
|
||||||
if (hatX > 0.5) {
|
else if (hatX > (0.5 + mapping.hatXDeadzone)) {
|
||||||
inputMap |= ControllerPacket.RIGHT_FLAG;
|
inputMap |= ControllerPacket.RIGHT_FLAG;
|
||||||
}
|
}
|
||||||
if (hatY < -0.5) {
|
|
||||||
|
inputMap &= ~(ControllerPacket.UP_FLAG | ControllerPacket.DOWN_FLAG);
|
||||||
|
if (hatY < -(0.5 + mapping.hatYDeadzone)) {
|
||||||
inputMap |= ControllerPacket.UP_FLAG;
|
inputMap |= ControllerPacket.UP_FLAG;
|
||||||
}
|
}
|
||||||
if (hatY > 0.5) {
|
else if (hatY > (0.5 + mapping.hatYDeadzone)) {
|
||||||
inputMap |= ControllerPacket.DOWN_FLAG;
|
inputMap |= ControllerPacket.DOWN_FLAG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
leftStickX = (short)Math.round(LS_X * 0x7FFF);
|
|
||||||
leftStickY = (short)Math.round(-LS_Y * 0x7FFF);
|
|
||||||
|
|
||||||
rightStickX = (short)Math.round(RS_X * 0x7FFF);
|
|
||||||
rightStickY = (short)Math.round(-RS_Y * 0x7FFF);
|
|
||||||
|
|
||||||
leftTrigger = (byte)Math.round(L2 * 0xFF);
|
|
||||||
rightTrigger = (byte)Math.round(R2 * 0xFF);
|
|
||||||
|
|
||||||
sendControllerInputPacket();
|
sendControllerInputPacket();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean handleButtonUp(int keyCode, KeyEvent event) {
|
public boolean handleButtonUp(int keyCode, KeyEvent event) {
|
||||||
InputDevice dev = event.getDevice();
|
ControllerMapping mapping = getMappingForDevice(event.getDevice());
|
||||||
if (dev == null) {
|
if (mapping == null) {
|
||||||
System.err.println("Unknown device");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
keyCode = handleRemapping(dev, keyCode);
|
keyCode = handleRemapping(mapping, keyCode);
|
||||||
if (keyCode == 0) {
|
if (keyCode == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -262,13 +368,12 @@ public class ControllerHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean handleButtonDown(int keyCode, KeyEvent event) {
|
public boolean handleButtonDown(int keyCode, KeyEvent event) {
|
||||||
InputDevice dev = event.getDevice();
|
ControllerMapping mapping = getMappingForDevice(event.getDevice());
|
||||||
if (dev == null) {
|
if (mapping == null) {
|
||||||
System.err.println("Unknown device");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
keyCode = handleRemapping(dev, keyCode);
|
keyCode = handleRemapping(mapping, keyCode);
|
||||||
if (keyCode == 0) {
|
if (keyCode == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -342,4 +447,29 @@ public class ControllerHandler {
|
|||||||
sendControllerInputPacket();
|
sendControllerInputPacket();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ControllerMapping {
|
||||||
|
public int leftStickXAxis = -1;
|
||||||
|
public float leftStickXAxisDeadzone;
|
||||||
|
|
||||||
|
public int leftStickYAxis = -1;
|
||||||
|
public float leftStickYAxisDeadzone;
|
||||||
|
|
||||||
|
public int rightStickXAxis = -1;
|
||||||
|
public float rightStickXAxisDeadzone;
|
||||||
|
|
||||||
|
public int rightStickYAxis = -1;
|
||||||
|
public float rightStickYAxisDeadzone;
|
||||||
|
|
||||||
|
public int leftTriggerAxis = -1;
|
||||||
|
public int rightTriggerAxis = -1;
|
||||||
|
public boolean triggersIdleNegative;
|
||||||
|
|
||||||
|
public int hatXAxis = -1;
|
||||||
|
public int hatYAxis = -1;
|
||||||
|
public float hatXDeadzone;
|
||||||
|
public float hatYDeadzone;
|
||||||
|
|
||||||
|
public boolean isDualShock4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user