Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat[input]: GLFW direct controller input #6604

Merged
merged 5 commits into from
Feb 7, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1687691695196
1738839797928
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1692525087345
1738839797913
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1732218529630
1738839797921
Original file line number Diff line number Diff line change
@@ -29,6 +29,8 @@
import net.kdt.pojavlaunch.customcontrols.ControlLayout;
import net.kdt.pojavlaunch.customcontrols.gamepad.DefaultDataProvider;
import net.kdt.pojavlaunch.customcontrols.gamepad.Gamepad;
import net.kdt.pojavlaunch.customcontrols.gamepad.direct.DirectGamepad;
import net.kdt.pojavlaunch.customcontrols.gamepad.direct.DirectGamepadEnableHandler;
import net.kdt.pojavlaunch.customcontrols.mouse.AbstractTouchpad;
import net.kdt.pojavlaunch.customcontrols.mouse.AndroidPointerCapture;
import net.kdt.pojavlaunch.customcontrols.mouse.InGUIEventProcessor;
@@ -40,15 +42,16 @@

import org.lwjgl.glfw.CallbackBridge;

import fr.spse.gamepad_remapper.GamepadHandler;
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
*/
public class MinecraftGLSurface extends View implements GrabListener {
public class MinecraftGLSurface extends View implements GrabListener, DirectGamepadEnableHandler {
/* Gamepad object for gamepad inputs, instantiated on need */
private Gamepad mGamepad = null;
private GamepadHandler mGamepadHandler;
/* 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)
@@ -88,6 +91,7 @@ public MinecraftGLSurface(Context context) {
public MinecraftGLSurface(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
setFocusable(true);
CallbackBridge.setDirectGamepadEnableHandler(this);
}

@RequiresApi(api = Build.VERSION_CODES.O)
@@ -203,7 +207,11 @@ public boolean onTouchEvent(MotionEvent e) {
}

private void createGamepad(View contextView, InputDevice inputDevice) {
mGamepad = new Gamepad(contextView, inputDevice, DefaultDataProvider.INSTANCE, true);
if(CallbackBridge.sGamepadDirectInput) {
mGamepadHandler = new DirectGamepad();
}else {
mGamepadHandler = new Gamepad(contextView, inputDevice, DefaultDataProvider.INSTANCE, true);
}
}

/**
@@ -215,9 +223,9 @@ public boolean dispatchGenericMotionEvent(MotionEvent event) {
int mouseCursorIndex = -1;

if(Gamepad.isGamepadEvent(event)){
if(mGamepad == null) createGamepad(this, event.getDevice());
if(mGamepadHandler == null) createGamepad(this, event.getDevice());

mInputManager.handleMotionEventInput(getContext(), event, mGamepad);
mInputManager.handleMotionEventInput(getContext(), event, mGamepadHandler);
return true;
}

@@ -287,9 +295,9 @@ public boolean processKeyEvent(KeyEvent event) {
}

if(Gamepad.isGamepadEvent(event)){
if(mGamepad == null) createGamepad(this, event.getDevice());
if(mGamepadHandler == null) createGamepad(this, event.getDevice());

mInputManager.handleKeyEventInput(getContext(), event, mGamepad);
mInputManager.handleKeyEventInput(getContext(), event, mGamepadHandler);
return true;
}

@@ -411,6 +419,17 @@ private void updateGrabState(boolean isGrabbing) {
}
}

@Override
public void onDirectGamepadEnabled() {
post(()->{
if(mGamepadHandler != null && mGamepadHandler instanceof Gamepad) {
((Gamepad)mGamepadHandler).removeSelf();
}
// Force gamepad recreation on next event
mGamepadHandler = null;
});
}

/** A small interface called when the listener is ready for the first time */
public interface SurfaceReadyListener {
void isReady();
Original file line number Diff line number Diff line change
@@ -30,4 +30,9 @@ public boolean isGrabbing() {
public void attachGrabListener(GrabListener grabListener) {
CallbackBridge.addGrabListener(grabListener);
}

@Override
public void detachGrabListener(GrabListener grabListener) {
CallbackBridge.removeGrabListener(grabListener);
}
}
Original file line number Diff line number Diff line change
@@ -26,7 +26,6 @@
import net.kdt.pojavlaunch.GrabListener;
import net.kdt.pojavlaunch.LwjglGlfwKeycode;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
import net.kdt.pojavlaunch.utils.MCOptionUtils;

import org.lwjgl.glfw.CallbackBridge;
@@ -88,6 +87,8 @@ public class Gamepad implements GrabListener, GamepadHandler {

private final GamepadDataProvider mMapProvider;

private boolean mRemoved = false;

public Gamepad(View contextView, InputDevice inputDevice, GamepadDataProvider mapProvider, boolean showCursor){
Settings.setDeadzoneScale(PREF_DEADZONE_SCALE);

@@ -96,7 +97,7 @@ public Gamepad(View contextView, InputDevice inputDevice, GamepadDataProvider ma
@Override
public void doFrame(long frameTimeNanos) {
tick(frameTimeNanos);
mScreenChoreographer.postFrameCallback(this);
if(!mRemoved) mScreenChoreographer.postFrameCallback(this);
}
};
mScreenChoreographer.postFrameCallback(frameCallback);
@@ -453,4 +454,15 @@ public void handleGamepadInput(int keycode, float value) {
break;
}
}

/**
* Stops the Gamepad and removes all traces of the Gamepad from the view hierarchy.
* After this call, the Gamepad is not recoverable and a new one must be made.
*/
public void removeSelf() {
mRemoved = true;
mMapProvider.detachGrabListener(this);
ViewGroup viewGroup = (ViewGroup) mPointerImageView.getParent();
if(viewGroup != null) viewGroup.removeView(mPointerImageView);
}
}
Original file line number Diff line number Diff line change
@@ -7,4 +7,5 @@ public interface GamepadDataProvider {
GamepadMap getGameMap();
boolean isGrabbing();
void attachGrabListener(GrabListener grabListener);
void detachGrabListener(GrabListener grabListener);
}
Original file line number Diff line number Diff line change
@@ -325,6 +325,11 @@ public void attachGrabListener(GrabListener grabListener) {
grabListener.onGrabState(mGrabState);
}

@Override
public void detachGrabListener(GrabListener grabListener) {
mGamepadGrabListener = null;
}

public void setGrabState(boolean newState) {
mGrabState = newState;
if(mGamepadGrabListener != null) mGamepadGrabListener.onGrabState(newState);
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package net.kdt.pojavlaunch.customcontrols.gamepad.direct;

import static org.lwjgl.glfw.CallbackBridge.sGamepadAxisBuffer;
import static org.lwjgl.glfw.CallbackBridge.sGamepadButtonBuffer;

import android.view.KeyEvent;
import android.view.MotionEvent;

import fr.spse.gamepad_remapper.GamepadHandler;

public class DirectGamepad implements GamepadHandler {
@Override
public void handleGamepadInput(int keycode, float value) {
int gKeycode = -1, gAxis = -1;
switch (keycode) {
case KeyEvent.KEYCODE_BUTTON_A: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_A; break;
case KeyEvent.KEYCODE_BUTTON_B: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_B; break;
case KeyEvent.KEYCODE_BUTTON_X: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_X; break;
case KeyEvent.KEYCODE_BUTTON_Y: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_Y; break;
case KeyEvent.KEYCODE_BUTTON_L1: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER; break;
case KeyEvent.KEYCODE_BUTTON_R1: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER; break;
case KeyEvent.KEYCODE_BUTTON_L2:
case MotionEvent.AXIS_LTRIGGER:
gAxis = GamepadKeycodes.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER;
break;
case KeyEvent.KEYCODE_BUTTON_R2:
case MotionEvent.AXIS_RTRIGGER:
gAxis = GamepadKeycodes.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER;
break;
case KeyEvent.KEYCODE_BUTTON_THUMBL: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_LEFT_THUMB; break;
case KeyEvent.KEYCODE_BUTTON_THUMBR: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB; break;
case KeyEvent.KEYCODE_BUTTON_START: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_START; break;
case KeyEvent.KEYCODE_BUTTON_SELECT: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_BACK; break;
case KeyEvent.KEYCODE_DPAD_UP: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_DPAD_UP; break;
case KeyEvent.KEYCODE_DPAD_DOWN: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_DPAD_DOWN; break;
case KeyEvent.KEYCODE_DPAD_LEFT: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_DPAD_LEFT; break;
case KeyEvent.KEYCODE_DPAD_RIGHT: gKeycode = GamepadKeycodes.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT; break;
case KeyEvent.KEYCODE_DPAD_CENTER:
// Behave the same way as the Gamepad here, as GLFW doesn't have a keycode
// for the dpad center.
sGamepadButtonBuffer.put(GamepadKeycodes.GLFW_GAMEPAD_BUTTON_DPAD_UP, GamepadKeycodes.GLFW_RELEASE);
sGamepadButtonBuffer.put(GamepadKeycodes.GLFW_GAMEPAD_BUTTON_DPAD_DOWN, GamepadKeycodes.GLFW_RELEASE);
sGamepadButtonBuffer.put(GamepadKeycodes.GLFW_GAMEPAD_BUTTON_DPAD_LEFT, GamepadKeycodes.GLFW_RELEASE);
sGamepadButtonBuffer.put(GamepadKeycodes.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, GamepadKeycodes.GLFW_RELEASE);
return;
case MotionEvent.AXIS_X: gAxis = GamepadKeycodes.GLFW_GAMEPAD_AXIS_LEFT_X; break;
case MotionEvent.AXIS_Y: gAxis = GamepadKeycodes.GLFW_GAMEPAD_AXIS_LEFT_Y; break;
case MotionEvent.AXIS_Z: gAxis = GamepadKeycodes.GLFW_GAMEPAD_AXIS_RIGHT_X; break;
case MotionEvent.AXIS_RZ: gAxis = GamepadKeycodes.GLFW_GAMEPAD_AXIS_RIGHT_Y; break;
case MotionEvent.AXIS_HAT_X:
sGamepadButtonBuffer.put(
GamepadKeycodes.GLFW_GAMEPAD_BUTTON_DPAD_LEFT,
value < -0.85 ? GamepadKeycodes.GLFW_PRESS : GamepadKeycodes.GLFW_RELEASE
);
sGamepadButtonBuffer.put(
GamepadKeycodes.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT,
value > 0.85 ? GamepadKeycodes.GLFW_PRESS : GamepadKeycodes.GLFW_RELEASE
);
return;
case MotionEvent.AXIS_HAT_Y:
sGamepadButtonBuffer.put(
GamepadKeycodes.GLFW_GAMEPAD_BUTTON_DPAD_UP,
value < -0.85 ? GamepadKeycodes.GLFW_PRESS : GamepadKeycodes.GLFW_RELEASE
);
sGamepadButtonBuffer.put(
GamepadKeycodes.GLFW_GAMEPAD_BUTTON_DPAD_DOWN,
value > 0.85 ? GamepadKeycodes.GLFW_PRESS : GamepadKeycodes.GLFW_RELEASE
);
return;
}
if(gKeycode != -1) {
sGamepadButtonBuffer.put(gKeycode, value > 0.85 ? GamepadKeycodes.GLFW_PRESS : GamepadKeycodes.GLFW_RELEASE);
}
if(gAxis != -1) {
sGamepadAxisBuffer.put(gAxis, value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package net.kdt.pojavlaunch.customcontrols.gamepad.direct;

/**
* Interface that is called once when the GLFW implementation requests to switch from
* the default gamepad implementation to the direct one. The implementor of this interface
* must take the necessary steps to disable the default gamepad implementation and replace
* it with an instance of DirectGamepad.
* This is useful for switching from default to direct input after the user has already
* interacted with the gamepad.
*/
public interface DirectGamepadEnableHandler {
/**
* Called once when GLFW requests switching the gamepad mode from default to direct.
*/
void onDirectGamepadEnabled();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package net.kdt.pojavlaunch.customcontrols.gamepad.direct;

public class GamepadKeycodes {
public static final byte GLFW_RELEASE = 0;
public static final byte GLFW_PRESS = 1;

public static final short GLFW_GAMEPAD_BUTTON_A = 0;
public static final short GLFW_GAMEPAD_BUTTON_B = 1;
public static final short GLFW_GAMEPAD_BUTTON_X = 2;
public static final short GLFW_GAMEPAD_BUTTON_Y = 3;
public static final short GLFW_GAMEPAD_BUTTON_LEFT_BUMPER = 4;
public static final short GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER = 5;
public static final short GLFW_GAMEPAD_BUTTON_BACK = 6;
public static final short GLFW_GAMEPAD_BUTTON_START = 7;
// Home button, unused because Android takes the home button events
// for itself
public static final short GLFW_GAMEPAD_BUTTON_GUIDE = 8;
public static final short GLFW_GAMEPAD_BUTTON_LEFT_THUMB = 9;
public static final short GLFW_GAMEPAD_BUTTON_RIGHT_THUMB = 10;
public static final short GLFW_GAMEPAD_BUTTON_DPAD_UP = 11;
public static final short GLFW_GAMEPAD_BUTTON_DPAD_RIGHT = 12;
public static final short GLFW_GAMEPAD_BUTTON_DPAD_DOWN = 13;
public static final short GLFW_GAMEPAD_BUTTON_DPAD_LEFT = 14;

public static final short GLFW_GAMEPAD_AXIS_LEFT_X = 0;
public static final short GLFW_GAMEPAD_AXIS_LEFT_Y = 1;
public static final short GLFW_GAMEPAD_AXIS_RIGHT_X = 2;
public static final short GLFW_GAMEPAD_AXIS_RIGHT_Y = 3;
public static final short GLFW_GAMEPAD_AXIS_LEFT_TRIGGER = 4;
public static final short GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER = 5;
}
Original file line number Diff line number Diff line change
@@ -2,8 +2,6 @@

import android.content.Context;
import android.content.SharedPreferences;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Bundle;

import androidx.preference.PreferenceCategory;
Loading