mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-13 14:51:51 -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.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.SurfaceTexture;
|
import android.graphics.SurfaceTexture;
|
||||||
|
import android.os.Build;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
@ -28,6 +29,7 @@ import androidx.annotation.RequiresApi;
|
|||||||
import net.kdt.pojavlaunch.customcontrols.ControlLayout;
|
import net.kdt.pojavlaunch.customcontrols.ControlLayout;
|
||||||
import net.kdt.pojavlaunch.customcontrols.gamepad.Gamepad;
|
import net.kdt.pojavlaunch.customcontrols.gamepad.Gamepad;
|
||||||
import net.kdt.pojavlaunch.customcontrols.mouse.AbstractTouchpad;
|
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.InGUIEventProcessor;
|
||||||
import net.kdt.pojavlaunch.customcontrols.mouse.InGameEventProcessor;
|
import net.kdt.pojavlaunch.customcontrols.mouse.InGameEventProcessor;
|
||||||
import net.kdt.pojavlaunch.customcontrols.mouse.TouchEventProcessor;
|
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 InGameEventProcessor mIngameProcessor = new InGameEventProcessor(mSensitivityFactor);
|
||||||
private final InGUIEventProcessor mInGUIProcessor = new InGUIEventProcessor(mScaleFactor);
|
private final InGUIEventProcessor mInGUIProcessor = new InGUIEventProcessor(mScaleFactor);
|
||||||
private TouchEventProcessor mCurrentTouchProcessor = mInGUIProcessor;
|
private TouchEventProcessor mCurrentTouchProcessor = mInGUIProcessor;
|
||||||
|
private AndroidPointerCapture mPointerCapture;
|
||||||
private boolean mLastGrabState = false;
|
private boolean mLastGrabState = false;
|
||||||
|
|
||||||
public MinecraftGLSurface(Context context) {
|
public MinecraftGLSurface(Context context) {
|
||||||
@ -87,6 +90,12 @@ public class MinecraftGLSurface extends View implements GrabListener {
|
|||||||
setFocusable(true);
|
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
|
/** Initialize the view and all its settings
|
||||||
* @param isAlreadyRunning set to true to tell the view that the game is already running
|
* @param isAlreadyRunning set to true to tell the view that the game is already running
|
||||||
* (only updates the window without calling the start listener)
|
* (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
|
* when the cursor is not grabbed
|
||||||
*/
|
*/
|
||||||
public void start(boolean isAlreadyRunning, AbstractTouchpad touchpad){
|
public void start(boolean isAlreadyRunning, AbstractTouchpad touchpad){
|
||||||
|
if(MainActivity.isAndroid8OrHigher()) setUpPointerCapture(touchpad);
|
||||||
mInGUIProcessor.setAbstractTouchpad(touchpad);
|
mInGUIProcessor.setAbstractTouchpad(touchpad);
|
||||||
if(LauncherPreferences.PREF_USE_ALTERNATE_SURFACE){
|
if(LauncherPreferences.PREF_USE_ALTERNATE_SURFACE){
|
||||||
SurfaceView surfaceView = new SurfaceView(getContext());
|
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
|
* The touch event for both grabbed an non-grabbed mouse state on the touch screen
|
||||||
* Does not cover the virtual mouse touchpad
|
* 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.
|
// Looking for a mouse to handle, won't have an effect if no mouse exists.
|
||||||
for (int i = 0; i < e.getPointerCount(); i++) {
|
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
|
// Mouse found
|
||||||
if(CallbackBridge.isGrabbing()) return false;
|
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 */
|
/** The event for keyboard/ gamepad button inputs */
|
||||||
public boolean processKeyEvent(KeyEvent event) {
|
public boolean processKeyEvent(KeyEvent event) {
|
||||||
//Log.i("KeyEvent", event.toString());
|
//Log.i("KeyEvent", event.toString());
|
||||||
@ -404,29 +394,6 @@ public class MinecraftGLSurface extends View implements GrabListener {
|
|||||||
mCurrentTouchProcessor = pickEventProcessor(isGrabbing);
|
mCurrentTouchProcessor = pickEventProcessor(isGrabbing);
|
||||||
mLastGrabState = 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 */
|
/** A small interface called when the listener is ready for the first time */
|
||||||
|
@ -1,6 +1,37 @@
|
|||||||
package net.kdt.pojavlaunch.customcontrols.mouse;
|
package net.kdt.pojavlaunch.customcontrols.mouse;
|
||||||
|
|
||||||
public interface AbstractTouchpad {
|
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();
|
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);
|
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 */
|
/** Enable the touchpad */
|
||||||
public void enable(){
|
private void privateEnable(){
|
||||||
setVisibility(VISIBLE);
|
setVisibility(VISIBLE);
|
||||||
placeMouseAt(currentDisplayMetrics.widthPixels / 2f, currentDisplayMetrics.heightPixels / 2f);
|
placeMouseAt(currentDisplayMetrics.widthPixels / 2f, currentDisplayMetrics.heightPixels / 2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Disable the touchpad and hides the mouse */
|
/** Disable the touchpad and hides the mouse */
|
||||||
public void disable(){
|
private void privateDisable(){
|
||||||
setVisibility(GONE);
|
setVisibility(GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,8 +55,8 @@ public class Touchpad extends View implements GrabListener, AbstractTouchpad {
|
|||||||
public boolean switchState(){
|
public boolean switchState(){
|
||||||
mDisplayState = !mDisplayState;
|
mDisplayState = !mDisplayState;
|
||||||
if(!CallbackBridge.isGrabbing()) {
|
if(!CallbackBridge.isGrabbing()) {
|
||||||
if(mDisplayState) enable();
|
if(mDisplayState) privateEnable();
|
||||||
else disable();
|
else privateDisable();
|
||||||
}
|
}
|
||||||
return mDisplayState;
|
return mDisplayState;
|
||||||
}
|
}
|
||||||
@ -112,10 +112,10 @@ public class Touchpad extends View implements GrabListener, AbstractTouchpad {
|
|||||||
}
|
}
|
||||||
private void updateGrabState(boolean isGrabbing) {
|
private void updateGrabState(boolean isGrabbing) {
|
||||||
if(!isGrabbing) {
|
if(!isGrabbing) {
|
||||||
if(mDisplayState && getVisibility() != VISIBLE) enable();
|
if(mDisplayState && getVisibility() != VISIBLE) privateEnable();
|
||||||
if(!mDisplayState && getVisibility() == VISIBLE) disable();
|
if(!mDisplayState && getVisibility() == VISIBLE) privateDisable();
|
||||||
}else{
|
}else{
|
||||||
if(getVisibility() != View.GONE) disable();
|
if(getVisibility() != View.GONE) privateDisable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,8 +126,30 @@ public class Touchpad extends View implements GrabListener, AbstractTouchpad {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyMotionVector(float[] vector) {
|
public void applyMotionVector(float[] vector) {
|
||||||
mMouseX = Math.max(0, Math.min(currentDisplayMetrics.widthPixels, mMouseX + vector[0] * LauncherPreferences.PREF_MOUSESPEED));
|
applyMotionVector(vector[0], vector[1]);
|
||||||
mMouseY = Math.max(0, Math.min(currentDisplayMetrics.heightPixels, mMouseY + vector[1] * LauncherPreferences.PREF_MOUSESPEED));
|
}
|
||||||
|
|
||||||
|
@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();
|
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