diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index d3582327..b13673b4 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -72,6 +72,7 @@ import java.io.ByteArrayInputStream; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.Locale; 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 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { if (!surfaceCreated) { diff --git a/app/src/main/java/com/limelight/binding/input/ControllerHandler.java b/app/src/main/java/com/limelight/binding/input/ControllerHandler.java index 3db6de1f..17d88ef1 100644 --- a/app/src/main/java/com/limelight/binding/input/ControllerHandler.java +++ b/app/src/main/java/com/limelight/binding/input/ControllerHandler.java @@ -6,6 +6,7 @@ import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import android.os.Build; import android.os.SystemClock; +import android.os.Vibrator; import android.util.SparseArray; import android.view.InputDevice; import android.view.InputEvent; @@ -14,6 +15,7 @@ import android.view.MotionEvent; import android.widget.Toast; import com.limelight.LimeLog; +import com.limelight.binding.input.driver.AbstractController; import com.limelight.binding.input.driver.UsbDriverListener; import com.limelight.binding.input.driver.UsbDriverService; import com.limelight.nvstream.NvConnection; @@ -298,10 +300,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD context.assignedControllerNumber = true; } - private UsbDeviceContext createUsbDeviceContextForDevice(int deviceId) { + private UsbDeviceContext createUsbDeviceContextForDevice(AbstractController device) { UsbDeviceContext context = new UsbDeviceContext(); - context.id = deviceId; + context.id = device.getControllerId(); + context.device = device; context.leftStickDeadzoneRadius = (float) stickDeadzone; context.rightStickDeadzoneRadius = (float) stickDeadzone; @@ -393,6 +396,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD context.name = devName; context.id = dev.getId(); + if (dev.getVibrator().hasVibrator()) { + context.vibrator = dev.getVibrator(); + } + context.leftStickXAxis = MotionEvent.AXIS_X; context.leftStickYAxis = MotionEvent.AXIS_Y; 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) { InputDeviceContext context = getContextForEvent(event); if (context == null) { @@ -1334,19 +1363,19 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD } @Override - public void deviceRemoved(int controllerId) { - UsbDeviceContext context = usbDeviceContexts.get(controllerId); + public void deviceRemoved(AbstractController controller) { + UsbDeviceContext context = usbDeviceContexts.get(controller.getControllerId()); if (context != null) { - LimeLog.info("Removed controller: "+controllerId); + LimeLog.info("Removed controller: "+controller.getControllerId()); releaseControllerNumber(context); - usbDeviceContexts.remove(controllerId); + usbDeviceContexts.remove(controller.getControllerId()); } } @Override - public void deviceAdded(int controllerId) { - UsbDeviceContext context = createUsbDeviceContextForDevice(controllerId); - usbDeviceContexts.put(controllerId, context); + public void deviceAdded(AbstractController controller) { + UsbDeviceContext context = createUsbDeviceContextForDevice(controller); + usbDeviceContexts.put(controller.getControllerId(), context); } class GenericControllerContext { @@ -1375,6 +1404,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD class InputDeviceContext extends GenericControllerContext { public String name; + public Vibrator vibrator; public int leftStickXAxis = -1; public int leftStickYAxis = -1; @@ -1412,5 +1442,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD public long startDownTime = 0; } - class UsbDeviceContext extends GenericControllerContext {} + class UsbDeviceContext extends GenericControllerContext { + public AbstractController device; + } } diff --git a/app/src/main/java/com/limelight/binding/input/driver/AbstractController.java b/app/src/main/java/com/limelight/binding/input/driver/AbstractController.java index 99842cb2..2aa34b9b 100644 --- a/app/src/main/java/com/limelight/binding/input/driver/AbstractController.java +++ b/app/src/main/java/com/limelight/binding/input/driver/AbstractController.java @@ -37,11 +37,13 @@ public abstract class AbstractController { this.listener = listener; } + public abstract void rumble(short lowFreqMotor, short highFreqMotor); + protected void notifyDeviceRemoved() { - listener.deviceRemoved(deviceId); + listener.deviceRemoved(this); } protected void notifyDeviceAdded() { - listener.deviceAdded(deviceId); + listener.deviceAdded(this); } } diff --git a/app/src/main/java/com/limelight/binding/input/driver/UsbDriverListener.java b/app/src/main/java/com/limelight/binding/input/driver/UsbDriverListener.java index 45d07c2d..31a03ff8 100644 --- a/app/src/main/java/com/limelight/binding/input/driver/UsbDriverListener.java +++ b/app/src/main/java/com/limelight/binding/input/driver/UsbDriverListener.java @@ -6,6 +6,6 @@ public interface UsbDriverListener { float rightStickX, float rightStickY, float leftTrigger, float rightTrigger); - void deviceRemoved(int controllerId); - void deviceAdded(int controllerId); + void deviceRemoved(AbstractController controller); + void deviceAdded(AbstractController controller); } diff --git a/app/src/main/java/com/limelight/binding/input/driver/UsbDriverService.java b/app/src/main/java/com/limelight/binding/input/driver/UsbDriverService.java index 345e62af..6348d879 100644 --- a/app/src/main/java/com/limelight/binding/input/driver/UsbDriverService.java +++ b/app/src/main/java/com/limelight/binding/input/driver/UsbDriverService.java @@ -47,26 +47,21 @@ public class UsbDriverService extends Service implements UsbDriverListener { } @Override - public void deviceRemoved(int controllerId) { + public void deviceRemoved(AbstractController controller) { // Remove the the controller from our list (if not removed already) - for (AbstractController controller : controllers) { - if (controller.getControllerId() == controllerId) { - controllers.remove(controller); - break; - } - } + controllers.remove(controller); // Call through to the client's listener if (listener != null) { - listener.deviceRemoved(controllerId); + listener.deviceRemoved(controller); } } @Override - public void deviceAdded(int controllerId) { + public void deviceAdded(AbstractController controller) { // Call through to the client's listener 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 if (listener != null) { for (AbstractController controller : controllers) { - listener.deviceAdded(controller.getControllerId()); + listener.deviceAdded(controller); } } } diff --git a/app/src/main/java/com/limelight/binding/input/driver/Xbox360Controller.java b/app/src/main/java/com/limelight/binding/input/driver/Xbox360Controller.java index 2211972e..2c92a6d2 100644 --- a/app/src/main/java/com/limelight/binding/input/driver/Xbox360Controller.java +++ b/app/src/main/java/com/limelight/binding/input/driver/Xbox360Controller.java @@ -139,4 +139,17 @@ public class Xbox360Controller extends AbstractXboxController { // No need to fail init if the LED command fails 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); + } + } } diff --git a/app/src/main/java/com/limelight/binding/input/driver/XboxOneController.java b/app/src/main/java/com/limelight/binding/input/driver/XboxOneController.java index 80f3363d..78187358 100644 --- a/app/src/main/java/com/limelight/binding/input/driver/XboxOneController.java +++ b/app/src/main/java/com/limelight/binding/input/driver/XboxOneController.java @@ -48,6 +48,7 @@ public class XboxOneController extends AbstractXboxController { new InitPacket(0x24c6, 0x543a, RUMBLE_INIT2), }; + private byte seqNum = 0; public XboxOneController(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) { super(device, connection, deviceId, listener); @@ -134,8 +135,6 @@ public class XboxOneController extends AbstractXboxController { @Override protected boolean doInit() { - byte seqNum = 0; - // Send all applicable init packets for (InitPacket pkt : INIT_PKTS) { if (pkt.vendorId != 0 && device.getVendorId() != pkt.vendorId) { @@ -162,6 +161,20 @@ public class XboxOneController extends AbstractXboxController { 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 { final int vendorId; final int productId; diff --git a/moonlight-common b/moonlight-common index 8190dffe..f41b0dbc 160000 --- a/moonlight-common +++ b/moonlight-common @@ -1 +1 @@ -Subproject commit 8190dffe99c33588b0ba280e3ed183d5a3853fc1 +Subproject commit f41b0dbc0c7813197a7644463232d96b188a379f