Add rumble support for the in-app Xbox driver

This commit is contained in:
Cameron Gutman 2019-02-16 17:03:10 -08:00
parent 6a939e7495
commit a22e33eeb9
8 changed files with 91 additions and 28 deletions

View File

@ -72,6 +72,7 @@ import java.io.ByteArrayInputStream;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Locale;
public class Game extends Activity implements SurfaceHolder.Callback, public class Game extends Activity implements SurfaceHolder.Callback,
@ -1384,6 +1385,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
} }
} }
@Override
public void rumble(short controllerNumber, short lowFreqMotor, short highFreqMotor) {
LimeLog.info(String.format((Locale)null, "Rumble on gamepad %d: %04x %04x", controllerNumber, lowFreqMotor, highFreqMotor));
controllerHandler.handleRumble(controllerNumber, lowFreqMotor, highFreqMotor);
}
@Override @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (!surfaceCreated) { if (!surfaceCreated) {

View File

@ -6,6 +6,7 @@ import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager; import android.hardware.usb.UsbManager;
import android.os.Build; import android.os.Build;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.Vibrator;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.InputEvent; import android.view.InputEvent;
@ -14,6 +15,7 @@ import android.view.MotionEvent;
import android.widget.Toast; import android.widget.Toast;
import com.limelight.LimeLog; import com.limelight.LimeLog;
import com.limelight.binding.input.driver.AbstractController;
import com.limelight.binding.input.driver.UsbDriverListener; import com.limelight.binding.input.driver.UsbDriverListener;
import com.limelight.binding.input.driver.UsbDriverService; import com.limelight.binding.input.driver.UsbDriverService;
import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnection;
@ -298,10 +300,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
context.assignedControllerNumber = true; context.assignedControllerNumber = true;
} }
private UsbDeviceContext createUsbDeviceContextForDevice(int deviceId) { private UsbDeviceContext createUsbDeviceContextForDevice(AbstractController device) {
UsbDeviceContext context = new UsbDeviceContext(); UsbDeviceContext context = new UsbDeviceContext();
context.id = deviceId; context.id = device.getControllerId();
context.device = device;
context.leftStickDeadzoneRadius = (float) stickDeadzone; context.leftStickDeadzoneRadius = (float) stickDeadzone;
context.rightStickDeadzoneRadius = (float) stickDeadzone; context.rightStickDeadzoneRadius = (float) stickDeadzone;
@ -393,6 +396,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
context.name = devName; context.name = devName;
context.id = dev.getId(); context.id = dev.getId();
if (dev.getVibrator().hasVibrator()) {
context.vibrator = dev.getVibrator();
}
context.leftStickXAxis = MotionEvent.AXIS_X; context.leftStickXAxis = MotionEvent.AXIS_X;
context.leftStickYAxis = MotionEvent.AXIS_Y; context.leftStickYAxis = MotionEvent.AXIS_Y;
if (getMotionRangeForJoystickAxis(dev, context.leftStickXAxis) != null && if (getMotionRangeForJoystickAxis(dev, context.leftStickXAxis) != null &&
@ -1043,6 +1050,28 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
} }
private void rumbleVibrator(Vibrator vibrator, short lowFreqMotor, short highFreqMotor) {
}
public void handleRumble(short controllerNumber, short lowFreqMotor, short highFreqMotor) {
for (int i = 0; i < inputDeviceContexts.size(); i++) {
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
if (deviceContext.controllerNumber == controllerNumber && deviceContext.vibrator != null) {
rumbleVibrator(deviceContext.vibrator, lowFreqMotor, highFreqMotor);
}
}
for (int i = 0; i < usbDeviceContexts.size(); i++) {
UsbDeviceContext deviceContext = usbDeviceContexts.valueAt(i);
if (deviceContext.controllerNumber == controllerNumber) {
deviceContext.device.rumble(lowFreqMotor, highFreqMotor);
}
}
}
public boolean handleButtonUp(KeyEvent event) { public boolean handleButtonUp(KeyEvent event) {
InputDeviceContext context = getContextForEvent(event); InputDeviceContext context = getContextForEvent(event);
if (context == null) { if (context == null) {
@ -1334,19 +1363,19 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
@Override @Override
public void deviceRemoved(int controllerId) { public void deviceRemoved(AbstractController controller) {
UsbDeviceContext context = usbDeviceContexts.get(controllerId); UsbDeviceContext context = usbDeviceContexts.get(controller.getControllerId());
if (context != null) { if (context != null) {
LimeLog.info("Removed controller: "+controllerId); LimeLog.info("Removed controller: "+controller.getControllerId());
releaseControllerNumber(context); releaseControllerNumber(context);
usbDeviceContexts.remove(controllerId); usbDeviceContexts.remove(controller.getControllerId());
} }
} }
@Override @Override
public void deviceAdded(int controllerId) { public void deviceAdded(AbstractController controller) {
UsbDeviceContext context = createUsbDeviceContextForDevice(controllerId); UsbDeviceContext context = createUsbDeviceContextForDevice(controller);
usbDeviceContexts.put(controllerId, context); usbDeviceContexts.put(controller.getControllerId(), context);
} }
class GenericControllerContext { class GenericControllerContext {
@ -1375,6 +1404,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
class InputDeviceContext extends GenericControllerContext { class InputDeviceContext extends GenericControllerContext {
public String name; public String name;
public Vibrator vibrator;
public int leftStickXAxis = -1; public int leftStickXAxis = -1;
public int leftStickYAxis = -1; public int leftStickYAxis = -1;
@ -1412,5 +1442,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
public long startDownTime = 0; public long startDownTime = 0;
} }
class UsbDeviceContext extends GenericControllerContext {} class UsbDeviceContext extends GenericControllerContext {
public AbstractController device;
}
} }

View File

@ -37,11 +37,13 @@ public abstract class AbstractController {
this.listener = listener; this.listener = listener;
} }
public abstract void rumble(short lowFreqMotor, short highFreqMotor);
protected void notifyDeviceRemoved() { protected void notifyDeviceRemoved() {
listener.deviceRemoved(deviceId); listener.deviceRemoved(this);
} }
protected void notifyDeviceAdded() { protected void notifyDeviceAdded() {
listener.deviceAdded(deviceId); listener.deviceAdded(this);
} }
} }

View File

@ -6,6 +6,6 @@ public interface UsbDriverListener {
float rightStickX, float rightStickY, float rightStickX, float rightStickY,
float leftTrigger, float rightTrigger); float leftTrigger, float rightTrigger);
void deviceRemoved(int controllerId); void deviceRemoved(AbstractController controller);
void deviceAdded(int controllerId); void deviceAdded(AbstractController controller);
} }

View File

@ -47,26 +47,21 @@ public class UsbDriverService extends Service implements UsbDriverListener {
} }
@Override @Override
public void deviceRemoved(int controllerId) { public void deviceRemoved(AbstractController controller) {
// Remove the the controller from our list (if not removed already) // Remove the the controller from our list (if not removed already)
for (AbstractController controller : controllers) {
if (controller.getControllerId() == controllerId) {
controllers.remove(controller); controllers.remove(controller);
break;
}
}
// Call through to the client's listener // Call through to the client's listener
if (listener != null) { if (listener != null) {
listener.deviceRemoved(controllerId); listener.deviceRemoved(controller);
} }
} }
@Override @Override
public void deviceAdded(int controllerId) { public void deviceAdded(AbstractController controller) {
// Call through to the client's listener // Call through to the client's listener
if (listener != null) { if (listener != null) {
listener.deviceAdded(controllerId); listener.deviceAdded(controller);
} }
} }
@ -113,7 +108,7 @@ public class UsbDriverService extends Service implements UsbDriverListener {
// Report all controllerMap that already exist // Report all controllerMap that already exist
if (listener != null) { if (listener != null) {
for (AbstractController controller : controllers) { for (AbstractController controller : controllers) {
listener.deviceAdded(controller.getControllerId()); listener.deviceAdded(controller);
} }
} }
} }

View File

@ -139,4 +139,17 @@ public class Xbox360Controller extends AbstractXboxController {
// No need to fail init if the LED command fails // No need to fail init if the LED command fails
return true; return true;
} }
@Override
public void rumble(short lowFreqMotor, short highFreqMotor) {
byte[] data = {
0x00, 0x08, 0x00,
(byte)(lowFreqMotor >> 8), (byte)(highFreqMotor >> 8),
0x00, 0x00, 0x00
};
int res = connection.bulkTransfer(outEndpt, data, data.length, 100);
if (res != data.length) {
LimeLog.warning("Rumble transfer failed: "+res);
}
}
} }

View File

@ -48,6 +48,7 @@ public class XboxOneController extends AbstractXboxController {
new InitPacket(0x24c6, 0x543a, RUMBLE_INIT2), new InitPacket(0x24c6, 0x543a, RUMBLE_INIT2),
}; };
private byte seqNum = 0;
public XboxOneController(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) { public XboxOneController(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) {
super(device, connection, deviceId, listener); super(device, connection, deviceId, listener);
@ -134,8 +135,6 @@ public class XboxOneController extends AbstractXboxController {
@Override @Override
protected boolean doInit() { protected boolean doInit() {
byte seqNum = 0;
// Send all applicable init packets // Send all applicable init packets
for (InitPacket pkt : INIT_PKTS) { for (InitPacket pkt : INIT_PKTS) {
if (pkt.vendorId != 0 && device.getVendorId() != pkt.vendorId) { if (pkt.vendorId != 0 && device.getVendorId() != pkt.vendorId) {
@ -162,6 +161,20 @@ public class XboxOneController extends AbstractXboxController {
return true; return true;
} }
@Override
public void rumble(short lowFreqMotor, short highFreqMotor) {
byte[] data = {
0x09, 0x00, seqNum++, 0x09, 0x00,
0x0F, 0x00, 0x00,
(byte)(lowFreqMotor >> 9), (byte)(highFreqMotor >> 9),
(byte)0xFF, 0x00, (byte)0xFF
};
int res = connection.bulkTransfer(outEndpt, data, data.length, 100);
if (res != data.length) {
LimeLog.warning("Rumble transfer failed: "+res);
}
}
private static class InitPacket { private static class InitPacket {
final int vendorId; final int vendorId;
final int productId; final int productId;

@ -1 +1 @@
Subproject commit 8190dffe99c33588b0ba280e3ed183d5a3853fc1 Subproject commit f41b0dbc0c7813197a7644463232d96b188a379f