diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index 3f58bb19..916b20ec 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -2104,12 +2104,24 @@ public class Game extends Activity implements SurfaceHolder.Callback, controllerHandler.handleRumble(controllerNumber, lowFreqMotor, highFreqMotor); } + @Override + public void rumbleTriggers(short controllerNumber, short leftTrigger, short rightTrigger) { + LimeLog.info(String.format((Locale)null, "Rumble on gamepad triggers %d: %04x %04x", controllerNumber, leftTrigger, rightTrigger)); + + controllerHandler.handleRumbleTriggers(controllerNumber, leftTrigger, rightTrigger); + } + @Override public void setHdrMode(boolean enabled, byte[] hdrMetadata) { LimeLog.info("Display HDR mode: " + (enabled ? "enabled" : "disabled")); decoderRenderer.setHdrMode(enabled, hdrMetadata); } + @Override + public void setMotionEventState(short controllerNumber, byte motionType, short reportRateHz) { + controllerHandler.handleSetMotionEventState(controllerNumber, motionType, reportRateHz); + } + @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 be0f2b6c..78ccdff4 100644 --- a/app/src/main/java/com/limelight/binding/input/ControllerHandler.java +++ b/app/src/main/java/com/limelight/binding/input/ControllerHandler.java @@ -817,7 +817,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD // Take the context's controller number and fuse all inputs with the same number short controllerNumber = originalContext.controllerNumber; - short inputMap = 0; + int inputMap = 0; byte leftTrigger = 0; byte rightTrigger = 0; short leftStickX = 0; @@ -1473,6 +1473,14 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD } } + public void handleRumbleTriggers(short controllerNumber, short leftTrigger, short rightTrigger) { + // TODO + } + + public void handleSetMotionEventState(short controllerNumber, byte motionType, short reportRateHz) { + // TODO + } + public boolean handleButtonUp(KeyEvent event) { InputDeviceContext context = getContextForEvent(event); if (context == null) { @@ -1794,7 +1802,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD return true; } - public void reportOscState(short buttonFlags, + public void reportOscState(int buttonFlags, short leftStickX, short leftStickY, short rightStickX, short rightStickY, byte leftTrigger, byte rightTrigger) { @@ -1813,7 +1821,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD } @Override - public void reportControllerState(int controllerId, short buttonFlags, + public void reportControllerState(int controllerId, int buttonFlags, float leftStickX, float leftStickY, float rightStickX, float rightStickY, float leftTrigger, float rightTrigger) { @@ -1883,7 +1891,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD public boolean reservedControllerNumber; public short controllerNumber; - public short inputMap = 0x0000; + public int inputMap = 0; public byte leftTrigger = 0x00; public byte rightTrigger = 0x00; public short rightStickX = 0x0000; @@ -1892,7 +1900,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD public short leftStickY = 0x0000; public boolean mouseEmulationActive; - public short mouseEmulationLastInputMap; + public int mouseEmulationLastInputMap; public final int mouseEmulationReportPeriod = 50; public final Runnable mouseEmulationRunnable = new Runnable() { 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 31a03ff8..c8121222 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 @@ -1,7 +1,7 @@ package com.limelight.binding.input.driver; public interface UsbDriverListener { - void reportControllerState(int controllerId, short buttonFlags, + void reportControllerState(int controllerId, int buttonFlags, float leftStickX, float leftStickY, float rightStickX, float rightStickY, float leftTrigger, float rightTrigger); 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 91350c86..2bbac86d 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 @@ -42,7 +42,8 @@ public class UsbDriverService extends Service implements UsbDriverListener { private int nextDeviceId; @Override - public void reportControllerState(int controllerId, short buttonFlags, float leftStickX, float leftStickY, float rightStickX, float rightStickY, float leftTrigger, float rightTrigger) { + public void reportControllerState(int controllerId, int buttonFlags, float leftStickX, float leftStickY, + float rightStickX, float rightStickY, float leftTrigger, float rightTrigger) { // Call through to the client's listener if (listener != null) { listener.reportControllerState(controllerId, buttonFlags, leftStickX, leftStickY, rightStickX, rightStickY, leftTrigger, rightTrigger); diff --git a/app/src/main/java/com/limelight/nvstream/NvConnection.java b/app/src/main/java/com/limelight/nvstream/NvConnection.java index 6792c3a8..44b3a6f1 100644 --- a/app/src/main/java/com/limelight/nvstream/NvConnection.java +++ b/app/src/main/java/com/limelight/nvstream/NvConnection.java @@ -486,7 +486,7 @@ public class NvConnection { } public void sendControllerInput(final short controllerNumber, - final short activeGamepadMask, final short buttonFlags, + final short activeGamepadMask, final int buttonFlags, final byte leftTrigger, final byte rightTrigger, final short leftStickX, final short leftStickY, final short rightStickX, final short rightStickY) @@ -496,18 +496,7 @@ public class NvConnection { leftTrigger, rightTrigger, leftStickX, leftStickY, rightStickX, rightStickY); } } - - public void sendControllerInput(final short buttonFlags, - final byte leftTrigger, final byte rightTrigger, - final short leftStickX, final short leftStickY, - final short rightStickX, final short rightStickY) - { - if (!isMonkey) { - MoonBridge.sendControllerInput(buttonFlags, leftTrigger, rightTrigger, leftStickX, - leftStickY, rightStickX, rightStickY); - } - } - + public void sendKeyboardInput(final short keyMap, final byte keyDirection, final byte modifier, final byte flags) { if (!isMonkey) { MoonBridge.sendKeyboardInput(keyMap, keyDirection, modifier, flags); @@ -538,6 +527,50 @@ public class NvConnection { } } + public int sendTouchEvent(byte eventType, int pointerId, float x, float y, float pressure) { + if (!isMonkey) { + return MoonBridge.sendTouchEvent(eventType, pointerId, x, y, pressure); + } + else { + return MoonBridge.LI_ERR_UNSUPPORTED; + } + } + + public int sendPenEvent(byte eventType, byte toolType, byte penButtons, float x, float y, + float pressure, short rotation, byte tiltX, byte tiltY) { + if (!isMonkey) { + return MoonBridge.sendPenEvent(eventType, toolType, penButtons, x, y, pressure, rotation, tiltX, tiltY); + } + else { + return MoonBridge.LI_ERR_UNSUPPORTED; + } + } + + public int sendControllerArrivalEvent(byte controllerNumber, short activeGamepadMask, byte type, + int supportedButtonFlags, short capabilities) { + return MoonBridge.sendControllerArrivalEvent(controllerNumber, activeGamepadMask, type, supportedButtonFlags, capabilities); + } + + public int sendControllerTouchEvent(byte controllerNumber, byte eventType, int pointerId, + float x, float y, float pressure) { + if (!isMonkey) { + return MoonBridge.sendControllerTouchEvent(controllerNumber, eventType, pointerId, x, y, pressure); + } + else { + return MoonBridge.LI_ERR_UNSUPPORTED; + } + } + + public int sendControllerMotionEvent(byte controllerNumber, byte motionType, + float x, float y, float z) { + if (!isMonkey) { + return MoonBridge.sendControllerMotionEvent(controllerNumber, motionType, x, y, z); + } + else { + return MoonBridge.LI_ERR_UNSUPPORTED; + } + } + public void sendUtf8Text(final String text) { if (!isMonkey) { MoonBridge.sendUtf8Text(text); diff --git a/app/src/main/java/com/limelight/nvstream/NvConnectionListener.java b/app/src/main/java/com/limelight/nvstream/NvConnectionListener.java index 945f18a3..aca0510b 100644 --- a/app/src/main/java/com/limelight/nvstream/NvConnectionListener.java +++ b/app/src/main/java/com/limelight/nvstream/NvConnectionListener.java @@ -13,6 +13,9 @@ public interface NvConnectionListener { void displayTransientMessage(String message); void rumble(short controllerNumber, short lowFreqMotor, short highFreqMotor); + void rumbleTriggers(short controllerNumber, short leftTrigger, short rightTrigger); void setHdrMode(boolean enabled, byte[] hdrMetadata); + + void setMotionEventState(short controllerNumber, byte motionType, short reportRateHz); } diff --git a/app/src/main/java/com/limelight/nvstream/input/ControllerPacket.java b/app/src/main/java/com/limelight/nvstream/input/ControllerPacket.java index 155c6077..b956598b 100644 --- a/app/src/main/java/com/limelight/nvstream/input/ControllerPacket.java +++ b/app/src/main/java/com/limelight/nvstream/input/ControllerPacket.java @@ -1,19 +1,27 @@ package com.limelight.nvstream.input; public class ControllerPacket { - public static final short A_FLAG = 0x1000; - public static final short B_FLAG = 0x2000; - public static final short X_FLAG = 0x4000; - public static final short Y_FLAG = (short)0x8000; - public static final short UP_FLAG = 0x0001; - public static final short DOWN_FLAG = 0x0002; - public static final short LEFT_FLAG = 0x0004; - public static final short RIGHT_FLAG = 0x0008; - public static final short LB_FLAG = 0x0100; - public static final short RB_FLAG = 0x0200; - public static final short PLAY_FLAG = 0x0010; - public static final short BACK_FLAG = 0x0020; - public static final short LS_CLK_FLAG = 0x0040; - public static final short RS_CLK_FLAG = 0x0080; - public static final short SPECIAL_BUTTON_FLAG = 0x0400; + public static final int A_FLAG = 0x1000; + public static final int B_FLAG = 0x2000; + public static final int X_FLAG = 0x4000; + public static final int Y_FLAG = 0x8000; + public static final int UP_FLAG = 0x0001; + public static final int DOWN_FLAG = 0x0002; + public static final int LEFT_FLAG = 0x0004; + public static final int RIGHT_FLAG = 0x0008; + public static final int LB_FLAG = 0x0100; + public static final int RB_FLAG = 0x0200; + public static final int PLAY_FLAG = 0x0010; + public static final int BACK_FLAG = 0x0020; + public static final int LS_CLK_FLAG = 0x0040; + public static final int RS_CLK_FLAG = 0x0080; + public static final int SPECIAL_BUTTON_FLAG = 0x0400; + + // Extended buttons (Sunshine only) + public static final int PADDLE1_FLAG = 0x010000; + public static final int PADDLE2_FLAG = 0x020000; + public static final int PADDLE3_FLAG = 0x040000; + public static final int PADDLE4_FLAG = 0x080000; + public static final int TOUCHPAD_FLAG = 0x100000; // Touchpad buttons on Sony controllers + public static final int MISC_FLAG = 0x200000; // Share/Mic/Capture/Mute buttons on various controllers } \ No newline at end of file diff --git a/app/src/main/java/com/limelight/nvstream/jni/MoonBridge.java b/app/src/main/java/com/limelight/nvstream/jni/MoonBridge.java index 0a4b6e5f..3c2a15d1 100644 --- a/app/src/main/java/com/limelight/nvstream/jni/MoonBridge.java +++ b/app/src/main/java/com/limelight/nvstream/jni/MoonBridge.java @@ -75,6 +75,39 @@ public class MoonBridge { public static final byte SS_KBE_FLAG_NON_NORMALIZED = 0x01; + public static final int LI_ERR_UNSUPPORTED = -5501; + + public static final byte LI_TOUCH_EVENT_HOVER = 0x00; + public static final byte LI_TOUCH_EVENT_DOWN = 0x01; + public static final byte LI_TOUCH_EVENT_UP = 0x02; + public static final byte LI_TOUCH_EVENT_MOVE = 0x03; + public static final byte LI_TOUCH_EVENT_CANCEL = 0x04; + + public static final byte LI_TOOL_TYPE_PEN = 0x01; + public static final byte LI_TOOL_TYPE_ERASER = 0x02; + + public static final byte LI_PEN_BUTTON_PRIMARY = 0x01; + public static final byte LI_PEN_BUTTON_SECONDARY = 0x02; + public static final byte LI_PEN_BUTTON_TERTIARY = 0x04; + + public static final byte LI_TILT_UNKNOWN = (byte)0xFF; + public static final byte LI_ROT_UNKNOWN = (byte)0xFF; + + public static final byte LI_CTYPE_UNKNOWN = 0x00; + public static final byte LI_CTYPE_XBOX = 0x01; + public static final byte LI_CTYPE_PS = 0x02; + public static final byte LI_CTYPE_NINTENDO = 0x03; + + public static final short LI_CCAP_ANALOG_TRIGGERS = 0x01; + public static final short LI_CCAP_RUMBLE = 0x02; + public static final short LI_CCAP_TRIGGER_RUMBLE = 0x04; + public static final short LI_CCAP_TOUCHPAD = 0x08; + public static final short LI_CCAP_ACCEL = 0x10; + public static final short LI_CCAP_GYRO = 0x20; + + public static final byte LI_MOTION_TYPE_ACCEL = 0x01; + public static final byte LI_MOTION_TYPE_GYRO = 0x02; + private static AudioRenderer audioRenderer; private static VideoDecoderRenderer videoRenderer; private static NvConnectionListener connectionListener; @@ -259,6 +292,18 @@ public class MoonBridge { } } + public static void bridgeClRumbleTriggers(short controllerNumber, short leftTrigger, short rightTrigger) { + if (connectionListener != null) { + connectionListener.rumbleTriggers(controllerNumber, leftTrigger, rightTrigger); + } + } + + public static void bridgeClSetMotionEventState(short controllerNumber, byte eventType, short sampleRateHz) { + if (connectionListener != null) { + connectionListener.setMotionEventState(controllerNumber, eventType, sampleRateHz); + } + } + public static void setupBridge(VideoDecoderRenderer videoRenderer, AudioRenderer audioRenderer, NvConnectionListener connectionListener) { MoonBridge.videoRenderer = videoRenderer; MoonBridge.audioRenderer = audioRenderer; @@ -297,15 +342,21 @@ public class MoonBridge { public static native void sendMouseButton(byte buttonEvent, byte mouseButton); public static native void sendMultiControllerInput(short controllerNumber, - short activeGamepadMask, short buttonFlags, + short activeGamepadMask, int buttonFlags, byte leftTrigger, byte rightTrigger, short leftStickX, short leftStickY, short rightStickX, short rightStickY); - public static native void sendControllerInput(short buttonFlags, - byte leftTrigger, byte rightTrigger, - short leftStickX, short leftStickY, - short rightStickX, short rightStickY); + public static native int sendTouchEvent(byte eventType, int pointerId, float x, float y, float pressure); + + public static native int sendPenEvent(byte eventType, byte toolType, byte penButtons, float x, float y, + float pressure, short rotation, byte tiltX, byte tiltY); + + public static native int sendControllerArrivalEvent(byte controllerNumber, short activeGamepadMask, byte type, int supportedButtonFlags, short capabilities); + + public static native int sendControllerTouchEvent(byte controllerNumber, byte eventType, int pointerId, float x, float y, float pressure); + + public static native int sendControllerMotionEvent(byte controllerNumber, byte motionType, float x, float y, float z); public static native void sendKeyboardInput(short keyMap, byte keyDirection, byte modifier, byte flags); diff --git a/app/src/main/jni/moonlight-core/callbacks.c b/app/src/main/jni/moonlight-core/callbacks.c index ce455fcd..aa770e9b 100644 --- a/app/src/main/jni/moonlight-core/callbacks.c +++ b/app/src/main/jni/moonlight-core/callbacks.c @@ -33,6 +33,8 @@ static jmethodID BridgeClConnectionTerminatedMethod; static jmethodID BridgeClRumbleMethod; static jmethodID BridgeClConnectionStatusUpdateMethod; static jmethodID BridgeClSetHdrModeMethod; +static jmethodID BridgeClRumbleTriggersMethod; +static jmethodID BridgeClSetMotionEventStateMethod; static jbyteArray DecodedFrameBuffer; static jshortArray DecodedAudioBuffer; @@ -94,6 +96,8 @@ Java_com_limelight_nvstream_jni_MoonBridge_init(JNIEnv *env, jclass clazz) { BridgeClRumbleMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClRumble", "(SSS)V"); BridgeClConnectionStatusUpdateMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClConnectionStatusUpdate", "(I)V"); BridgeClSetHdrModeMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClSetHdrMode", "(Z[B)V"); + BridgeClRumbleTriggersMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClRumbleTriggers", "(SSS)V"); + BridgeClSetMotionEventStateMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClSetMotionEventState", "(SBS)V"); } int BridgeDrSetup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { @@ -347,6 +351,29 @@ void BridgeClSetHdrMode(bool enabled) { } } +void BridgeClRumbleTriggers(unsigned short controllerNumber, unsigned short leftTrigger, unsigned short rightTrigger) { + JNIEnv* env = GetThreadEnv(); + + // The seemingly redundant short casts are required in order to convert the unsigned short to a signed short. + // If we leave it as an unsigned short, CheckJNI will fail when the value exceeds 32767. The cast itself is + // fine because the Java code treats the value as unsigned even though it's stored in a signed type. + (*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClRumbleTriggersMethod, controllerNumber, (short)leftTrigger, (short)rightTrigger); + if ((*env)->ExceptionCheck(env)) { + // We will crash here + (*JVM)->DetachCurrentThread(JVM); + } +} + +void BridgeClSetMotionEventState(uint16_t controllerNumber, uint8_t motionType, uint16_t reportRateHz) { + JNIEnv* env = GetThreadEnv(); + + (*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClSetMotionEventStateMethod, controllerNumber, motionType, reportRateHz); + if ((*env)->ExceptionCheck(env)) { + // We will crash here + (*JVM)->DetachCurrentThread(JVM); + } +} + void BridgeClLogMessage(const char* format, ...) { va_list va; va_start(va, format); @@ -381,6 +408,8 @@ static CONNECTION_LISTENER_CALLBACKS BridgeConnListenerCallbacks = { .rumble = BridgeClRumble, .connectionStatusUpdate = BridgeClConnectionStatusUpdate, .setHdrMode = BridgeClSetHdrMode, + .rumbleTriggers = BridgeClRumbleTriggers, + .setMotionEventState = BridgeClSetMotionEventState, }; JNIEXPORT jint JNICALL diff --git a/app/src/main/jni/moonlight-core/moonlight-common-c b/app/src/main/jni/moonlight-core/moonlight-common-c index 284840bd..5cbb6f21 160000 --- a/app/src/main/jni/moonlight-core/moonlight-common-c +++ b/app/src/main/jni/moonlight-core/moonlight-common-c @@ -1 +1 @@ -Subproject commit 284840bde75c0e30dd72aaa5e4e136dedf084ee1 +Subproject commit 5cbb6f210d40d2af55e619526a4fc4a33df72c15 diff --git a/app/src/main/jni/moonlight-core/simplejni.c b/app/src/main/jni/moonlight-core/simplejni.c index bc776a80..81a4060f 100644 --- a/app/src/main/jni/moonlight-core/simplejni.c +++ b/app/src/main/jni/moonlight-core/simplejni.c @@ -30,7 +30,7 @@ Java_com_limelight_nvstream_jni_MoonBridge_sendMouseButton(JNIEnv *env, jclass c JNIEXPORT void JNICALL Java_com_limelight_nvstream_jni_MoonBridge_sendMultiControllerInput(JNIEnv *env, jclass clazz, jshort controllerNumber, - jshort activeGamepadMask, jshort buttonFlags, + jshort activeGamepadMask, jint buttonFlags, jbyte leftTrigger, jbyte rightTrigger, jshort leftStickX, jshort leftStickY, jshort rightStickX, jshort rightStickY) { @@ -38,12 +38,47 @@ Java_com_limelight_nvstream_jni_MoonBridge_sendMultiControllerInput(JNIEnv *env, leftTrigger, rightTrigger, leftStickX, leftStickY, rightStickX, rightStickY); } -JNIEXPORT void JNICALL -Java_com_limelight_nvstream_jni_MoonBridge_sendControllerInput(JNIEnv *env, jclass clazz, jshort buttonFlags, - jbyte leftTrigger, jbyte rightTrigger, - jshort leftStickX, jshort leftStickY, - jshort rightStickX, jshort rightStickY) { - LiSendControllerEvent(buttonFlags, leftTrigger, rightTrigger, leftStickX, leftStickY, rightStickX, rightStickY); +JNIEXPORT jint JNICALL +Java_com_limelight_nvstream_jni_MoonBridge_sendTouchEvent(JNIEnv *env, jclass clazz, + jbyte eventType, jint pointerId, + jfloat x, jfloat y, jfloat pressure) { + return LiSendTouchEvent(eventType, pointerId, x, y, pressure); +} + +JNIEXPORT jint JNICALL +Java_com_limelight_nvstream_jni_MoonBridge_sendPenEvent(JNIEnv *env, jclass clazz, jbyte eventType, + jbyte toolType, jbyte penButtons, + jfloat x, jfloat y, jfloat pressure, + jshort rotation, jbyte tiltX, + jbyte tiltY) { + return LiSendPenEvent(eventType, toolType, penButtons, x, y, pressure, rotation, tiltX, tiltY); +} + +JNIEXPORT jint JNICALL +Java_com_limelight_nvstream_jni_MoonBridge_sendControllerArrivalEvent(JNIEnv *env, jclass clazz, + jbyte controllerNumber, + jshort activeGamepadMask, + jbyte type, + jint supportedButtonFlags, + jshort capabilities) { + return LiSendControllerArrivalEvent(controllerNumber, activeGamepadMask, type, supportedButtonFlags, capabilities); +} + +JNIEXPORT jint JNICALL +Java_com_limelight_nvstream_jni_MoonBridge_sendControllerTouchEvent(JNIEnv *env, jclass clazz, + jbyte controllerNumber, + jbyte eventType, + jint pointerId, jfloat x, + jfloat y, jfloat pressure) { + return LiSendControllerTouchEvent(controllerNumber, eventType, pointerId, x, y, pressure); +} + +JNIEXPORT jint JNICALL +Java_com_limelight_nvstream_jni_MoonBridge_sendControllerMotionEvent(JNIEnv *env, jclass clazz, + jbyte controllerNumber, + jbyte motionType, jfloat x, + jfloat y, jfloat z) { + return LiSendControllerMotionEvent(controllerNumber, motionType, x, y, z); } JNIEXPORT void JNICALL