mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-15 23:59:21 -04:00
Implement grab listening and use it for Android native mouse grab and Touchpad
This commit is contained in:
parent
5d2f61c2db
commit
c0e4d45ff3
@ -0,0 +1,5 @@
|
||||
package net.kdt.pojavlaunch;
|
||||
|
||||
public interface GrabListener {
|
||||
void onGrabState(boolean isGrabbing);
|
||||
}
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<GrabListener> 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);
|
||||
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user