mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-19 01:27:18 -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));
|
MCOptionUtils.load(Tools.getGameDirPath(minecraftProfile));
|
||||||
GameService.startService(this);
|
GameService.startService(this);
|
||||||
initLayout(R.layout.activity_basemain);
|
initLayout(R.layout.activity_basemain);
|
||||||
|
CallbackBridge.addGrabListener(touchpad);
|
||||||
|
CallbackBridge.addGrabListener(minecraftGLView);
|
||||||
|
|
||||||
// Enabling this on TextureView results in a broken white result
|
// Enabling this on TextureView results in a broken white result
|
||||||
if(PREF_USE_ALTERNATE_SURFACE)
|
if(PREF_USE_ALTERNATE_SURFACE)
|
||||||
@ -260,6 +262,13 @@ public class MainActivity extends BaseActivity {
|
|||||||
super.onStop();
|
super.onStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
CallbackBridge.removeGrabListener(touchpad);
|
||||||
|
CallbackBridge.removeGrabListener(minecraftGLView);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||||
super.onConfigurationChanged(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
|
* 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 */
|
/* Gamepad object for gamepad inputs, instantiated on need */
|
||||||
private Gamepad mGamepad = null;
|
private Gamepad mGamepad = null;
|
||||||
/* Pointer Debug textview, used to show info about the pointer state */
|
/* Pointer Debug textview, used to show info about the pointer state */
|
||||||
@ -410,12 +411,6 @@ public class MinecraftGLSurface extends View {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(mouseCursorIndex == -1) return false; // we cant consoom that, theres no mice!
|
if(mouseCursorIndex == -1) return false; // we cant consoom that, theres no mice!
|
||||||
if(CallbackBridge.isGrabbing()) {
|
|
||||||
if(MainActivity.isAndroid8OrHigher() && !hasPointerCapture()){
|
|
||||||
requestFocus();
|
|
||||||
requestPointerCapture();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch(event.getActionMasked()) {
|
switch(event.getActionMasked()) {
|
||||||
case MotionEvent.ACTION_HOVER_MOVE:
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
CallbackBridge.mouseX = (event.getX(mouseCursorIndex) * mScaleFactor);
|
CallbackBridge.mouseX = (event.getX(mouseCursorIndex) * mScaleFactor);
|
||||||
@ -444,10 +439,6 @@ public class MinecraftGLSurface extends View {
|
|||||||
public boolean dispatchCapturedPointerEvent(MotionEvent e) {
|
public boolean dispatchCapturedPointerEvent(MotionEvent e) {
|
||||||
CallbackBridge.mouseX += (e.getX()* mScaleFactor);
|
CallbackBridge.mouseX += (e.getX()* mScaleFactor);
|
||||||
CallbackBridge.mouseY += (e.getY()* mScaleFactor);
|
CallbackBridge.mouseY += (e.getY()* mScaleFactor);
|
||||||
if(!CallbackBridge.isGrabbing()){
|
|
||||||
releasePointerCapture();
|
|
||||||
clearFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mPointerDebugTextView.getVisibility() == View.VISIBLE && !debugErrored) {
|
if (mPointerDebugTextView.getVisibility() == View.VISIBLE && !debugErrored) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
@ -653,6 +644,25 @@ public class MinecraftGLSurface extends View {
|
|||||||
}, "JVM Main thread").start();
|
}, "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 */
|
/** A small interface called when the listener is ready for the first time */
|
||||||
public interface SurfaceReadyListener {
|
public interface SurfaceReadyListener {
|
||||||
void isReady();
|
void isReady();
|
||||||
|
@ -6,6 +6,7 @@ import static net.kdt.pojavlaunch.prefs.LauncherPreferences.DEFAULT_PREF;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.GestureDetector;
|
import android.view.GestureDetector;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
@ -25,13 +26,14 @@ import org.lwjgl.glfw.CallbackBridge;
|
|||||||
/**
|
/**
|
||||||
* Class dealing with the virtual mouse
|
* Class dealing with the virtual mouse
|
||||||
*/
|
*/
|
||||||
public class Touchpad extends FrameLayout {
|
public class Touchpad extends FrameLayout implements GrabListener{
|
||||||
/* Whether the Touchpad should be displayed */
|
/* Whether the Touchpad should be displayed */
|
||||||
private boolean mDisplayState;
|
private boolean mDisplayState;
|
||||||
/* Mouse pointer icon used by the touchpad */
|
/* Mouse pointer icon used by the touchpad */
|
||||||
private final ImageView mMousePointerImageView = new ImageView(getContext());
|
private final ImageView mMousePointerImageView = new ImageView(getContext());
|
||||||
/* Detect a classic android Tap */
|
/* Detect a classic android Tap */
|
||||||
private final GestureDetector mSingleTapDetector = new GestureDetector(getContext(), new SingleTapConfirm());
|
private final GestureDetector mSingleTapDetector = new GestureDetector(getContext(), new SingleTapConfirm());
|
||||||
|
private final Handler uiThreadHandler = new Handler();
|
||||||
/* Resolution scaler option, allow downsizing a window */
|
/* Resolution scaler option, allow downsizing a window */
|
||||||
private final float mScaleFactor = DEFAULT_PREF.getInt("resolutionRatio",100)/100f;
|
private final float mScaleFactor = DEFAULT_PREF.getInt("resolutionRatio",100)/100f;
|
||||||
/* Current pointer ID to move the mouse */
|
/* 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 */
|
/* Last first pointer positions non-scaled, used to scroll distance */
|
||||||
private float mScrollLastInitialX, mScrollLastInitialY;
|
private float mScrollLastInitialX, mScrollLastInitialY;
|
||||||
/* Handler and message to check if we are grabbing */
|
/* 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) {
|
public Touchpad(@NonNull Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
@ -156,6 +145,10 @@ public class Touchpad extends FrameLayout {
|
|||||||
/** @return The new state, enabled or disabled */
|
/** @return The new state, enabled or disabled */
|
||||||
public boolean switchState(){
|
public boolean switchState(){
|
||||||
mDisplayState = !mDisplayState;
|
mDisplayState = !mDisplayState;
|
||||||
|
if(!CallbackBridge.isGrabbing()) {
|
||||||
|
if(mDisplayState) enable();
|
||||||
|
else disable();
|
||||||
|
}
|
||||||
return mDisplayState;
|
return mDisplayState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +177,18 @@ public class Touchpad extends FrameLayout {
|
|||||||
// When the game is grabbing, we should not display the mouse
|
// When the game is grabbing, we should not display the mouse
|
||||||
disable();
|
disable();
|
||||||
mDisplayState = false;
|
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 net.kdt.pojavlaunch.*;
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
|
import android.telecom.Call;
|
||||||
import android.view.Choreographer;
|
import android.view.Choreographer;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class CallbackBridge {
|
public class CallbackBridge {
|
||||||
public static Choreographer sChoreographer = Choreographer.getInstance();
|
public static Choreographer sChoreographer = Choreographer.getInstance();
|
||||||
private static boolean isGrabbing = false;
|
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 ANDROID_TYPE_GRAB_STATE = 0;
|
||||||
|
|
||||||
public static final int CLIPBOARD_COPY = 2000;
|
public static final int CLIPBOARD_COPY = 2000;
|
||||||
@ -118,11 +123,6 @@ public class CallbackBridge {
|
|||||||
|
|
||||||
public static boolean isGrabbing() {
|
public static boolean isGrabbing() {
|
||||||
// Avoid going through the JNI each time.
|
// Avoid going through the JNI each time.
|
||||||
long currentTime = System.currentTimeMillis();
|
|
||||||
if (currentTime - lastGrabTime > 250){
|
|
||||||
isGrabbing = nativeIsGrabbing();
|
|
||||||
lastGrabTime = currentTime;
|
|
||||||
}
|
|
||||||
return isGrabbing;
|
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 void nativeSetUseInputStackQueue(boolean useInputStackQueue);
|
||||||
public static native boolean nativeAttachThreadToOther(boolean isAndroid, boolean isUsePushPoll);
|
public static native boolean nativeAttachThreadToOther(boolean isAndroid, boolean isUsePushPoll);
|
||||||
|
|
||||||
|
@ -46,7 +46,11 @@ static float grabCursorX, grabCursorY, lastCursorX, lastCursorY;
|
|||||||
|
|
||||||
jclass inputBridgeClass_ANDROID, inputBridgeClass_JRE;
|
jclass inputBridgeClass_ANDROID, inputBridgeClass_JRE;
|
||||||
jmethodID inputBridgeMethod_ANDROID, inputBridgeMethod_JRE;
|
jmethodID inputBridgeMethod_ANDROID, inputBridgeMethod_JRE;
|
||||||
|
jmethodID method_accessAndroidClipboard;
|
||||||
|
jmethodID method_onGrabStateChanged;
|
||||||
|
jmethodID method_glftSetWindowAttrib;
|
||||||
jclass bridgeClazz;
|
jclass bridgeClazz;
|
||||||
|
jclass vmGlfwClass;
|
||||||
jboolean isGrabbing;
|
jboolean isGrabbing;
|
||||||
|
|
||||||
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||||
@ -55,11 +59,14 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||||||
dalvikJavaVMPtr = vm;
|
dalvikJavaVMPtr = vm;
|
||||||
(*vm)->GetEnv(vm, (void**) &dalvikJNIEnvPtr_ANDROID, JNI_VERSION_1_4);
|
(*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"));
|
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;
|
isUseStackQueueCall = JNI_FALSE;
|
||||||
} else if (dalvikJavaVMPtr != vm) {
|
} else if (dalvikJavaVMPtr != vm) {
|
||||||
runtimeJavaVMPtr = vm;
|
runtimeJavaVMPtr = vm;
|
||||||
(*vm)->GetEnv(vm, (void**) &runtimeJNIEnvPtr_JRE, JNI_VERSION_1_4);
|
(*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();
|
hookExec();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,9 +232,6 @@ JNIEXPORT jstring JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeClipboard(JNI
|
|||||||
(*dalvikJavaVMPtr)->AttachCurrentThread(dalvikJavaVMPtr, &dalvikEnv, NULL);
|
(*dalvikJavaVMPtr)->AttachCurrentThread(dalvikJavaVMPtr, &dalvikEnv, NULL);
|
||||||
assert(dalvikEnv != NULL);
|
assert(dalvikEnv != NULL);
|
||||||
assert(bridgeClazz != 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");
|
LOGD("Clipboard: Converting string\n");
|
||||||
char *copySrcC = NULL;
|
char *copySrcC = NULL;
|
||||||
@ -238,7 +242,7 @@ JNIEXPORT jstring JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeClipboard(JNI
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOGD("Clipboard: Calling 2nd\n");
|
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) {
|
if (copySrc) {
|
||||||
(*dalvikEnv)->DeleteLocalRef(dalvikEnv, copyDst);
|
(*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) {
|
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;
|
isGrabbing = grabbing;
|
||||||
if (isGrabbing == JNI_TRUE) {
|
if (isGrabbing == JNI_TRUE) {
|
||||||
grabCursorX = xset; // savedWidth / 2;
|
grabCursorX = xset; // savedWidth / 2;
|
||||||
@ -422,14 +430,9 @@ JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetWindowAttrib(
|
|||||||
return;
|
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)->CallStaticVoidMethod(
|
||||||
runtimeJNIEnvPtr_JRE,
|
runtimeJNIEnvPtr_JRE,
|
||||||
glfwClazz, glfwMethod,
|
vmGlfwClass, method_glftSetWindowAttrib,
|
||||||
(jlong) showingWindow, attrib, value
|
(jlong) showingWindow, attrib, value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user