Feat[V 2.Gamepad]: Merge #4179

* Feat: gamepad remapping, with custom deazone

* Fix[gamepad]: wrong starting pointer offset

* cleanup[gamepad]: Remove useless variables

* Fix[gamepad]: Reduce jitter

Turns out the argument was a bit off when there are cpu constraints

* Chore: bump up remapper library version

* Build: bump up library version
This commit is contained in:
Boulay Mathias 2023-05-21 12:50:06 +02:00 committed by GitHub
parent 7916166795
commit 809f5cd6e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 277 additions and 192 deletions

View File

@ -157,6 +157,7 @@ dependencies {
implementation 'com.github.PojavLauncherTeam:portrait-sdp:ed33e89cbc' implementation 'com.github.PojavLauncherTeam:portrait-sdp:ed33e89cbc'
implementation 'com.github.PojavLauncherTeam:portrait-ssp:6c02fd739b' implementation 'com.github.PojavLauncherTeam:portrait-ssp:6c02fd739b'
implementation 'com.github.Mathias-Boulay:ExtendedView:1.0.0' implementation 'com.github.Mathias-Boulay:ExtendedView:1.0.0'
implementation 'com.github.Mathias-Boulay:android_gamepad_remapper:71397676b5'
// implementation 'com.intuit.sdp:sdp-android:1.0.5' // implementation 'com.intuit.sdp:sdp-android:1.0.5'

View File

@ -39,12 +39,32 @@ import net.kdt.pojavlaunch.utils.MathUtils;
import org.lwjgl.glfw.CallbackBridge; import org.lwjgl.glfw.CallbackBridge;
import fr.spse.gamepad_remapper.RemapperManager;
import fr.spse.gamepad_remapper.RemapperView;
/** /**
* 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 implements GrabListener{ public class MinecraftGLSurface extends View implements GrabListener {
/* Gamepad object for gamepad inputs, instantiated on need */ /* Gamepad object for gamepad inputs, instantiated on need */
private Gamepad mGamepad = null; private Gamepad mGamepad = null;
/* The RemapperView.Builder object allows you to set which buttons to remap */
private final RemapperManager mInputManager = new RemapperManager(getContext(), new RemapperView.Builder(null)
.remapA(true)
.remapB(true)
.remapX(true)
.remapY(true)
.remapDpad(true)
.remapLeftJoystick(true)
.remapRightJoystick(true)
.remapStart(true)
.remapSelect(true)
.remapLeftShoulder(true)
.remapRightShoulder(true)
.remapLeftTrigger(true)
.remapRightTrigger(true));
/* Resolution scaler option, allow downsizing a window */ /* Resolution scaler option, allow downsizing a window */
private final float mScaleFactor = LauncherPreferences.PREF_SCALE_FACTOR/100f; private final float mScaleFactor = LauncherPreferences.PREF_SCALE_FACTOR/100f;
/* Sensitivity, adjusted according to screen size */ /* Sensitivity, adjusted according to screen size */
@ -385,7 +405,7 @@ public class MinecraftGLSurface extends View implements GrabListener{
mGamepad = new Gamepad(this, event.getDevice()); mGamepad = new Gamepad(this, event.getDevice());
} }
mGamepad.update(event); mInputManager.handleMotionEventInput(getContext(), event, mGamepad);
return true; return true;
} }
@ -479,7 +499,7 @@ public class MinecraftGLSurface extends View implements GrabListener{
mGamepad = new Gamepad(this, event.getDevice()); mGamepad = new Gamepad(this, event.getDevice());
} }
mGamepad.update(event); mInputManager.handleKeyEventInput(getContext(), event, mGamepad);
return true; return true;
} }

View File

@ -1,6 +1,15 @@
package net.kdt.pojavlaunch.customcontrols.gamepad; package net.kdt.pojavlaunch.customcontrols.gamepad;
import static android.view.MotionEvent.AXIS_HAT_X;
import static android.view.MotionEvent.AXIS_HAT_Y;
import static android.view.MotionEvent.AXIS_LTRIGGER;
import static android.view.MotionEvent.AXIS_RTRIGGER;
import static android.view.MotionEvent.AXIS_RZ;
import static android.view.MotionEvent.AXIS_X;
import static android.view.MotionEvent.AXIS_Y;
import static android.view.MotionEvent.AXIS_Z;
import android.content.Context; import android.content.Context;
import android.view.Choreographer; import android.view.Choreographer;
import android.view.InputDevice; import android.view.InputDevice;
@ -38,19 +47,18 @@ import static net.kdt.pojavlaunch.utils.MCOptionUtils.getMcScale;
import static org.lwjgl.glfw.CallbackBridge.sendKeyPress; import static org.lwjgl.glfw.CallbackBridge.sendKeyPress;
import static org.lwjgl.glfw.CallbackBridge.sendMouseButton; import static org.lwjgl.glfw.CallbackBridge.sendMouseButton;
public class Gamepad implements GrabListener { import fr.spse.gamepad_remapper.GamepadHandler;
public class Gamepad implements GrabListener, GamepadHandler {
/* Resolution scaler option, allow downsizing a window */ /* Resolution scaler option, allow downsizing a window */
private final float mScaleFactor = LauncherPreferences.DEFAULT_PREF.getInt("resolutionRatio",100)/100f; private final float mScaleFactor = LauncherPreferences.DEFAULT_PREF.getInt("resolutionRatio",100)/100f;
/* Mouse positions, scaled by the scaleFactor */
private float mMouse_x, mMouse_y;
/* Sensitivity, adjusted according to screen size */ /* Sensitivity, adjusted according to screen size */
private final double mSensitivityFactor = (1.4 * (1080f/ currentDisplayMetrics.heightPixels)); private final double mSensitivityFactor = (1.4 * (1080f/ currentDisplayMetrics.heightPixels));
private final ImageView mPointerImageView; private final ImageView mPointerImageView;
private final GamepadDpad mGamepadDpad = new GamepadDpad();
private final GamepadJoystick mLeftJoystick; private final GamepadJoystick mLeftJoystick;
private int mCurrentJoystickDirection = DIRECTION_NONE; private int mCurrentJoystickDirection = DIRECTION_NONE;
@ -70,9 +78,7 @@ public class Gamepad implements GrabListener {
// The negation is to force trigger the onGrabState // The negation is to force trigger the onGrabState
private boolean isGrabbing = !CallbackBridge.isGrabbing(); private boolean isGrabbing = !CallbackBridge.isGrabbing();
//private final boolean mModifierDigitalTriggers;
private final boolean mModifierAnalogTriggers;
private boolean mModifierSwappedAxis = true; //Triggers and right stick axis are swapped.
/* Choreographer with time to compute delta on ticking */ /* Choreographer with time to compute delta on ticking */
private final Choreographer mScreenChoreographer; private final Choreographer mScreenChoreographer;
@ -97,25 +103,9 @@ public class Gamepad implements GrabListener {
/* Add the listener for the cross hair */ /* Add the listener for the cross hair */
MCOptionUtils.addMCOptionListener(mGuiScaleListener); MCOptionUtils.addMCOptionListener(mGuiScaleListener);
Toast.makeText(contextView.getContext(),"GAMEPAD CREATED", Toast.LENGTH_LONG).show(); mLeftJoystick = new GamepadJoystick(AXIS_X, AXIS_Y, inputDevice);
for(InputDevice.MotionRange range : inputDevice.getMotionRanges()){ mRightJoystick = new GamepadJoystick(AXIS_Z, AXIS_RZ, inputDevice);
if(range.getAxis() == MotionEvent.AXIS_RTRIGGER
|| range.getAxis() == MotionEvent.AXIS_LTRIGGER
|| range.getAxis() == MotionEvent.AXIS_GAS
|| range.getAxis() == MotionEvent.AXIS_BRAKE){
mModifierSwappedAxis = false;
break;
}
}
mLeftJoystick = new GamepadJoystick(MotionEvent.AXIS_X, MotionEvent.AXIS_Y, inputDevice);
if(!mModifierSwappedAxis)
mRightJoystick = new GamepadJoystick(MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, inputDevice);
else
mRightJoystick = new GamepadJoystick(MotionEvent.AXIS_RX, MotionEvent.AXIS_RY, inputDevice);
//mModifierDigitalTriggers = inputDevice.hasKeys(KeyEvent.KEYCODE_BUTTON_R2)[0];
mModifierAnalogTriggers = supportAnalogTriggers(inputDevice);
Context ctx = contextView.getContext(); Context ctx = contextView.getContext();
mPointerImageView = new ImageView(contextView.getContext()); mPointerImageView = new ImageView(contextView.getContext());
@ -125,28 +115,20 @@ public class Gamepad implements GrabListener {
int size = (int) ((22 * getMcScale()) / mScaleFactor); int size = (int) ((22 * getMcScale()) / mScaleFactor);
mPointerImageView.setLayoutParams(new FrameLayout.LayoutParams(size, size)); mPointerImageView.setLayoutParams(new FrameLayout.LayoutParams(size, size));
mMouse_x = CallbackBridge.windowWidth/2f; CallbackBridge.sendCursorPos(CallbackBridge.windowWidth/2f, CallbackBridge.windowHeight/2f);
mMouse_y = CallbackBridge.windowHeight/2f;
CallbackBridge.sendCursorPos(mMouse_x, mMouse_y);
placePointerView(CallbackBridge.physicalWidth/2, CallbackBridge.physicalHeight/2);
((ViewGroup)contextView.getParent()).addView(mPointerImageView); ((ViewGroup)contextView.getParent()).addView(mPointerImageView);
placePointerView(CallbackBridge.physicalWidth/2, CallbackBridge.physicalHeight/2);
CallbackBridge.addGrabListener(this); CallbackBridge.addGrabListener(this);
} }
public void update(KeyEvent event){
sendButton(event);
}
public void update(MotionEvent event){
updateDirectionalJoystick(event);
updateMouseJoystick(event);
updateAnalogTriggers(event);
int[] dpadEvent = mGamepadDpad.convertEvent(event); public void updateJoysticks(){
sendButton(dpadEvent[0], dpadEvent[1]); updateDirectionalJoystick();
updateMouseJoystick();
} }
public void notifyGUISizeChange(int newSize){ public void notifyGUISizeChange(int newSize){
@ -156,86 +138,6 @@ public class Gamepad implements GrabListener {
} }
public void sendButton(KeyEvent event){
sendButton(event.getKeyCode(), event.getAction());
}
public void sendButton(int keycode, int action){
boolean isDown = action == KeyEvent.ACTION_DOWN;
switch (keycode){
case KeyEvent.KEYCODE_BUTTON_A:
getCurrentMap().BUTTON_A.update(isDown);
break;
case KeyEvent.KEYCODE_BUTTON_B:
getCurrentMap().BUTTON_B.update(isDown);
break;
case KeyEvent.KEYCODE_BUTTON_X:
getCurrentMap().BUTTON_X.update(isDown);
break;
case KeyEvent.KEYCODE_BUTTON_Y:
getCurrentMap().BUTTON_Y.update(isDown);
break;
//Shoulders
case KeyEvent.KEYCODE_BUTTON_L1:
getCurrentMap().SHOULDER_LEFT.update(isDown);
break;
case KeyEvent.KEYCODE_BUTTON_R1:
getCurrentMap().SHOULDER_RIGHT.update(isDown);
break;
//Triggers
case KeyEvent.KEYCODE_BUTTON_L2:
if(mModifierAnalogTriggers) break;
getCurrentMap().TRIGGER_LEFT.update(isDown);
break;
case KeyEvent.KEYCODE_BUTTON_R2:
if(mModifierAnalogTriggers) break;
getCurrentMap().TRIGGER_RIGHT.update(isDown);
break;
//L3 || R3
case KeyEvent.KEYCODE_BUTTON_THUMBL:
getCurrentMap().THUMBSTICK_LEFT.update(isDown);
break;
case KeyEvent.KEYCODE_BUTTON_THUMBR:
getCurrentMap().THUMBSTICK_RIGHT.update(isDown);
break;
//DPAD
case KeyEvent.KEYCODE_DPAD_UP:
getCurrentMap().DPAD_UP.update(isDown);
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
getCurrentMap().DPAD_DOWN.update(isDown);
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
getCurrentMap().DPAD_LEFT.update(isDown);
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
getCurrentMap().DPAD_RIGHT.update(isDown);
break;
case KeyEvent.KEYCODE_DPAD_CENTER:
getCurrentMap().DPAD_RIGHT.update(false);
getCurrentMap().DPAD_LEFT.update(false);
getCurrentMap().DPAD_UP.update(false);
getCurrentMap().DPAD_DOWN.update(false);
break;
//Start/select
case KeyEvent.KEYCODE_BUTTON_START:
getCurrentMap().BUTTON_START.update(isDown);
break;
case KeyEvent.KEYCODE_BUTTON_SELECT:
getCurrentMap().BUTTON_SELECT.update(isDown);
break;
default:
sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_SPACE, CallbackBridge.getCurrentMods(), isDown);
break;
}
}
public static void sendInput(int[] keycodes, boolean isDown){ public static void sendInput(int[] keycodes, boolean isDown){
for(int keycode : keycodes){ for(int keycode : keycodes){
@ -281,6 +183,7 @@ public class Gamepad implements GrabListener {
*/ */
private void tick(long frameTimeNanos){ private void tick(long frameTimeNanos){
//update mouse position //update mouse position
long newFrameTime = System.nanoTime();
if(mLastHorizontalValue != 0 || mLastVerticalValue != 0){ if(mLastHorizontalValue != 0 || mLastVerticalValue != 0){
GamepadJoystick currentJoystick = isGrabbing ? mLeftJoystick : mRightJoystick; GamepadJoystick currentJoystick = isGrabbing ? mLeftJoystick : mRightJoystick;
@ -291,7 +194,8 @@ public class Gamepad implements GrabListener {
// Compute delta since last tick time // Compute delta since last tick time
float deltaX = (float) (Math.cos(mMouseAngle) * acceleration * mMouseSensitivity); float deltaX = (float) (Math.cos(mMouseAngle) * acceleration * mMouseSensitivity);
float deltaY = (float) (Math.sin(mMouseAngle) * acceleration * mMouseSensitivity); float deltaY = (float) (Math.sin(mMouseAngle) * acceleration * mMouseSensitivity);
float deltaTimeScale = ((frameTimeNanos - mLastFrameTime) / 16666666f); // Scale of 1 = 60Hz newFrameTime = System.nanoTime(); // More accurate delta
float deltaTimeScale = ((newFrameTime - mLastFrameTime) / 16666666f); // Scale of 1 = 60Hz
deltaX *= deltaTimeScale; deltaX *= deltaTimeScale;
deltaY *= deltaTimeScale; deltaY *= deltaTimeScale;
@ -304,27 +208,24 @@ public class Gamepad implements GrabListener {
placePointerView((int) (CallbackBridge.mouseX / mScaleFactor), (int) (CallbackBridge.mouseY/ mScaleFactor)); placePointerView((int) (CallbackBridge.mouseX / mScaleFactor), (int) (CallbackBridge.mouseY/ mScaleFactor));
} }
mMouse_x = CallbackBridge.mouseX;
mMouse_y = CallbackBridge.mouseY;
//Send the mouse to the game //Send the mouse to the game
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY); CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
} }
// Update last nano time // Update last nano time
mLastFrameTime = frameTimeNanos; mLastFrameTime = newFrameTime;
} }
private void updateMouseJoystick(MotionEvent event){ private void updateMouseJoystick(){
GamepadJoystick currentJoystick = isGrabbing ? mRightJoystick : mLeftJoystick; GamepadJoystick currentJoystick = isGrabbing ? mRightJoystick : mLeftJoystick;
float horizontalValue = currentJoystick.getHorizontalAxis(event); float horizontalValue = currentJoystick.getHorizontalAxis();
float verticalValue = currentJoystick.getVerticalAxis(event); float verticalValue = currentJoystick.getVerticalAxis();
if(horizontalValue != mLastHorizontalValue || verticalValue != mLastVerticalValue){ if(horizontalValue != mLastHorizontalValue || verticalValue != mLastVerticalValue){
mLastHorizontalValue = horizontalValue; mLastHorizontalValue = horizontalValue;
mLastVerticalValue = verticalValue; mLastVerticalValue = verticalValue;
mMouseMagnitude = currentJoystick.getMagnitude(event); mMouseMagnitude = currentJoystick.getMagnitude();
mMouseAngle = currentJoystick.getAngleRadian(event); mMouseAngle = currentJoystick.getAngleRadian();
tick(System.nanoTime()); tick(System.nanoTime());
return; return;
@ -332,16 +233,16 @@ public class Gamepad implements GrabListener {
mLastHorizontalValue = horizontalValue; mLastHorizontalValue = horizontalValue;
mLastVerticalValue = verticalValue; mLastVerticalValue = verticalValue;
mMouseMagnitude = currentJoystick.getMagnitude(event); mMouseMagnitude = currentJoystick.getMagnitude();
mMouseAngle = currentJoystick.getAngleRadian(event); mMouseAngle = currentJoystick.getAngleRadian();
} }
private void updateDirectionalJoystick(MotionEvent event){ private void updateDirectionalJoystick(){
GamepadJoystick currentJoystick = isGrabbing ? mLeftJoystick : mRightJoystick; GamepadJoystick currentJoystick = isGrabbing ? mLeftJoystick : mRightJoystick;
int lastJoystickDirection = mCurrentJoystickDirection; int lastJoystickDirection = mCurrentJoystickDirection;
mCurrentJoystickDirection = currentJoystick.getHeightDirection(event); mCurrentJoystickDirection = currentJoystick.getHeightDirection();
if(mCurrentJoystickDirection == lastJoystickDirection) return; if(mCurrentJoystickDirection == lastJoystickDirection) return;
@ -349,37 +250,6 @@ public class Gamepad implements GrabListener {
sendDirectionalKeycode(mCurrentJoystickDirection, true, getCurrentMap()); sendDirectionalKeycode(mCurrentJoystickDirection, true, getCurrentMap());
} }
private void updateAnalogTriggers(MotionEvent event){
if(mModifierAnalogTriggers){
getCurrentMap().TRIGGER_LEFT.update(
(event.getAxisValue(MotionEvent.AXIS_LTRIGGER) > 0.5)
|| (event.getAxisValue(MotionEvent.AXIS_BRAKE) > 0.5)
|| (mModifierSwappedAxis &&(event.getAxisValue(MotionEvent.AXIS_Z) > 0.5)) );
getCurrentMap().TRIGGER_RIGHT.update(
(event.getAxisValue( MotionEvent.AXIS_RTRIGGER) > 0.5)
|| (event.getAxisValue(MotionEvent.AXIS_GAS) > 0.5)
|| (mModifierSwappedAxis && event.getAxisValue(MotionEvent.AXIS_RZ) > 0.5) );
}
}
/**
* Detect if a gamepad supports analog triggers
* @param inputDevice The input device with all the MotionRange
* @return Whether the gamepad supports analog triggers
*/
private boolean supportAnalogTriggers(InputDevice inputDevice){
for(InputDevice.MotionRange motionRange : inputDevice.getMotionRanges()){
int axis = motionRange.getAxis();
if( axis == MotionEvent.AXIS_BRAKE || axis == MotionEvent.AXIS_GAS ||
axis == MotionEvent.AXIS_LTRIGGER || axis == MotionEvent.AXIS_RTRIGGER ||
(mModifierSwappedAxis && axis == MotionEvent.AXIS_Z) ||
(mModifierSwappedAxis && axis == MotionEvent.AXIS_RZ)){
return true;
}
}
return false;
}
private GamepadMap getCurrentMap(){ private GamepadMap getCurrentMap(){
return mCurrentMap; return mCurrentMap;
@ -418,6 +288,7 @@ public class Gamepad implements GrabListener {
} }
} }
/** Place the pointer on the screen, offsetting the image size */
private void placePointerView(int x, int y){ private void placePointerView(int x, int y){
mPointerImageView.setX(x - mPointerImageView.getWidth()/2f); mPointerImageView.setX(x - mPointerImageView.getWidth()/2f);
mPointerImageView.setY(y - mPointerImageView.getHeight()/2f); mPointerImageView.setY(y - mPointerImageView.getHeight()/2f);
@ -442,12 +313,123 @@ public class Gamepad implements GrabListener {
mCurrentMap = mMenuMap; mCurrentMap = mMenuMap;
sendDirectionalKeycode(mCurrentJoystickDirection, false, mGameMap); // removing what we were doing sendDirectionalKeycode(mCurrentJoystickDirection, false, mGameMap); // removing what we were doing
mMouse_x = CallbackBridge.windowWidth/2f; CallbackBridge.sendCursorPos(CallbackBridge.windowWidth/2f, CallbackBridge.windowHeight/2f);
mMouse_y = CallbackBridge.windowHeight/2f;
CallbackBridge.sendCursorPos(mMouse_x, mMouse_y);
placePointerView(CallbackBridge.physicalWidth/2, CallbackBridge.physicalHeight/2); placePointerView(CallbackBridge.physicalWidth/2, CallbackBridge.physicalHeight/2);
mPointerImageView.setVisibility(View.VISIBLE); mPointerImageView.setVisibility(View.VISIBLE);
// Sensitivity in menu is MC and HARDWARE resolution dependent // Sensitivity in menu is MC and HARDWARE resolution dependent
mMouseSensitivity = 19 * mScaleFactor / mSensitivityFactor; mMouseSensitivity = 19 * mScaleFactor / mSensitivityFactor;
} }
@Override
public void handleGamepadInput(int keycode, float value) {
boolean isKeyEventDown = value == 1f;
switch (keycode){
case KeyEvent.KEYCODE_BUTTON_A:
getCurrentMap().BUTTON_A.update(isKeyEventDown);
break;
case KeyEvent.KEYCODE_BUTTON_B:
getCurrentMap().BUTTON_B.update(isKeyEventDown);
break;
case KeyEvent.KEYCODE_BUTTON_X:
getCurrentMap().BUTTON_X.update(isKeyEventDown);
break;
case KeyEvent.KEYCODE_BUTTON_Y:
getCurrentMap().BUTTON_Y.update(isKeyEventDown);
break;
//Shoulders
case KeyEvent.KEYCODE_BUTTON_L1:
getCurrentMap().SHOULDER_LEFT.update(isKeyEventDown);
break;
case KeyEvent.KEYCODE_BUTTON_R1:
getCurrentMap().SHOULDER_RIGHT.update(isKeyEventDown);
break;
//Triggers
case KeyEvent.KEYCODE_BUTTON_L2:
getCurrentMap().TRIGGER_LEFT.update(isKeyEventDown);
break;
case KeyEvent.KEYCODE_BUTTON_R2:
getCurrentMap().TRIGGER_RIGHT.update(isKeyEventDown);
break;
//L3 || R3
case KeyEvent.KEYCODE_BUTTON_THUMBL:
getCurrentMap().THUMBSTICK_LEFT.update(isKeyEventDown);
break;
case KeyEvent.KEYCODE_BUTTON_THUMBR:
getCurrentMap().THUMBSTICK_RIGHT.update(isKeyEventDown);
break;
//DPAD
case KeyEvent.KEYCODE_DPAD_UP:
getCurrentMap().DPAD_UP.update(isKeyEventDown);
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
getCurrentMap().DPAD_DOWN.update(isKeyEventDown);
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
getCurrentMap().DPAD_LEFT.update(isKeyEventDown);
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
getCurrentMap().DPAD_RIGHT.update(isKeyEventDown);
break;
case KeyEvent.KEYCODE_DPAD_CENTER:
getCurrentMap().DPAD_RIGHT.update(false);
getCurrentMap().DPAD_LEFT.update(false);
getCurrentMap().DPAD_UP.update(false);
getCurrentMap().DPAD_DOWN.update(false);
break;
//Start/select
case KeyEvent.KEYCODE_BUTTON_START:
getCurrentMap().BUTTON_START.update(isKeyEventDown);
break;
case KeyEvent.KEYCODE_BUTTON_SELECT:
getCurrentMap().BUTTON_SELECT.update(isKeyEventDown);
break;
/* Now, it is time for motionEvents */
case AXIS_HAT_X:
getCurrentMap().DPAD_RIGHT.update(value > 0.85);
getCurrentMap().DPAD_LEFT.update(value < -0.85);
break;
case AXIS_HAT_Y:
getCurrentMap().DPAD_DOWN.update(value > 0.85);
getCurrentMap().DPAD_UP.update(value < -0.85);
break;
// Left joystick
case AXIS_X:
mLeftJoystick.setXAxisValue(value);
updateJoysticks();
break;
case AXIS_Y:
mLeftJoystick.setYAxisValue(value);
updateJoysticks();
break;
// Right joystick
case AXIS_Z:
mRightJoystick.setXAxisValue(value);
updateJoysticks();
break;
case AXIS_RZ:
mRightJoystick.setYAxisValue(value);
updateJoysticks();
break;
// Triggers
case AXIS_RTRIGGER:
getCurrentMap().TRIGGER_RIGHT.update(value > 0.5);
break;
case AXIS_LTRIGGER:
getCurrentMap().TRIGGER_LEFT.update(value > 0.5);
break;
default:
sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_SPACE, CallbackBridge.getCurrentMods(), isKeyEventDown);
break;
}
}
} }

View File

@ -1,5 +1,8 @@
package net.kdt.pojavlaunch.customcontrols.gamepad; package net.kdt.pojavlaunch.customcontrols.gamepad;
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_DEADZONE_SCALE;
import android.util.Log;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.MotionEvent; import android.view.MotionEvent;
@ -21,42 +24,46 @@ public class GamepadJoystick {
public static final int DIRECTION_SOUTH_EAST = 7; public static final int DIRECTION_SOUTH_EAST = 7;
private final InputDevice mInputDevice; private final InputDevice mInputDevice;
private final int mVerticalAxis;
private final int mHorizontalAxis; private final int mHorizontalAxis;
private final int mVerticalAxis;
private float mVerticalAxisValue = 0;
private float mHorizontalAxisValue = 0;
public GamepadJoystick(int horizontalAxis, int verticalAxis, InputDevice device){ public GamepadJoystick(int horizontalAxis, int verticalAxis, InputDevice device){
this.mVerticalAxis = verticalAxis; mHorizontalAxis = horizontalAxis;
this.mHorizontalAxis = horizontalAxis; mVerticalAxis = verticalAxis;
this.mInputDevice = device; this.mInputDevice = device;
} }
public double getAngleRadian(MotionEvent event){ public double getAngleRadian(){
//From -PI to PI //From -PI to PI
return -Math.atan2(getVerticalAxis(event), getHorizontalAxis(event)); // TODO misuse of the deadzone here !
return -Math.atan2(getVerticalAxis(), getHorizontalAxis());
} }
public double getAngleDegree(MotionEvent event){ public double getAngleDegree(){
//From 0 to 360 degrees //From 0 to 360 degrees
double result = Math.toDegrees(getAngleRadian(event)); double result = Math.toDegrees(getAngleRadian());
if(result < 0) result += 360; if(result < 0) result += 360;
return result; return result;
} }
public double getMagnitude(MotionEvent event){ public double getMagnitude(){
float x = Math.abs(event.getAxisValue(mHorizontalAxis)); float x = Math.abs(mHorizontalAxisValue);
float y = Math.abs(event.getAxisValue(mVerticalAxis)); float y = Math.abs(mVerticalAxisValue);
return MathUtils.dist(0,0, x, y); return MathUtils.dist(0,0, x, y);
} }
public float getVerticalAxis(MotionEvent event){ public float getVerticalAxis(){
return applyDeadzone(event, mVerticalAxis); return applyDeadzone(mVerticalAxisValue);
} }
public float getHorizontalAxis(MotionEvent event){ public float getHorizontalAxis(){
return applyDeadzone(event, mHorizontalAxis); return applyDeadzone(mHorizontalAxisValue);
} }
public static boolean isJoystickEvent(MotionEvent event){ public static boolean isJoystickEvent(MotionEvent event){
@ -65,9 +72,9 @@ public class GamepadJoystick {
} }
public int getHeightDirection(MotionEvent event){ public int getHeightDirection(){
if(getMagnitude(event) <= getDeadzone()) return DIRECTION_NONE; if(getMagnitude() <= getDeadzone()) return DIRECTION_NONE;
return ((int) ((getAngleDegree(event)+22.5)/45)) % 8; return ((int) ((getAngleDegree()+22.5)/45)) % 8;
} }
/** /**
@ -77,20 +84,31 @@ public class GamepadJoystick {
*/ */
public float getDeadzone() { public float getDeadzone() {
try{ try{
return Math.max(mInputDevice.getMotionRange(mHorizontalAxis).getFlat() * 1.9f, 0.2f); return mInputDevice.getMotionRange(mHorizontalAxis).getFlat() * PREF_DEADZONE_SCALE;
}catch (Exception e){ }catch (Exception e){
Log.e(GamepadJoystick.class.toString(), "Dynamic Deadzone is not supported ");
return 0.2f; return 0.2f;
} }
} }
private float applyDeadzone(MotionEvent event, int axis){ private float applyDeadzone(float value){
//This piece of code also modifies the value //This piece of code also modifies the value
//to make it seem like there was no deadzone in the first place //to make it seem like there was no deadzone in the first place
double magnitude = getMagnitude(event); double magnitude = getMagnitude();
float deadzone = getDeadzone(); float deadzone = getDeadzone();
if (magnitude < deadzone) return 0; if (magnitude < deadzone) return 0;
return (float) ( (event.getAxisValue(axis) / magnitude) * ((magnitude - deadzone) / (1 - deadzone)) ); return (float) ( (value / magnitude) * ((magnitude - deadzone) / (1 - deadzone)) );
}
/* Setters */
public void setXAxisValue(float value){
this.mHorizontalAxisValue = value;
}
public void setYAxisValue(float value){
this.mVerticalAxisValue = value;
} }
} }

View File

@ -0,0 +1,34 @@
package net.kdt.pojavlaunch.prefs;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import net.kdt.pojavlaunch.R;
import fr.spse.gamepad_remapper.Remapper;
public class GamepadRemapPreference extends Preference {
public GamepadRemapPreference(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public GamepadRemapPreference(@NonNull Context context) {
super(context);
init();
}
private void init(){
setOnPreferenceClickListener(preference -> {
Remapper.wipePreferences(getContext());
Toast.makeText(getContext(), R.string.preference_controller_map_wiped, Toast.LENGTH_SHORT).show();
return true;
});
}
}

View File

@ -57,6 +57,7 @@ public class LauncherPreferences {
public static boolean PREF_BUTTON_ALL_CAPS = true; public static boolean PREF_BUTTON_ALL_CAPS = true;
public static boolean PREF_DUMP_SHADERS = false; public static boolean PREF_DUMP_SHADERS = false;
public static float PREF_DEADZONE_SCALE = 1f;
public static void loadPreferences(Context ctx) { public static void loadPreferences(Context ctx) {
@ -96,6 +97,7 @@ public class LauncherPreferences {
PREF_FORCE_VSYNC = DEFAULT_PREF.getBoolean("force_vsync", false); PREF_FORCE_VSYNC = DEFAULT_PREF.getBoolean("force_vsync", false);
PREF_BUTTON_ALL_CAPS = DEFAULT_PREF.getBoolean("buttonAllCaps", true); PREF_BUTTON_ALL_CAPS = DEFAULT_PREF.getBoolean("buttonAllCaps", true);
PREF_DUMP_SHADERS = DEFAULT_PREF.getBoolean("dump_shaders", false); PREF_DUMP_SHADERS = DEFAULT_PREF.getBoolean("dump_shaders", false);
PREF_DEADZONE_SCALE = DEFAULT_PREF.getInt("gamepad_deadzone_scale", 100)/100f;
String argLwjglLibname = "-Dorg.lwjgl.opengl.libname="; String argLwjglLibname = "-Dorg.lwjgl.opengl.libname=";
for (String arg : JREUtils.parseJavaArguments(PREF_CUSTOM_JAVA_ARGS)) { for (String arg : JREUtils.parseJavaArguments(PREF_CUSTOM_JAVA_ARGS)) {

View File

@ -23,6 +23,7 @@ public class LauncherPreferenceControlFragment extends LauncherPreferenceFragmen
int gyroSampleRate = LauncherPreferences.PREF_GYRO_SAMPLE_RATE; int gyroSampleRate = LauncherPreferences.PREF_GYRO_SAMPLE_RATE;
float mouseSpeed = LauncherPreferences.PREF_MOUSESPEED; float mouseSpeed = LauncherPreferences.PREF_MOUSESPEED;
float gyroSpeed = LauncherPreferences.PREF_GYRO_SENSITIVITY; float gyroSpeed = LauncherPreferences.PREF_GYRO_SENSITIVITY;
float joystickDeadzone = LauncherPreferences.PREF_DEADZONE_SCALE;
//Triggers a write for some reason which resets the value //Triggers a write for some reason which resets the value
addPreferencesFromResource(R.xml.pref_control); addPreferencesFromResource(R.xml.pref_control);
@ -47,6 +48,11 @@ public class LauncherPreferenceControlFragment extends LauncherPreferenceFragmen
seek6.setValue((int)(mouseSpeed *100f)); seek6.setValue((int)(mouseSpeed *100f));
seek6.setSuffix(" %"); seek6.setSuffix(" %");
CustomSeekBarPreference deadzoneSeek = findPreference("gamepad_deadzone_scale");
deadzoneSeek.setRange(50, 200);
deadzoneSeek.setValue((int) joystickDeadzone * 100);
deadzoneSeek.setSuffix(" %");
Context context = getContext(); Context context = getContext();
if(context != null) { if(context != null) {
mGyroAvailable = ((SensorManager)context.getSystemService(Context.SENSOR_SERVICE)).getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null; mGyroAvailable = ((SensorManager)context.getSystemService(Context.SENSOR_SERVICE)).getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null;

View File

@ -364,4 +364,10 @@
<string name="global_save_and_exit">Save and exit</string> <string name="global_save_and_exit">Save and exit</string>
<string name="global_yes">Yes</string> <string name="global_yes">Yes</string>
<string name="global_no">No</string> <string name="global_no">No</string>
<string name="preference_controller_map_wiped">The controller config has been wiped</string>
<string name="preference_category_controller_settings">Controller settings</string>
<string name="preference_wipe_controller_title">Reset controller mapping</string>
<string name="preference_wipe_controller_description">Allow you to remap the controller buttons</string>
<string name="preference_deadzone_scale_title">Joystick deadzone scale</string>
<string name="preference_deadzone_scale_description">Increase it if the joystick drifts</string>
</resources> </resources>

View File

@ -123,6 +123,22 @@
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory
android:title="@string/preference_category_controller_settings"
>
<net.kdt.pojavlaunch.prefs.GamepadRemapPreference
android:title="@string/preference_wipe_controller_title"
android:summary="@string/preference_wipe_controller_description"
/>
<net.kdt.pojavlaunch.prefs.CustomSeekBarPreference
android:key="gamepad_deadzone_scale"
android:title="@string/preference_deadzone_scale_title"
android:summary="@string/preference_deadzone_scale_description"
app2:showSeekBarValue="true"
app2:seekBarIncrement="5"
/>
</PreferenceCategory>