mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-09 20:51:37 -04:00
Many under the hood improvements:
- CPU overhead reduced - Compatibility increased - Fix digital triggers auto-disabling - Fix KeyEvents from DPAD
This commit is contained in:
parent
51e7c2fce8
commit
b4a21feaea
@ -1,6 +1,8 @@
|
|||||||
package net.kdt.pojavlaunch.customcontrols.gamepad;
|
package net.kdt.pojavlaunch.customcontrols.gamepad;
|
||||||
|
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
@ -34,7 +36,7 @@ public class Gamepad {
|
|||||||
private final BaseMainActivity gameActivity;
|
private final BaseMainActivity gameActivity;
|
||||||
private final ImageView pointerView;
|
private final ImageView pointerView;
|
||||||
|
|
||||||
private final GamepadDpad gamepadDpad = new GamepadDpad(this);
|
private final GamepadDpad gamepadDpad = new GamepadDpad();
|
||||||
|
|
||||||
private final GamepadJoystick leftJoystick;
|
private final GamepadJoystick leftJoystick;
|
||||||
private int currentJoystickDirection = DIRECTION_NONE;
|
private int currentJoystickDirection = DIRECTION_NONE;
|
||||||
@ -55,86 +57,37 @@ public class Gamepad {
|
|||||||
private GamepadMap currentMap = gameMap;
|
private GamepadMap currentMap = gameMap;
|
||||||
|
|
||||||
private boolean lastGrabbingState = true;
|
private boolean lastGrabbingState = true;
|
||||||
private MotionEvent lastMotionEvent = null;
|
private final boolean hasDigitalTriggers;
|
||||||
|
|
||||||
private final Thread mouseThread;
|
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||||
private final Runnable mouseRunnable;
|
|
||||||
private final Runnable switchStateRunnable;
|
private final Runnable switchStateRunnable;
|
||||||
private final Runnable motionEventRunnable;
|
|
||||||
|
|
||||||
public Gamepad(BaseMainActivity gameActivity, InputDevice inputDevice){
|
public Gamepad(BaseMainActivity gameActivity, InputDevice inputDevice){
|
||||||
Toast.makeText(gameActivity.getApplicationContext(),"GAMEPAD CREATED", Toast.LENGTH_LONG).show();
|
Toast.makeText(gameActivity.getApplicationContext(),"GAMEPAD CREATED", Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
leftJoystick = new GamepadJoystick(MotionEvent.AXIS_X, MotionEvent.AXIS_Y, inputDevice);
|
leftJoystick = new GamepadJoystick(MotionEvent.AXIS_X, MotionEvent.AXIS_Y, inputDevice);
|
||||||
rightJoystick = new GamepadJoystick(MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, inputDevice);
|
rightJoystick = new GamepadJoystick(MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, inputDevice);
|
||||||
|
hasDigitalTriggers = inputDevice.hasKeys(KeyEvent.KEYCODE_BUTTON_R2)[0];
|
||||||
|
|
||||||
this.gameActivity = gameActivity;
|
this.gameActivity = gameActivity;
|
||||||
pointerView = this.gameActivity.findViewById(R.id.console_pointer);
|
pointerView = this.gameActivity.findViewById(R.id.console_pointer);
|
||||||
pointerView.getDrawable().setFilterBitmap(false);
|
pointerView.getDrawable().setFilterBitmap(false);
|
||||||
notifyGUISizeChange(gameActivity.getMcScale());
|
notifyGUISizeChange(gameActivity.getMcScale());
|
||||||
|
|
||||||
|
Runnable handlerRunnable = new Runnable() {
|
||||||
mouseThread = new Thread("Gamepad Thread"){
|
|
||||||
long lastTime = System.nanoTime();
|
|
||||||
final double ticks = 60D;
|
|
||||||
final double ns = 1000000000 / ticks;
|
|
||||||
double delta = 0;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
while (!isInterrupted()) {
|
|
||||||
long now = System.nanoTime();
|
|
||||||
delta += (now - lastTime) / ns;
|
|
||||||
lastTime = now;
|
|
||||||
|
|
||||||
if(delta >= 1) {
|
|
||||||
updateGrabbingState();
|
updateGrabbingState();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
delta--;
|
handler.postDelayed(this, 16);
|
||||||
|
|
||||||
try{ sleep(Math.max((long) ( (1 - delta) * (1000/ticks) ), 0)); } catch (InterruptedException e) {e.printStackTrace();}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tick(){
|
|
||||||
//Update motion events, and the mouse position
|
|
||||||
if(lastMotionEvent != null)gameActivity.runOnUiThread(motionEventRunnable);
|
|
||||||
|
|
||||||
if(lastHorizontalValue != 0 || lastVerticalValue != 0){
|
|
||||||
GamepadJoystick currentJoystick = lastGrabbingState ? leftJoystick : rightJoystick;
|
|
||||||
|
|
||||||
acceleration = (mouseMagnitude - currentJoystick.getDeadzone())/(1 - currentJoystick.getDeadzone());
|
|
||||||
acceleration = Math.pow(acceleration, mouseMaxAcceleration);
|
|
||||||
|
|
||||||
if(acceleration > 1) acceleration = 1;
|
|
||||||
|
|
||||||
|
|
||||||
CallbackBridge.mouseX += Math.cos(mouseAngle) * acceleration * mouseSensitivity;
|
|
||||||
CallbackBridge.mouseY -= Math.sin(mouseAngle) * acceleration * mouseSensitivity;
|
|
||||||
|
|
||||||
gameActivity.runOnUiThread(mouseRunnable);
|
|
||||||
|
|
||||||
if(!lastGrabbingState){
|
|
||||||
CallbackBridge.mouseX = MathUtils.clamp(CallbackBridge.mouseX, 0, CallbackBridge.windowWidth);
|
|
||||||
CallbackBridge.mouseY = MathUtils.clamp(CallbackBridge.mouseY, 0, CallbackBridge.windowHeight);
|
|
||||||
placePointerView((int) (CallbackBridge.mouseX /gameActivity.scaleFactor), (int) (CallbackBridge.mouseY/gameActivity.scaleFactor));
|
|
||||||
}
|
|
||||||
|
|
||||||
gameActivity.mouse_x = CallbackBridge.mouseX;
|
|
||||||
gameActivity.mouse_y = CallbackBridge.mouseY;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mouseThread.setPriority(Thread.MAX_PRIORITY);
|
|
||||||
mouseThread.start();
|
|
||||||
|
|
||||||
|
handler.postDelayed(handlerRunnable, 16);
|
||||||
|
|
||||||
//Initialize runnables to be used by the input system, avoiding generating one each time is better memory.
|
//Initialize runnables to be used by the input system, avoiding generating one each time is better memory.
|
||||||
mouseRunnable = () -> CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
|
||||||
switchStateRunnable = () -> {
|
switchStateRunnable = () -> {
|
||||||
currentMap.resetPressedState();
|
currentMap.resetPressedState();
|
||||||
if(lastGrabbingState){
|
if(lastGrabbingState){
|
||||||
@ -155,12 +108,34 @@ public class Gamepad {
|
|||||||
mouseSensitivity = 14; //sensitivity in game doesn't need to be resolution dependent
|
mouseSensitivity = 14; //sensitivity in game doesn't need to be resolution dependent
|
||||||
};
|
};
|
||||||
|
|
||||||
motionEventRunnable = () -> {
|
}
|
||||||
updateDirectionalJoystick(lastMotionEvent);
|
|
||||||
updateMouseJoystick(lastMotionEvent);
|
|
||||||
updateAnalogTriggers(lastMotionEvent);
|
|
||||||
gamepadDpad.update(lastMotionEvent);
|
private void tick(){
|
||||||
};
|
//update mouse position
|
||||||
|
if(lastHorizontalValue != 0 || lastVerticalValue != 0){
|
||||||
|
GamepadJoystick currentJoystick = lastGrabbingState ? leftJoystick : rightJoystick;
|
||||||
|
|
||||||
|
acceleration = (mouseMagnitude - currentJoystick.getDeadzone())/(1 - currentJoystick.getDeadzone());
|
||||||
|
acceleration = Math.pow(acceleration, mouseMaxAcceleration);
|
||||||
|
if(acceleration > 1) acceleration = 1;
|
||||||
|
|
||||||
|
CallbackBridge.mouseX += Math.cos(mouseAngle) * acceleration * mouseSensitivity;
|
||||||
|
CallbackBridge.mouseY -= Math.sin(mouseAngle) * acceleration * mouseSensitivity;
|
||||||
|
|
||||||
|
if(!lastGrabbingState){
|
||||||
|
CallbackBridge.mouseX = MathUtils.clamp(CallbackBridge.mouseX, 0, CallbackBridge.windowWidth);
|
||||||
|
CallbackBridge.mouseY = MathUtils.clamp(CallbackBridge.mouseY, 0, CallbackBridge.windowHeight);
|
||||||
|
placePointerView((int) (CallbackBridge.mouseX /gameActivity.scaleFactor), (int) (CallbackBridge.mouseY/gameActivity.scaleFactor));
|
||||||
|
}
|
||||||
|
|
||||||
|
gameActivity.mouse_x = CallbackBridge.mouseX;
|
||||||
|
gameActivity.mouse_y = CallbackBridge.mouseY;
|
||||||
|
|
||||||
|
//Send the mouse to the game
|
||||||
|
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,13 +148,14 @@ public class Gamepad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void update(KeyEvent event){
|
public void update(KeyEvent event){
|
||||||
gamepadDpad.update(event);
|
|
||||||
sendButton(event);
|
sendButton(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(MotionEvent event){
|
public void update(MotionEvent event){
|
||||||
//Motion events are taken into account every 1/60th second
|
updateDirectionalJoystick(event);
|
||||||
lastMotionEvent = event;
|
updateMouseJoystick(event);
|
||||||
|
updateAnalogTriggers(event);
|
||||||
|
sendButton(gamepadDpad.convertEvent(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMouseJoystick(MotionEvent event){
|
private void updateMouseJoystick(MotionEvent event){
|
||||||
@ -204,8 +180,10 @@ public class Gamepad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateAnalogTriggers(MotionEvent event){
|
private void updateAnalogTriggers(MotionEvent event){
|
||||||
getCurrentMap().TRIGGER_LEFT.update(event.getAxisValue(MotionEvent.AXIS_LTRIGGER) > 0.5);
|
if(!hasDigitalTriggers){
|
||||||
getCurrentMap().TRIGGER_RIGHT.update(event.getAxisValue(MotionEvent.AXIS_RTRIGGER) > 0.5);
|
getCurrentMap().TRIGGER_LEFT.update((event.getAxisValue(MotionEvent.AXIS_LTRIGGER) > 0.5) || (event.getAxisValue(MotionEvent.AXIS_BRAKE) > 0.5));
|
||||||
|
getCurrentMap().TRIGGER_RIGHT.update((event.getAxisValue(MotionEvent.AXIS_RTRIGGER) > 0.5) || (event.getAxisValue(MotionEvent.AXIS_GAS) > 0.5));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyGUISizeChange(int newSize){
|
public void notifyGUISizeChange(int newSize){
|
||||||
@ -298,19 +276,19 @@ public class Gamepad {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
//DPAD
|
//DPAD
|
||||||
case GamepadDpad.UP:
|
case KeyEvent.KEYCODE_DPAD_UP:
|
||||||
getCurrentMap().DPAD_UP.update(event);
|
getCurrentMap().DPAD_UP.update(event);
|
||||||
break;
|
break;
|
||||||
case GamepadDpad.DOWN:
|
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||||
getCurrentMap().DPAD_DOWN.update(event);
|
getCurrentMap().DPAD_DOWN.update(event);
|
||||||
break;
|
break;
|
||||||
case GamepadDpad.LEFT:
|
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||||
getCurrentMap().DPAD_LEFT.update(event);
|
getCurrentMap().DPAD_LEFT.update(event);
|
||||||
break;
|
break;
|
||||||
case GamepadDpad.RIGHT:
|
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||||
getCurrentMap().DPAD_RIGHT.update(event);
|
getCurrentMap().DPAD_RIGHT.update(event);
|
||||||
break;
|
break;
|
||||||
case GamepadDpad.CENTER:
|
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||||
getCurrentMap().DPAD_RIGHT.update(false);
|
getCurrentMap().DPAD_RIGHT.update(false);
|
||||||
getCurrentMap().DPAD_LEFT.update(false);
|
getCurrentMap().DPAD_LEFT.update(false);
|
||||||
getCurrentMap().DPAD_UP.update(false);
|
getCurrentMap().DPAD_UP.update(false);
|
||||||
|
@ -1,91 +1,77 @@
|
|||||||
package net.kdt.pojavlaunch.customcontrols.gamepad;
|
package net.kdt.pojavlaunch.customcontrols.gamepad;
|
||||||
|
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
import android.view.InputEvent;
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
import net.kdt.pojavlaunch.LWJGLGLFWKeycode;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
import static android.view.InputDevice.KEYBOARD_TYPE_NON_ALPHABETIC;
|
import static android.view.InputDevice.KEYBOARD_TYPE_NON_ALPHABETIC;
|
||||||
|
import static android.view.InputDevice.SOURCE_DPAD;
|
||||||
|
import static android.view.KeyEvent.KEYCODE_DPAD_CENTER;
|
||||||
|
import static android.view.KeyEvent.KEYCODE_DPAD_DOWN;
|
||||||
|
import static android.view.KeyEvent.KEYCODE_DPAD_LEFT;
|
||||||
|
import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
|
||||||
|
import static android.view.KeyEvent.KEYCODE_DPAD_UP;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Code from the android documentation
|
Reflection is used to avoid memory churning, and only has an negative impact at start
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class GamepadDpad {
|
public class GamepadDpad {
|
||||||
final static int UP = 999;
|
|
||||||
final static int LEFT = 9999;
|
|
||||||
final static int RIGHT = 99999;
|
|
||||||
final static int DOWN = 999999;
|
|
||||||
final static int CENTER = 9999999;
|
|
||||||
|
|
||||||
int pressedDirection = -1;
|
|
||||||
Gamepad parentPad;
|
private int lastKeycode = KEYCODE_DPAD_CENTER;
|
||||||
KeyEvent dummyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, CENTER);
|
private KeyEvent dummyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, lastKeycode);
|
||||||
Field eventCodeField;
|
private Field eventCodeField;
|
||||||
|
private Field eventActionField;
|
||||||
|
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
eventCodeField = dummyEvent.getClass().getDeclaredField("mKeyCode");
|
eventCodeField = dummyEvent.getClass().getDeclaredField("mKeyCode");
|
||||||
eventCodeField.setAccessible(true);
|
eventCodeField.setAccessible(true);
|
||||||
|
|
||||||
|
eventActionField = dummyEvent.getClass().getDeclaredField("mAction");
|
||||||
|
eventActionField.setAccessible(true);
|
||||||
} catch (NoSuchFieldException e) {
|
} catch (NoSuchFieldException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GamepadDpad(Gamepad parentPad){
|
public KeyEvent convertEvent(MotionEvent event){
|
||||||
this.parentPad = parentPad;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(KeyEvent event){
|
|
||||||
|
|
||||||
//TODO check if the event is valid
|
|
||||||
if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
|
|
||||||
pressedDirection = LEFT;
|
|
||||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
|
|
||||||
pressedDirection = RIGHT;
|
|
||||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
|
|
||||||
pressedDirection = UP;
|
|
||||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
|
|
||||||
pressedDirection = DOWN;
|
|
||||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
|
|
||||||
pressedDirection = CENTER;
|
|
||||||
}
|
|
||||||
|
|
||||||
setDummyEventKeyCode(pressedDirection);
|
|
||||||
parentPad.sendButton(dummyEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(MotionEvent event){
|
|
||||||
//TODO check if the event is valid
|
|
||||||
|
|
||||||
// Use the hat axis value to find the D-pad direction
|
// Use the hat axis value to find the D-pad direction
|
||||||
float xaxis = event.getAxisValue(MotionEvent.AXIS_HAT_X);
|
float xaxis = event.getAxisValue(MotionEvent.AXIS_HAT_X);
|
||||||
float yaxis = event.getAxisValue(MotionEvent.AXIS_HAT_Y);
|
float yaxis = event.getAxisValue(MotionEvent.AXIS_HAT_Y);
|
||||||
|
int action = KeyEvent.ACTION_DOWN;
|
||||||
|
|
||||||
// Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
|
// Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
|
||||||
// LEFT and RIGHT direction accordingly.
|
// LEFT and RIGHT direction accordingly.
|
||||||
if (Float.compare(xaxis, -1.0f) == 0) {
|
if (Float.compare(xaxis, -1.0f) == 0) {
|
||||||
pressedDirection = LEFT;
|
lastKeycode = KEYCODE_DPAD_LEFT;
|
||||||
} else if (Float.compare(xaxis, 1.0f) == 0) {
|
} else if (Float.compare(xaxis, 1.0f) == 0) {
|
||||||
pressedDirection = RIGHT;
|
lastKeycode = KEYCODE_DPAD_RIGHT;
|
||||||
}
|
}
|
||||||
// Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
|
// Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
|
||||||
// UP and DOWN direction accordingly.
|
// UP and DOWN direction accordingly.
|
||||||
else if (Float.compare(yaxis, -1.0f) == 0) {
|
else if (Float.compare(yaxis, -1.0f) == 0) {
|
||||||
pressedDirection = UP;
|
lastKeycode = KEYCODE_DPAD_UP;
|
||||||
} else if (Float.compare(yaxis, 1.0f) == 0) {
|
} else if (Float.compare(yaxis, 1.0f) == 0) {
|
||||||
pressedDirection = DOWN;
|
lastKeycode = KEYCODE_DPAD_DOWN;
|
||||||
}else {
|
}else {
|
||||||
pressedDirection = CENTER;
|
//No keycode change
|
||||||
|
action = KeyEvent.ACTION_UP;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDummyEventKeyCode(pressedDirection);
|
setDummyEventKeycode(lastKeycode);
|
||||||
parentPad.sendButton(dummyEvent);
|
setDummyEventAction(action);
|
||||||
|
dummyEvent.setSource(SOURCE_DPAD);
|
||||||
|
return dummyEvent;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDummyEventKeyCode(int fakeKeycode){
|
private void setDummyEventKeycode(int fakeKeycode){
|
||||||
try {
|
try {
|
||||||
eventCodeField.setInt(dummyEvent, fakeKeycode);
|
eventCodeField.setInt(dummyEvent, fakeKeycode);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
@ -93,6 +79,14 @@ public class GamepadDpad {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setDummyEventAction(int action){
|
||||||
|
try {
|
||||||
|
eventActionField.setInt(dummyEvent, action);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isDpadEvent(MotionEvent event) {
|
public static boolean isDpadEvent(MotionEvent event) {
|
||||||
// Check that input comes from a device with directional pads.
|
// Check that input comes from a device with directional pads.
|
||||||
// And... also the joystick since it declares sometimes as a joystick.
|
// And... also the joystick since it declares sometimes as a joystick.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user