mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-12 22:26:56 -04:00
Feat[mouse]: always grab the mouse and use the touchpad as the cursor.
This commit is contained in:
parent
5884451735
commit
aab37d1fef
@ -10,6 +10,7 @@ import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
@ -28,6 +29,7 @@ import androidx.annotation.RequiresApi;
|
||||
import net.kdt.pojavlaunch.customcontrols.ControlLayout;
|
||||
import net.kdt.pojavlaunch.customcontrols.gamepad.Gamepad;
|
||||
import net.kdt.pojavlaunch.customcontrols.mouse.AbstractTouchpad;
|
||||
import net.kdt.pojavlaunch.customcontrols.mouse.AndroidPointerCapture;
|
||||
import net.kdt.pojavlaunch.customcontrols.mouse.InGUIEventProcessor;
|
||||
import net.kdt.pojavlaunch.customcontrols.mouse.InGameEventProcessor;
|
||||
import net.kdt.pojavlaunch.customcontrols.mouse.TouchEventProcessor;
|
||||
@ -76,6 +78,7 @@ public class MinecraftGLSurface extends View implements GrabListener {
|
||||
private final InGameEventProcessor mIngameProcessor = new InGameEventProcessor(mSensitivityFactor);
|
||||
private final InGUIEventProcessor mInGUIProcessor = new InGUIEventProcessor(mScaleFactor);
|
||||
private TouchEventProcessor mCurrentTouchProcessor = mInGUIProcessor;
|
||||
private AndroidPointerCapture mPointerCapture;
|
||||
private boolean mLastGrabState = false;
|
||||
|
||||
public MinecraftGLSurface(Context context) {
|
||||
@ -87,6 +90,12 @@ public class MinecraftGLSurface extends View implements GrabListener {
|
||||
setFocusable(true);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void setUpPointerCapture(AbstractTouchpad touchpad) {
|
||||
if(mPointerCapture != null) mPointerCapture.detach();
|
||||
mPointerCapture = new AndroidPointerCapture(touchpad, this, mScaleFactor);
|
||||
}
|
||||
|
||||
/** Initialize the view and all its settings
|
||||
* @param isAlreadyRunning set to true to tell the view that the game is already running
|
||||
* (only updates the window without calling the start listener)
|
||||
@ -94,6 +103,7 @@ public class MinecraftGLSurface extends View implements GrabListener {
|
||||
* when the cursor is not grabbed
|
||||
*/
|
||||
public void start(boolean isAlreadyRunning, AbstractTouchpad touchpad){
|
||||
if(MainActivity.isAndroid8OrHigher()) setUpPointerCapture(touchpad);
|
||||
mInGUIProcessor.setAbstractTouchpad(touchpad);
|
||||
if(LauncherPreferences.PREF_USE_ALTERNATE_SURFACE){
|
||||
SurfaceView surfaceView = new SurfaceView(getContext());
|
||||
@ -162,7 +172,6 @@ public class MinecraftGLSurface extends View implements GrabListener {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The touch event for both grabbed an non-grabbed mouse state on the touch screen
|
||||
* Does not cover the virtual mouse touchpad
|
||||
@ -175,7 +184,14 @@ public class MinecraftGLSurface extends View implements GrabListener {
|
||||
|
||||
// Looking for a mouse to handle, won't have an effect if no mouse exists.
|
||||
for (int i = 0; i < e.getPointerCount(); i++) {
|
||||
if(e.getToolType(i) != MotionEvent.TOOL_TYPE_MOUSE && e.getToolType(i) != MotionEvent.TOOL_TYPE_STYLUS ) continue;
|
||||
int toolType = e.getToolType(i);
|
||||
if(toolType == MotionEvent.TOOL_TYPE_MOUSE &&
|
||||
MainActivity.isAndroid8OrHigher() &&
|
||||
mPointerCapture != null) {
|
||||
mPointerCapture.handleAutomaticCapture();
|
||||
return true;
|
||||
}
|
||||
if(toolType != MotionEvent.TOOL_TYPE_STYLUS) continue;
|
||||
|
||||
// Mouse found
|
||||
if(CallbackBridge.isGrabbing()) return false;
|
||||
@ -232,32 +248,6 @@ public class MinecraftGLSurface extends View implements GrabListener {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO MOVE THIS SOMEWHERE ELSE
|
||||
/** The input event for mouse with a captured pointer */
|
||||
@RequiresApi(26)
|
||||
@Override
|
||||
public boolean dispatchCapturedPointerEvent(MotionEvent e) {
|
||||
CallbackBridge.mouseX += (e.getX()* mScaleFactor);
|
||||
CallbackBridge.mouseY += (e.getY()* mScaleFactor);
|
||||
|
||||
// Position is updated by many events, hence it is send regardless of the event value
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
|
||||
switch (e.getActionMasked()) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
return true;
|
||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||
return sendMouseButtonUnconverted(e.getActionButton(), true);
|
||||
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||
return sendMouseButtonUnconverted(e.getActionButton(), false);
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
CallbackBridge.sendScroll(e.getAxisValue(MotionEvent.AXIS_HSCROLL), e.getAxisValue(MotionEvent.AXIS_VSCROLL));
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** The event for keyboard/ gamepad button inputs */
|
||||
public boolean processKeyEvent(KeyEvent event) {
|
||||
//Log.i("KeyEvent", event.toString());
|
||||
@ -404,29 +394,6 @@ public class MinecraftGLSurface extends View implements GrabListener {
|
||||
mCurrentTouchProcessor = pickEventProcessor(isGrabbing);
|
||||
mLastGrabState = isGrabbing;
|
||||
}
|
||||
if(!MainActivity.isAndroid8OrHigher()) return;
|
||||
|
||||
boolean hasPointerCapture = hasPointerCapture();
|
||||
if(isGrabbing){
|
||||
if(!hasPointerCapture) {
|
||||
requestFocus();
|
||||
if(hasWindowFocus()) requestPointerCapture();
|
||||
// Otherwise, onWindowFocusChanged() would get called.
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(hasPointerCapture) {
|
||||
releasePointerCapture();
|
||||
clearFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
||||
super.onWindowFocusChanged(hasWindowFocus);
|
||||
if(hasWindowFocus && CallbackBridge.isGrabbing() &&
|
||||
MainActivity.isAndroid8OrHigher()) requestPointerCapture();
|
||||
}
|
||||
|
||||
/** A small interface called when the listener is ready for the first time */
|
||||
|
@ -1,6 +1,37 @@
|
||||
package net.kdt.pojavlaunch.customcontrols.mouse;
|
||||
|
||||
public interface AbstractTouchpad {
|
||||
/**
|
||||
* Get the supposed display state of the mouse (whether it should be shown when the user is in a GUI)
|
||||
* Note that this does *not* reflect the actual visibility state of the mouse
|
||||
* @return current supposed enabled state
|
||||
*/
|
||||
boolean getDisplayState();
|
||||
|
||||
/**
|
||||
* Apply a motion vector to the mouse in form of a two-entry float array. This will move the mouse
|
||||
* on the screen and send the new cursor position to the game.
|
||||
* @param vector the array that contains the vector
|
||||
*/
|
||||
void applyMotionVector(float[] vector);
|
||||
|
||||
/**
|
||||
* Apply a motion vector to the mouse in form of the separate X/Y coordinates. This will move the mouse
|
||||
* on the screen and send the new cursor position to the game.
|
||||
* @param x the relative X coordinate of the vector
|
||||
* @param y the relative Y coordinate for the vector
|
||||
*/
|
||||
void applyMotionVector(float x, float y);
|
||||
|
||||
/**
|
||||
* Sets the state of the touchpad to "enabled"
|
||||
* @param supposed if set to true, this will set the supposed display state to enabled but may not
|
||||
* affect the touchpad until internal conditions are met
|
||||
* if set to false it will turn the touchpad on regardless of internal conditions
|
||||
*/
|
||||
void enable(boolean supposed);
|
||||
/**
|
||||
* Sets the state of the touchpad to "disabled".
|
||||
*/
|
||||
void disable();
|
||||
}
|
||||
|
@ -0,0 +1,85 @@
|
||||
package net.kdt.pojavlaunch.customcontrols.mouse;
|
||||
|
||||
import android.os.Build;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import net.kdt.pojavlaunch.MainActivity;
|
||||
import net.kdt.pojavlaunch.MinecraftGLSurface;
|
||||
|
||||
import org.lwjgl.glfw.CallbackBridge;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChangeListener, View.OnCapturedPointerListener {
|
||||
private final AbstractTouchpad mTouchpad;
|
||||
private final View mHostView;
|
||||
private final float mScaleFactor;
|
||||
|
||||
public AndroidPointerCapture(AbstractTouchpad touchpad, View hostView, float scaleFactor) {
|
||||
this.mScaleFactor = scaleFactor;
|
||||
this.mTouchpad = touchpad;
|
||||
this.mHostView = hostView;
|
||||
hostView.setOnCapturedPointerListener(this);
|
||||
hostView.getViewTreeObserver().addOnWindowFocusChangeListener(this);
|
||||
}
|
||||
|
||||
private void enableTouchpadIfNecessary() {
|
||||
if(!mTouchpad.getDisplayState()) mTouchpad.enable(true);
|
||||
}
|
||||
|
||||
public void handleAutomaticCapture() {
|
||||
if(!CallbackBridge.isGrabbing()) return;
|
||||
if(mHostView.hasPointerCapture()) {
|
||||
enableTouchpadIfNecessary();
|
||||
return;
|
||||
}
|
||||
if(!mHostView.hasWindowFocus()) {
|
||||
mHostView.requestFocus();
|
||||
} else {
|
||||
mHostView.requestPointerCapture();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCapturedPointer(View view, MotionEvent event) {
|
||||
if(!CallbackBridge.isGrabbing()) {
|
||||
enableTouchpadIfNecessary();
|
||||
mTouchpad.applyMotionVector(event.getX(), event.getY());
|
||||
} else {
|
||||
// Position is updated by many events, hence it is send regardless of the event value
|
||||
CallbackBridge.mouseX += (event.getX() * mScaleFactor);
|
||||
CallbackBridge.mouseY += (event.getY() * mScaleFactor);
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
}
|
||||
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
return true;
|
||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||
return MinecraftGLSurface.sendMouseButtonUnconverted(event.getActionButton(), true);
|
||||
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||
return MinecraftGLSurface.sendMouseButtonUnconverted(event.getActionButton(), false);
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
CallbackBridge.sendScroll(
|
||||
event.getAxisValue(MotionEvent.AXIS_HSCROLL),
|
||||
event.getAxisValue(MotionEvent.AXIS_VSCROLL)
|
||||
);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
if(hasFocus && MainActivity.isAndroid8OrHigher()) mHostView.requestPointerCapture();
|
||||
}
|
||||
|
||||
public void detach() {
|
||||
mHostView.setOnCapturedPointerListener(null);
|
||||
mHostView.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
|
||||
}
|
||||
}
|
@ -41,13 +41,13 @@ public class Touchpad extends View implements GrabListener, AbstractTouchpad {
|
||||
}
|
||||
|
||||
/** Enable the touchpad */
|
||||
public void enable(){
|
||||
private void privateEnable(){
|
||||
setVisibility(VISIBLE);
|
||||
placeMouseAt(currentDisplayMetrics.widthPixels / 2f, currentDisplayMetrics.heightPixels / 2f);
|
||||
}
|
||||
|
||||
/** Disable the touchpad and hides the mouse */
|
||||
public void disable(){
|
||||
private void privateDisable(){
|
||||
setVisibility(GONE);
|
||||
}
|
||||
|
||||
@ -55,8 +55,8 @@ public class Touchpad extends View implements GrabListener, AbstractTouchpad {
|
||||
public boolean switchState(){
|
||||
mDisplayState = !mDisplayState;
|
||||
if(!CallbackBridge.isGrabbing()) {
|
||||
if(mDisplayState) enable();
|
||||
else disable();
|
||||
if(mDisplayState) privateEnable();
|
||||
else privateDisable();
|
||||
}
|
||||
return mDisplayState;
|
||||
}
|
||||
@ -112,10 +112,10 @@ public class Touchpad extends View implements GrabListener, AbstractTouchpad {
|
||||
}
|
||||
private void updateGrabState(boolean isGrabbing) {
|
||||
if(!isGrabbing) {
|
||||
if(mDisplayState && getVisibility() != VISIBLE) enable();
|
||||
if(!mDisplayState && getVisibility() == VISIBLE) disable();
|
||||
if(mDisplayState && getVisibility() != VISIBLE) privateEnable();
|
||||
if(!mDisplayState && getVisibility() == VISIBLE) privateDisable();
|
||||
}else{
|
||||
if(getVisibility() != View.GONE) disable();
|
||||
if(getVisibility() != View.GONE) privateDisable();
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,8 +126,30 @@ public class Touchpad extends View implements GrabListener, AbstractTouchpad {
|
||||
|
||||
@Override
|
||||
public void applyMotionVector(float[] vector) {
|
||||
mMouseX = Math.max(0, Math.min(currentDisplayMetrics.widthPixels, mMouseX + vector[0] * LauncherPreferences.PREF_MOUSESPEED));
|
||||
mMouseY = Math.max(0, Math.min(currentDisplayMetrics.heightPixels, mMouseY + vector[1] * LauncherPreferences.PREF_MOUSESPEED));
|
||||
applyMotionVector(vector[0], vector[1]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyMotionVector(float x, float y) {
|
||||
mMouseX = Math.max(0, Math.min(currentDisplayMetrics.widthPixels, mMouseX + x * LauncherPreferences.PREF_MOUSESPEED));
|
||||
mMouseY = Math.max(0, Math.min(currentDisplayMetrics.heightPixels, mMouseY + y * LauncherPreferences.PREF_MOUSESPEED));
|
||||
updateMousePosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable(boolean supposed) {
|
||||
if(mDisplayState) return;
|
||||
mDisplayState = true;
|
||||
if(supposed && CallbackBridge.isGrabbing()) return;
|
||||
privateEnable();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
if(!mDisplayState) return;
|
||||
mDisplayState = false;
|
||||
privateDisable();
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user