diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/GrabListener.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/GrabListener.java new file mode 100644 index 000000000..8cfacb7bc --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/GrabListener.java @@ -0,0 +1,5 @@ +package net.kdt.pojavlaunch; + +public interface GrabListener { + void onGrabState(boolean isGrabbing); +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java index 0792f7bd2..475cab056 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java @@ -81,6 +81,8 @@ public class MainActivity extends BaseActivity { MCOptionUtils.load(Tools.getGameDirPath(minecraftProfile)); GameService.startService(this); initLayout(R.layout.activity_basemain); + CallbackBridge.addGrabListener(touchpad); + CallbackBridge.addGrabListener(minecraftGLView); // Enabling this on TextureView results in a broken white result if(PREF_USE_ALTERNATE_SURFACE) @@ -260,6 +262,13 @@ public class MainActivity extends BaseActivity { super.onStop(); } + @Override + protected void onDestroy() { + super.onDestroy(); + CallbackBridge.removeGrabListener(touchpad); + CallbackBridge.removeGrabListener(minecraftGLView); + } + @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java index 5efed244a..8a7a3c9ba 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java @@ -43,7 +43,8 @@ import org.lwjgl.glfw.CallbackBridge; /** * Class dealing with showing minecraft surface and taking inputs to dispatch them to minecraft */ -public class MinecraftGLSurface extends View { +public class MinecraftGLSurface extends View implements GrabListener{ + Handler uiThreadHandler = new Handler(); /* Gamepad object for gamepad inputs, instantiated on need */ private Gamepad mGamepad = null; /* Pointer Debug textview, used to show info about the pointer state */ @@ -410,12 +411,6 @@ public class MinecraftGLSurface extends View { break; } if(mouseCursorIndex == -1) return false; // we cant consoom that, theres no mice! - if(CallbackBridge.isGrabbing()) { - if(MainActivity.isAndroid8OrHigher() && !hasPointerCapture()){ - requestFocus(); - requestPointerCapture(); - } - } switch(event.getActionMasked()) { case MotionEvent.ACTION_HOVER_MOVE: CallbackBridge.mouseX = (event.getX(mouseCursorIndex) * mScaleFactor); @@ -444,10 +439,6 @@ public class MinecraftGLSurface extends View { public boolean dispatchCapturedPointerEvent(MotionEvent e) { CallbackBridge.mouseX += (e.getX()* mScaleFactor); CallbackBridge.mouseY += (e.getY()* mScaleFactor); - if(!CallbackBridge.isGrabbing()){ - releasePointerCapture(); - clearFocus(); - } if (mPointerDebugTextView.getVisibility() == View.VISIBLE && !debugErrored) { StringBuilder builder = new StringBuilder(); @@ -653,6 +644,25 @@ public class MinecraftGLSurface extends View { }, "JVM Main thread").start(); } + @Override + public void onGrabState(boolean isGrabbing) { + uiThreadHandler.post(()->updateGrabState(isGrabbing)); + } + + private void updateGrabState(boolean isGrabbing) { + if(MainActivity.isAndroid8OrHigher()) { + if (isGrabbing && !hasPointerCapture()) { + requestFocus(); + requestPointerCapture(); + } + + if (!isGrabbing && hasPointerCapture()) { + releasePointerCapture(); + clearFocus(); + } + } + } + /** A small interface called when the listener is ready for the first time */ public interface SurfaceReadyListener { void isReady(); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Touchpad.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Touchpad.java index 0fb7dfc21..1a7342a53 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Touchpad.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Touchpad.java @@ -6,6 +6,7 @@ import static net.kdt.pojavlaunch.prefs.LauncherPreferences.DEFAULT_PREF; import android.content.Context; import android.os.Build; +import android.os.Handler; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; @@ -25,13 +26,14 @@ import org.lwjgl.glfw.CallbackBridge; /** * Class dealing with the virtual mouse */ -public class Touchpad extends FrameLayout { +public class Touchpad extends FrameLayout implements GrabListener{ /* Whether the Touchpad should be displayed */ private boolean mDisplayState; /* Mouse pointer icon used by the touchpad */ private final ImageView mMousePointerImageView = new ImageView(getContext()); /* Detect a classic android Tap */ private final GestureDetector mSingleTapDetector = new GestureDetector(getContext(), new SingleTapConfirm()); + private final Handler uiThreadHandler = new Handler(); /* Resolution scaler option, allow downsizing a window */ private final float mScaleFactor = DEFAULT_PREF.getInt("resolutionRatio",100)/100f; /* Current pointer ID to move the mouse */ @@ -41,19 +43,6 @@ public class Touchpad extends FrameLayout { /* Last first pointer positions non-scaled, used to scroll distance */ private float mScrollLastInitialX, mScrollLastInitialY; /* Handler and message to check if we are grabbing */ - private final Runnable mGrabRunnable = new Runnable() { - @Override - public void run() { - if (!CallbackBridge.isGrabbing() && mDisplayState && getVisibility() != VISIBLE) { - enable(); - }else{ - if ((CallbackBridge.isGrabbing() && getVisibility() != View.GONE) || !mDisplayState && getVisibility() == VISIBLE) { - disable(); - } - } - postDelayed(this, 250); - } - }; public Touchpad(@NonNull Context context) { this(context, null); @@ -156,6 +145,10 @@ public class Touchpad extends FrameLayout { /** @return The new state, enabled or disabled */ public boolean switchState(){ mDisplayState = !mDisplayState; + if(!CallbackBridge.isGrabbing()) { + if(mDisplayState) enable(); + else disable(); + } return mDisplayState; } @@ -184,7 +177,18 @@ public class Touchpad extends FrameLayout { // When the game is grabbing, we should not display the mouse disable(); mDisplayState = false; - postDelayed(mGrabRunnable, 250); } + @Override + public void onGrabState(boolean isGrabbing) { + uiThreadHandler.post(()->updateGrabState(isGrabbing)); + } + private void updateGrabState(boolean isGrabbing) { + if(!isGrabbing) { + if(mDisplayState && getVisibility() != VISIBLE) enable(); + if(!mDisplayState && getVisibility() == VISIBLE) disable(); + }else{ + if(getVisibility() != View.GONE) disable(); + } + } } diff --git a/app_pojavlauncher/src/main/java/org/lwjgl/glfw/CallbackBridge.java b/app_pojavlauncher/src/main/java/org/lwjgl/glfw/CallbackBridge.java index 0b6b43a95..e2abaaf6e 100644 --- a/app_pojavlauncher/src/main/java/org/lwjgl/glfw/CallbackBridge.java +++ b/app_pojavlauncher/src/main/java/org/lwjgl/glfw/CallbackBridge.java @@ -2,12 +2,17 @@ package org.lwjgl.glfw; import net.kdt.pojavlaunch.*; import android.content.*; +import android.telecom.Call; import android.view.Choreographer; +import java.lang.ref.WeakReference; +import java.util.ArrayList; + public class CallbackBridge { public static Choreographer sChoreographer = Choreographer.getInstance(); private static boolean isGrabbing = false; - private static long lastGrabTime = System.currentTimeMillis(); + private static final ArrayList grabListeners = new ArrayList<>(); + public static final int ANDROID_TYPE_GRAB_STATE = 0; public static final int CLIPBOARD_COPY = 2000; @@ -118,11 +123,6 @@ public class CallbackBridge { public static boolean isGrabbing() { // Avoid going through the JNI each time. - long currentTime = System.currentTimeMillis(); - if (currentTime - lastGrabTime > 250){ - isGrabbing = nativeIsGrabbing(); - lastGrabTime = currentTime; - } return isGrabbing; } @@ -206,6 +206,24 @@ public class CallbackBridge { } } + private static void onGrabStateChanged(boolean grabbing) { + isGrabbing = grabbing; + synchronized (grabListeners) { + for (GrabListener g : grabListeners) g.onGrabState(grabbing); + } + } + public static void addGrabListener(GrabListener listener) { + synchronized (grabListeners) { + listener.onGrabState(isGrabbing); + grabListeners.add(listener); + } + } + public static void removeGrabListener(GrabListener listener) { + synchronized (grabListeners) { + grabListeners.remove(listener); + } + } + public static native void nativeSetUseInputStackQueue(boolean useInputStackQueue); public static native boolean nativeAttachThreadToOther(boolean isAndroid, boolean isUsePushPoll); diff --git a/app_pojavlauncher/src/main/jni/input_bridge_v3.c b/app_pojavlauncher/src/main/jni/input_bridge_v3.c index f5d220a8c..b3fdff771 100644 --- a/app_pojavlauncher/src/main/jni/input_bridge_v3.c +++ b/app_pojavlauncher/src/main/jni/input_bridge_v3.c @@ -46,7 +46,11 @@ static float grabCursorX, grabCursorY, lastCursorX, lastCursorY; jclass inputBridgeClass_ANDROID, inputBridgeClass_JRE; jmethodID inputBridgeMethod_ANDROID, inputBridgeMethod_JRE; +jmethodID method_accessAndroidClipboard; +jmethodID method_onGrabStateChanged; +jmethodID method_glftSetWindowAttrib; jclass bridgeClazz; +jclass vmGlfwClass; jboolean isGrabbing; jint JNI_OnLoad(JavaVM* vm, void* reserved) { @@ -55,11 +59,14 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { dalvikJavaVMPtr = vm; (*vm)->GetEnv(vm, (void**) &dalvikJNIEnvPtr_ANDROID, JNI_VERSION_1_4); bridgeClazz = (*dalvikJNIEnvPtr_ANDROID)->NewGlobalRef(dalvikJNIEnvPtr_ANDROID,(*dalvikJNIEnvPtr_ANDROID) ->FindClass(dalvikJNIEnvPtr_ANDROID,"org/lwjgl/glfw/CallbackBridge")); - assert(bridgeClazz != NULL); + method_accessAndroidClipboard = (*dalvikJNIEnvPtr_ANDROID)->GetStaticMethodID(dalvikJNIEnvPtr_ANDROID, bridgeClazz, "accessAndroidClipboard", "(ILjava/lang/String;)Ljava/lang/String;"); + method_onGrabStateChanged = (*dalvikJNIEnvPtr_ANDROID)->GetStaticMethodID(dalvikJNIEnvPtr_ANDROID, bridgeClazz, "onGrabStateChanged", "(Z)V"); isUseStackQueueCall = JNI_FALSE; } else if (dalvikJavaVMPtr != vm) { runtimeJavaVMPtr = vm; (*vm)->GetEnv(vm, (void**) &runtimeJNIEnvPtr_JRE, JNI_VERSION_1_4); + vmGlfwClass = (*runtimeJNIEnvPtr_JRE)->NewGlobalRef(runtimeJNIEnvPtr_JRE, (*runtimeJNIEnvPtr_JRE)->FindClass(runtimeJNIEnvPtr_JRE, "org/lwjgl/glfw/GLFW")); + method_glftSetWindowAttrib = (*runtimeJNIEnvPtr_JRE)->GetStaticMethodID(runtimeJNIEnvPtr_JRE, vmGlfwClass, "glfwSetWindowAttrib", "(JII)V"); hookExec(); } @@ -225,9 +232,6 @@ JNIEXPORT jstring JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeClipboard(JNI (*dalvikJavaVMPtr)->AttachCurrentThread(dalvikJavaVMPtr, &dalvikEnv, NULL); assert(dalvikEnv != NULL); assert(bridgeClazz != NULL); - LOGD("Clipboard: Obtaining method\n"); - jmethodID bridgeMethod = (*dalvikEnv)->GetStaticMethodID(dalvikEnv, bridgeClazz, "accessAndroidClipboard", "(ILjava/lang/String;)Ljava/lang/String;"); - assert(bridgeMethod != NULL); LOGD("Clipboard: Converting string\n"); char *copySrcC = NULL; @@ -238,7 +242,7 @@ JNIEXPORT jstring JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeClipboard(JNI } LOGD("Clipboard: Calling 2nd\n"); - jstring pasteDst = convertStringJVM(dalvikEnv, env, (jstring) (*dalvikEnv)->CallStaticObjectMethod(dalvikEnv, bridgeClazz, bridgeMethod, action, copyDst)); + jstring pasteDst = convertStringJVM(dalvikEnv, env, (jstring) (*dalvikEnv)->CallStaticObjectMethod(dalvikEnv, bridgeClazz, method_accessAndroidClipboard, action, copyDst)); if (copySrc) { (*dalvikEnv)->DeleteLocalRef(dalvikEnv, copyDst); @@ -257,6 +261,10 @@ JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetInputRead } JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetGrabbing(JNIEnv* env, jclass clazz, jboolean grabbing, jint xset, jint yset) { + JNIEnv *dalvikEnv; + (*dalvikJavaVMPtr)->AttachCurrentThread(dalvikJavaVMPtr, &dalvikEnv, NULL); + (*dalvikEnv)->CallStaticVoidMethod(dalvikEnv, bridgeClazz, method_onGrabStateChanged, grabbing); + (*dalvikJavaVMPtr)->DetachCurrentThread(dalvikJavaVMPtr); isGrabbing = grabbing; if (isGrabbing == JNI_TRUE) { grabCursorX = xset; // savedWidth / 2; @@ -422,14 +430,9 @@ JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetWindowAttrib( return; } - jclass glfwClazz = (*runtimeJNIEnvPtr_JRE)->FindClass(runtimeJNIEnvPtr_JRE, "org/lwjgl/glfw/GLFW"); - assert(glfwClazz != NULL); - jmethodID glfwMethod = (*runtimeJNIEnvPtr_JRE)->GetStaticMethodID(runtimeJNIEnvPtr_JRE, glfwClazz, "glfwSetWindowAttrib", "(JII)V"); - assert(glfwMethod != NULL); - (*runtimeJNIEnvPtr_JRE)->CallStaticVoidMethod( runtimeJNIEnvPtr_JRE, - glfwClazz, glfwMethod, + vmGlfwClass, method_glftSetWindowAttrib, (jlong) showingWindow, attrib, value ); }