From 935bae1c09e007a778291e4db165a98810b7f207 Mon Sep 17 00:00:00 2001 From: artdeell Date: Tue, 19 Nov 2024 14:42:13 +0300 Subject: [PATCH] Fix[input_bridge]: fix Forge crashing the input bridge when app is rolled down Also fixes incorrect behaviour of GLFW regarding shown windows by default --- .../src/main/jni/input_bridge_v3.c | 33 +++++++++++++++---- .../src/main/java/org/lwjgl/glfw/GLFW.java | 22 ++++++++++--- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/app_pojavlauncher/src/main/jni/input_bridge_v3.c b/app_pojavlauncher/src/main/jni/input_bridge_v3.c index 4dff47f368..5b50a82282 100644 --- a/app_pojavlauncher/src/main/jni/input_bridge_v3.c +++ b/app_pojavlauncher/src/main/jni/input_bridge_v3.c @@ -545,17 +545,38 @@ JNIEXPORT void JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetShowingWindow(__attribut } JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetWindowAttrib(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jint attrib, jint value) { - if (!pojav_environ->showingWindow || !pojav_environ->isUseStackQueueCall) { + // Check for stack queue no longer necessary here as the JVM crash's origin is resolved + if (!pojav_environ->showingWindow) { // If the window is not shown, there is nothing to do yet. - // For Minecraft < 1.13, calling to JNI functions here crashes the JVM for some reason, therefore it is skipped for now. return; } - (*pojav_environ->runtimeJNIEnvPtr_JRE)->CallStaticVoidMethod( - pojav_environ->runtimeJNIEnvPtr_JRE, - pojav_environ->vmGlfwClass, pojav_environ->method_glftSetWindowAttrib, - (jlong) pojav_environ->showingWindow, attrib, value + // We cannot use pojav_environ->runtimeJNIEnvPtr_JRE here because that environment is attached + // on the thread that loaded pojavexec (which is the thread that first references the GLFW class) + // But this method is only called from the Android UI thread + + // Technically the better solution would be to have a permanently attached env pointer stored + // in environ for the Android UI thread but this is the only place that uses it + // (very rarely, only in lifecycle callbacks) so i dont care + + JavaVM* jvm = pojav_environ->runtimeJavaVMPtr; + JNIEnv *jvm_env = NULL; + jint env_result = (*jvm)->GetEnv(jvm, (void**)&jvm_env, JNI_VERSION_1_4); + if(env_result == JNI_EDETACHED) { + env_result = (*jvm)->AttachCurrentThread(jvm, &jvm_env, NULL); + } + if(env_result != JNI_OK) { + printf("input_bridge nativeSetWindowAttrib() JNI call failed: %i\n", env_result); + return; + } + + (*jvm_env)->CallStaticVoidMethod( + jvm_env, pojav_environ->vmGlfwClass, + pojav_environ->method_glftSetWindowAttrib, + (jlong) pojav_environ->showingWindow, attrib, value ); + + // Attaching every time is annoying, so stick the attachment to the Android GUI thread around } const static JNINativeMethod critical_fcns[] = { {"nativeSetUseInputStackQueue", "(Z)V", critical_set_stackqueue}, diff --git a/jre_lwjgl3glfw/src/main/java/org/lwjgl/glfw/GLFW.java b/jre_lwjgl3glfw/src/main/java/org/lwjgl/glfw/GLFW.java index dde2f39baf..3a1b9c74fb 100644 --- a/jre_lwjgl3glfw/src/main/java/org/lwjgl/glfw/GLFW.java +++ b/jre_lwjgl3glfw/src/main/java/org/lwjgl/glfw/GLFW.java @@ -505,6 +505,7 @@ public class GLFW private static ArrayMap mGLFWWindowMap; public static boolean mGLFWIsInputReady; private static boolean mGLFWInputPumping; + private static boolean mGLFWWindowVisibleOnCreation = true; public static final ByteBuffer keyDownBuffer = ByteBuffer.allocateDirect(317); public static final ByteBuffer mouseDownBuffer = ByteBuffer.allocateDirect(8); @@ -1000,11 +1001,15 @@ public static long glfwCreateWindow(int width, int height, CharSequence title, l win.height = mGLFWWindowHeight; win.title = title; - win.windowAttribs.put(GLFW_HOVERED, 1); - win.windowAttribs.put(GLFW_VISIBLE, 1); - mGLFWWindowMap.put(ptr, win); mainContext = ptr; + + if(mGLFWWindowVisibleOnCreation || monitor != 0) { + // Show window by default if GLFW_VISIBLE hint is specified on creation or + // if the monitor is nonnull (fullscreen requested) + glfwShowWindow(ptr); + } + return ptr; //Return our context } @@ -1021,7 +1026,9 @@ public static void glfwDestroyWindow(long window) { nglfwSetShowingWindow(mGLFWWindowMap.size() == 0 ? 0 : mGLFWWindowMap.keyAt(mGLFWWindowMap.size() - 1)); } - public static void glfwDefaultWindowHints() {} + public static void glfwDefaultWindowHints() { + mGLFWWindowVisibleOnCreation = true; + } public static void glfwGetWindowSize(long window, IntBuffer width, IntBuffer height) { if (width != null) width.put(internalGetWindow(window).width); @@ -1044,10 +1051,17 @@ public static void glfwSetWindowSize(long window, int width, int height) { } public static void glfwShowWindow(long window) { + GLFWWindowProperties win = internalGetWindow(window); + win.windowAttribs.put(GLFW_HOVERED, 1); + win.windowAttribs.put(GLFW_VISIBLE, 1); nglfwSetShowingWindow(window); } public static void glfwWindowHint(int hint, int value) { + if (hint == GLFW_VISIBLE) { + mGLFWWindowVisibleOnCreation = value == GLFW_TRUE; + return; + } long __functionAddress = Functions.SetWindowHint; invokeV(hint, value, __functionAddress); }