From 904a8286dce7e66c97c407d1f09e6a1e81731ebe Mon Sep 17 00:00:00 2001 From: khanhduytran0 Date: Wed, 14 Oct 2020 15:09:32 +0700 Subject: [PATCH] Re-write the input pipe to C code --- .../net/kdt/pojavlaunch/MainActivity.java | 16 +- .../java/org/lwjgl/glfw/CallbackBridge.java | 48 +++- app/src/main/jni/input_bridge.c | 214 +++++++++++++++++- app/src/main/jni/utils.h | 6 +- 4 files changed, 260 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/net/kdt/pojavlaunch/MainActivity.java b/app/src/main/java/net/kdt/pojavlaunch/MainActivity.java index d3fadd92f..87be41ba8 100644 --- a/app/src/main/java/net/kdt/pojavlaunch/MainActivity.java +++ b/app/src/main/java/net/kdt/pojavlaunch/MainActivity.java @@ -403,7 +403,7 @@ public class MainActivity extends LoggableActivity implements OnTouchListener, O switch (e.getActionMasked()) { case MotionEvent.ACTION_DOWN: // 0 case MotionEvent.ACTION_POINTER_DOWN: // 5 - CallbackBridge.sendGrabInitialPosUnset(); + CallbackBridge.sendPrepareGrabInitialPos(); isTouchInHotbar = hudKeyHandled != -1; if (isTouchInHotbar) { @@ -631,7 +631,7 @@ public class MainActivity extends LoggableActivity implements OnTouchListener, O switch (e.getActionMasked()) { case MotionEvent.ACTION_DOWN: // 0 case MotionEvent.ACTION_POINTER_DOWN: // 5 - CallbackBridge.sendGrabInitialPosUnset(); + CallbackBridge.sendPrepareGrabInitialPos(); CallbackBridge.sendMouseKeycode(!CallbackBridge.mouseLeft ? LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT : LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_LEFT, 0, true); initialX = x; @@ -693,6 +693,18 @@ public class MainActivity extends LoggableActivity implements OnTouchListener, O } }); minecraftGLView.setOnTouchListener(glTouchListener); + minecraftGLView.setOnGenericMotionListener(new OnGenericMotionListener(){ + @Override + public boolean onGenericMotion(View v, MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_SCROLL: + CallbackBridge.sendScroll((double) event.getAxisValue(MotionEvent.AXIS_VSCROLL), (double) event.getAxisValue(MotionEvent.AXIS_HSCROLL)); + break; + } + return true; + } + }); + minecraftGLView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener(){ private boolean isCalled = false; diff --git a/app/src/main/java/org/lwjgl/glfw/CallbackBridge.java b/app/src/main/java/org/lwjgl/glfw/CallbackBridge.java index b767f378c..df2146750 100644 --- a/app/src/main/java/org/lwjgl/glfw/CallbackBridge.java +++ b/app/src/main/java/org/lwjgl/glfw/CallbackBridge.java @@ -2,6 +2,7 @@ package org.lwjgl.glfw; import java.io.*; import java.util.*; import android.widget.*; +import net.kdt.pojavlaunch.*; public class CallbackBridge { public static final int JRE_TYPE_CURSOR_POS = 0; @@ -35,32 +36,43 @@ public class CallbackBridge { DEBUG_STRING.append("CursorPos=" + x + ", " + y + "\n"); mouseX = x; mouseY = y; - sendData(JRE_TYPE_CURSOR_POS, x, y); + nativeSendCursorPos(x, y); } - public static void sendGrabInitialPosUnset() { - DEBUG_STRING.append("Grab initial posititon uset"); - sendData(JRE_TYPE_GRAB_INITIAL_POS_UNSET); + public static void sendPrepareGrabInitialPos() { + DEBUG_STRING.append("Prepare set grab initial posititon"); + sendMouseKeycode(-1, 0, false); } public static void sendKeycode(int keycode, char keychar, int modifiers, boolean isDown) { DEBUG_STRING.append("KeyCode=" + keycode + ", Char=" + keychar); - sendData(JRE_TYPE_KEYCODE_CONTROL, keycode, Character.toString(keychar), Boolean.toString(isDown), modifiers); + // TODO CHECK: This may cause input issue, not receive input! + if (!nativeSendCharMods(keycode, modifiers) ||!nativeSendChar(keycode)) { + nativeSendKey(keycode, 0 /* scancode */, isDown ? 1 : 0, modifiers); + } + + // sendData(JRE_TYPE_KEYCODE_CONTROL, keycode, Character.toString(keychar), Boolean.toString(isDown), modifiers); } - public static void sendMouseKeycode(int keycode, int modifiers, boolean isDown) { - DEBUG_STRING.append("MouseKey=" + keycode + ", down=" + isDown + "\n"); + public static void sendMouseKeycode(int button, int modifiers, boolean isDown) { + DEBUG_STRING.append("MouseKey=" + button + ", down=" + isDown + "\n"); // if (isGrabbing()) DEBUG_STRING.append("MouseGrabStrace: " + android.util.Log.getStackTraceString(new Throwable()) + "\n"); - sendData(JRE_TYPE_MOUSE_KEYCODE_CONTROL, keycode, Boolean.toString(isDown), modifiers); + nativeSendMouseButton(button, isDown ? 1 : 0, modifiers); } public static void sendMouseKeycode(int keycode) { sendMouseKeycode(keycode, 0, true); sendMouseKeycode(keycode, 0, false); } + + public static void sendScroll(double xoffset, double yoffset) { + DEBUG_STRING.append("ScrollX=" + xoffset + ",ScrollY=" + yoffset); + nativeSendScroll(xoffset, yoffset); + } public static void sendUpdateWindowSize(int w, int h) { - sendData(JRE_TYPE_WINDOW_SIZE, w, h); + nativeSendFramebufferSize(w, h); + nativeSendWindowSize(w, h); } public static boolean isGrabbing() { @@ -76,7 +88,7 @@ public class CallbackBridge { break; } } - +/* private static String currData; public static void sendData(int type, Object... dataArr) { currData = ""; @@ -92,12 +104,26 @@ public class CallbackBridge { } nativeSendData(true, type, currData); } - private static native void nativeSendData(boolean isAndroid, int type, String data); +*/ + + private static native void nativeAttachThreadToJRE(); + private static native boolean nativeSendChar(int codepoint); + // GLFW: GLFWCharModsCallback deprecated, but is Minecraft still use? + private static native boolean nativeSendCharMods(int codepoint, int mods); + private static native void nativeSendCursorEnter(int entered); + private static native void nativeSendCursorPos(int x, int y); + private static native void nativeSendFramebufferSize(int width, int height); + private static native void nativeSendKey(int key, int scancode, int action, int mods); + private static native void nativeSendMouseButton(int button, int action, int mods); + private static native void nativeSendScroll(double xoffset, double yoffset); + private static native void nativeSendWindowSize(int width, int height); + public static native boolean nativeIsGrabbing(); static { System.loadLibrary("pojavexec"); + nativeAttachThreadToJRE(); } } diff --git a/app/src/main/jni/input_bridge.c b/app/src/main/jni/input_bridge.c index b50c41168..1d9f1a101 100644 --- a/app/src/main/jni/input_bridge.c +++ b/app/src/main/jni/input_bridge.c @@ -3,20 +3,24 @@ #include "utils.h" -jclass inputBridgeClass_ANDROID; -jmethodID inputBridgeMethod_ANDROID; +typedef void GLFW_invoke_Char_func(void* window, unsigned int codepoint); +typedef void GLFW_invoke_CharMods_func(void* window, unsigned int codepoint, int mods); +typedef void GLFW_invoke_CursorEnter_func(void* window, int entered); +typedef void GLFW_invoke_CursorPos_func(void* window, double xpos, double ypos); +typedef void GLFW_invoke_FramebufferSize_func(void* window, int width, int height); +typedef void GLFW_invoke_Key_func(void* window, int key, int scancode, int action, int mods); +typedef void GLFW_invoke_MouseButton_func(void* window, int button, int action, int mods); +typedef void GLFW_invoke_Scroll_func(void* window, double xoffset, double yoffset); +typedef void GLFW_invoke_WindowSize_func(void* window, int width, int height); -jclass inputBridgeClass_JRE; -jmethodID inputBridgeMethod_JRE; +JavaVM* firstJavaVM, secondJavaVM; +JNIEnv* firstJNIEnv, secondJNIEnv; + +jclass inputBridgeClass_ANDROID, inputBridgeClass_JRE; +jmethodID inputBridgeMethod_ANDROID, inputBridgeMethod_JRE; jboolean isGrabbing; -JavaVM* firstJavaVM; -JNIEnv* firstJNIEnv; - -JavaVM* secondJavaVM; -JNIEnv* secondJNIEnv; - jint JNI_OnLoad(JavaVM* vm, void* reserved) { if (dalvikJavaVMPtr == NULL) { //Save dalvik global JavaVM pointer @@ -62,6 +66,27 @@ void getJavaInputBridge(jclass* clazz, jmethodID* method) { } } +JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeAttachThreadToJRE(JNIEnv* env, jclass clazz, jboolean isAndroid) { + if (isAndroid == JNI_TRUE) { + firstJavaVM = dalvikJavaVMPtr; + firstJNIEnv = dalvikJNIEnvPtr_ANDROID; + secondJavaVM = runtimeJavaVMPtr; + + attachThreadIfNeed(&isAndroidThreadAttached, &runtimeJNIEnvPtr_ANDROID); + getJavaInputBridge(&inputBridgeClass_ANDROID, &inputBridgeMethod_ANDROID); + + isPrepareGrabPos = true; + } else { + firstJavaVM = runtimeJavaVMPtr; + firstJNIEnv = runtimeJNIEnvPtr_JRE; + secondJavaVM = dalvikJavaVMPtr; + + attachThreadIfNeed(&isRuntimeThreadAttached, &dalvikJNIEnvPtr_JRE); + getJavaInputBridge(&inputBridgeClass_JRE, &inputBridgeMethod_JRE); + } +} + +// TODO deprecate if implement fully callback in native JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendData(JNIEnv* env, jclass clazz, jboolean isAndroid, jint type, jstring data) { if (isAndroid == JNI_TRUE) { firstJavaVM = dalvikJavaVMPtr; @@ -105,3 +130,172 @@ JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeIsGrabbing(J return isGrabbing; } +JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendChar(JNIEnv* env, jclass clazz, jint codepoint) { + if (GLFW_invoke_Char && isInputReady) { + GLFW_invoke_Char(showingWindow, codepoint); + return JNI_TRUE; + } + return JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendCharMods(JNIEnv* env, jclass clazz, jint codepoint, jint mods) { + if (GLFW_invoke_CharMods && isInputReady) { + GLFW_invoke_CharMods(showingWindow, codepoint, mods); + return JNI_TRUE; + } + return JNI_FALSE; +} +/* +JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendCursorEnter(JNIEnv* env, jclass clazz, jint entered) { + if (GLFW_invoke_CursorEnter && isInputReady) { + GLFW_invoke_CursorEnter(showingWindow, entered); + } +} +*/ +JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendCursorPos(JNIEnv* env, jclass clazz, jint x, jint y) { + if (GLFW_invoke_CursorPos && isInputReady) { + if (GLFW_invoke_CursorEnter && !isCursorEntered) { + isCursorEntered = true; + GLFW_invoke_CursorEnter(showingWindow, entered); + } + + if (isGrabbing) { + if (!isPrepareGrabPos) { + grabCursorX += x - lastCursorX; + grabCursorY += y - lastCursorY; + } else { + isPrepareGrabPos = false; + lastCursorX = x; + lastCursotY = y; + return; + } + } + GLFW_invoke_CursorPos(showingWindow, (double) (isGrabbing ? grabCursorX : x), (double) (isGrabbing ? grabCursorY : y)); + lastCursorX = x; + lastCursotY = y; + } +} + +JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendFramebufferSize(JNIEnv* env, jclass clazz, jint width, jint height) { + if (GLFW_invoke_FramebufferSize && isInputReady) { + GLFW_invoke_FramebufferSize(showingWindow, width, height); + } +} + +JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendKey(JNIEnv* env, jclass clazz, jint key, jint scancode, jint action, jint mods) { + if (GLFW_invoke_Key && isInputReady) { + GLFW_invoke_Key(showingWindow, key, scancode, action, mods); + } +} + +JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendMouseButton(JNIEnv* env, jclass clazz, jint button, jint action, jint mods) { + if (isInputReady) { + if (button == -1) { + // Notify to prepare set new grab pos + isPrepareGrabPos = true; + } else if (GLFW_invoke_MouseButton) { + GLFW_invoke_MouseButton(showingWindow, button, action, mods); + } + } +} + +JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendScroll(JNIEnv* env, jclass clazz, jdouble xoffset, jdouble yoffset) { + if (GLFW_invoke_Scroll && isInputReady) { + GLFW_invoke_Scroll(showingWindow, (double) xoffset, (double) yoffset); + } +} + +JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendWindowSize(JNIEnv* env, jclass clazz, jint x, jint y) { + if (GLFW_invoke_WindowSize && isInputReady) { + GLFW_invoke_WindowSize(showingWindow, width, height); + } +} + +JNIEXPORT void JNICALL Java_org_lwjgl_glfw_GLFW_nativeSetShowingWindow(JNIEnv* env, jclass clazz, jlong window) { + showingWindow = (long) window; +} + +#define ADD_CALLBACK_WWIN(NAME) \ +GLFW_invoke_##NAME##_func* GLFW_invoke_##NAME; \ +JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSet##NAME##Callback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { \ + void* oldCallback = &GLFW_invoke_##NAME; \ + GLFW_invoke_##NAME = (GLFW_invoke_##NAME##_func*) (uintptr_t) callbackptr; \ + return (jlong) (uintptr_t) *oldCallback; \ +} + +ADD_CALLBACK_WWIN(Char); +ADD_CALLBACK_WWIN(CharMods); +ADD_CALLBACK_WWIN(CursorEnter); +ADD_CALLBACK_WWIN(CursorPos); +ADD_CALLBACK_WWIN(FramebufferSize); +ADD_CALLBACK_WWIN(MouseButton); +ADD_CALLBACK_WWIN(Scroll); +ADD_CALLBACK_WWIN(WindowSize); + +#undef ADD_CALLBACK_WWIN + +/* +JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetCharCallback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { + void* oldCallback = &GLFW_invoke_Char; + GLFW_invoke_Char = (GLFW_invoke_Char_func*) (uintptr_t) callbackptr; + return (jlong) (uintptr_t) *oldCallback; +} + +JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetCharModsCallback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { + void* oldCallback = &GLFW_invoke_CharMods; + GLFW_invoke_CharMods = (GLFW_invoke_CharMods_func*) (uintptr_t) callbackptr; + return (jlong) (uintptr_t) *oldCallback; +} + +JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetCursorEnterCallback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { + void* oldCallback = &GLFW_invoke_CursorEnter; + GLFW_invoke_CursorEnter = (GLFW_invoke_CursorEnter_func*) (uintptr_t) callbackptr; + return (jlong) (uintptr_t) *oldCallback; +} + +JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetCursorPosCallback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { + void* oldCallback = &GLFW_invoke_CursorPos; + GLFW_invoke_CursorPos = (GLFW_invoke_CursorPos_func*) (uintptr_t) callbackptr; + return (jlong) (uintptr_t) *oldCallback; +} + +JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetFramebufferSizeCallback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { + void* oldCallback = &GLFW_invoke_FramebufferSize; + GLFW_invoke_FramebufferSize = (GLFW_invoke_FramebufferSize_func*) (uintptr_t) callbackptr; + return (jlong) (uintptr_t) *oldCallback; +} + +JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetKeyCallback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { + void* oldCallback = &GLFW_invoke_Key; + GLFW_invoke_Key = (GLFW_invoke_Key_func*) (uintptr_t) callbackptr; + return (jlong) (uintptr_t) *oldCallback; +} + +JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetMouseButtonCallback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { + void* oldCallback = &GLFW_invoke_MouseButton; + GLFW_invoke_MouseButton = (GLFW_invoke_MouseButton_func*) (uintptr_t) callbackptr; + return (jlong) (uintptr_t) *oldCallback; +} + +JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetScrollCallback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { + void* oldCallback = &GLFW_invoke_Scroll; + GLFW_invoke_Scroll = (GLFW_invoke_Scroll_func*) (uintptr_t) callbackptr; + return (jlong) (uintptr_t) *oldCallback; +} + +JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetWindowSizeCallback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { + void* oldCallback = &GLFW_invoke_WindowSize; + GLFW_invoke_WindowSize = (GLFW_invoke_WindowSize_func*) (uintptr_t) callbackptr; + return (jlong) (uintptr_t) *oldCallback; +} +*/ + +/* + * Class: org_lwjgl_glfw_GLFW + * Method: nglfwSetInputReady + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetInputReady(JNIEnv *env, jclass cls) { + isInputReady = true; +} + diff --git a/app/src/main/jni/utils.h b/app/src/main/jni/utils.h index f35e51a7e..7273397d9 100644 --- a/app/src/main/jni/utils.h +++ b/app/src/main/jni/utils.h @@ -11,7 +11,11 @@ JavaVM* dalvikJavaVMPtr; JNIEnv* dalvikJNIEnvPtr_ANDROID; JNIEnv* dalvikJNIEnvPtr_JRE; -bool isAndroidThreadAttached, isRuntimeThreadAttached; +int grabCursorX, grabCursorY, lastCursorX, lastCursorY; + +long showingWindow; + +bool isAndroidThreadAttached, isRuntimeThreadAttached, isInputReady, isCursorEntered, isPrepareGrabPos; char** convert_to_char_array(JNIEnv *env, jobjectArray jstringArray); jobjectArray convert_from_char_array(JNIEnv *env, char **charArray, int num_rows);