Skip to content

Commit

Permalink
Integrate Gamepad Input
Browse files Browse the repository at this point in the history
  • Loading branch information
CADIndie committed Sep 10, 2024
1 parent afbfc2a commit 2578776
Show file tree
Hide file tree
Showing 5 changed files with 410 additions and 41 deletions.
2 changes: 1 addition & 1 deletion src/main/assets/lwjgl/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1725917740464
1725945217571
189 changes: 185 additions & 4 deletions src/main/java/pojlib/UnityPlayerActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import static android.os.Build.VERSION.SDK_INT;

import static org.lwjgl.glfw.CallbackBridge.sendKeyPress;
import static org.lwjgl.glfw.CallbackBridge.sendMouseButton;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityGroup;
import android.content.ClipData;
Expand All @@ -11,24 +15,53 @@
import android.os.Build;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Window;

import androidx.annotation.RequiresApi;

import com.unity3d.player.IUnityPlayerLifecycleEvents;
import com.unity3d.player.UnityPlayer;

import org.lwjgl.glfw.CallbackBridge;

import java.io.File;

import fr.spse.gamepad_remapper.RemapperManager;
import fr.spse.gamepad_remapper.RemapperView;
import pojlib.input.AWTInputBridge;
import pojlib.input.EfficientAndroidLWJGLKeycode;
import pojlib.input.GrabListener;
import pojlib.input.LwjglGlfwKeycode;
import pojlib.input.gamepad.DefaultDataProvider;
import pojlib.input.gamepad.Gamepad;
import pojlib.util.FileUtil;

public class UnityPlayerActivity extends ActivityGroup implements IUnityPlayerLifecycleEvents
public class UnityPlayerActivity extends ActivityGroup implements IUnityPlayerLifecycleEvents, GrabListener
{
protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code
public static volatile ClipboardManager GLOBAL_CLIPBOARD;
private Gamepad mGamepad = null;

private final RemapperManager mInputManager = new RemapperManager(this, new RemapperView.Builder(null)
.remapA(true)
.remapB(true)
.remapX(true)
.remapY(true)

.remapLeftJoystick(true)
.remapRightJoystick(true)
.remapStart(true)
.remapSelect(true)
.remapLeftShoulder(true)
.remapRightShoulder(true)
.remapLeftTrigger(true)
.remapRightTrigger(true)
.remapDpad(true));

private boolean mLastGrabState = false;

// Override this in your custom UnityPlayerActivity to tweak the command line arguments passed to the Unity Android Player
// The command line arguments are passed as a string, separated by spaces
Expand Down Expand Up @@ -69,7 +102,6 @@ protected String updateUnityCommandLineArguments(String cmdLine)

updateWindowSize(this);
GLOBAL_CLIPBOARD = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);

}

public static DisplayMetrics getDisplayMetrics(Activity activity) {
Expand Down Expand Up @@ -136,7 +168,156 @@ public static void putClipboardData(String data, String mimeType) {
if(clipData != null) GLOBAL_CLIPBOARD.setPrimaryClip(clipData);
}

private void createGamepad(InputDevice inputDevice) {
mGamepad = new Gamepad(inputDevice, DefaultDataProvider.INSTANCE);
}

@SuppressLint("NewApi")
@Override
public boolean dispatchGenericMotionEvent(MotionEvent event) {
int mouseCursorIndex = -1;

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

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

for(int i = 0; i < event.getPointerCount(); i++) {
if(event.getToolType(i) != MotionEvent.TOOL_TYPE_MOUSE && event.getToolType(i) != MotionEvent.TOOL_TYPE_STYLUS ) continue;
// Mouse found
mouseCursorIndex = i;
break;
}
if(mouseCursorIndex == -1) return false; // we cant consoom that, theres no mice!

// Make sure we grabbed the mouse if necessary
updateGrabState(CallbackBridge.isGrabbing());

switch(event.getActionMasked()) {
case MotionEvent.ACTION_HOVER_MOVE:
CallbackBridge.mouseX = (event.getX(mouseCursorIndex) * 100);
CallbackBridge.mouseY = (event.getY(mouseCursorIndex) * 100);
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
return true;
case MotionEvent.ACTION_SCROLL:
CallbackBridge.sendScroll((double) event.getAxisValue(MotionEvent.AXIS_HSCROLL), (double) event.getAxisValue(MotionEvent.AXIS_VSCROLL));
return true;
case MotionEvent.ACTION_BUTTON_PRESS:
return sendMouseButtonUnconverted(event.getActionButton(),true);
case MotionEvent.ACTION_BUTTON_RELEASE:
return sendMouseButtonUnconverted(event.getActionButton(),false);
default:
return false;
}
}

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
boolean handleEvent;
if(!(handleEvent = processKeyEvent(event))) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if(event.getAction() != KeyEvent.ACTION_UP) return true; // We eat it anyway
sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_ESCAPE);
return true;
}
}
return handleEvent;
}

/** The event for keyboard/ gamepad button inputs */
public boolean processKeyEvent(KeyEvent event) {
//Log.i("KeyEvent", event.toString());

//Filtering useless events by order of probability
int eventKeycode = event.getKeyCode();
if(eventKeycode == KeyEvent.KEYCODE_UNKNOWN) return true;
if(eventKeycode == KeyEvent.KEYCODE_VOLUME_DOWN) return false;
if(eventKeycode == KeyEvent.KEYCODE_VOLUME_UP) return false;
if(event.getRepeatCount() != 0) return true;
int action = event.getAction();
if(action == KeyEvent.ACTION_MULTIPLE) return true;
// Ignore the cancelled up events. They occur when the user switches layouts.
// In accordance with https://developer.android.com/reference/android/view/KeyEvent#FLAG_CANCELED
if(action == KeyEvent.ACTION_UP &&
(event.getFlags() & KeyEvent.FLAG_CANCELED) != 0) return true;

//Sometimes, key events comes from SOME keys of the software keyboard
//Even weirder, is is unknown why a key or another is selected to trigger a keyEvent
if((event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) == KeyEvent.FLAG_SOFT_KEYBOARD){
if(eventKeycode == KeyEvent.KEYCODE_ENTER) return true; //We already listen to it.
dispatchKeyEvent(event);
return true;
}

//Sometimes, key events may come from the mouse
if(event.getDevice() != null
&& ( (event.getSource() & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE
|| (event.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) ){

if(eventKeycode == KeyEvent.KEYCODE_BACK){
sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT, event.getAction() == KeyEvent.ACTION_DOWN);
return true;
}
}

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

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

int index = EfficientAndroidLWJGLKeycode.getIndexByKey(eventKeycode);
if(EfficientAndroidLWJGLKeycode.containsIndex(index)) {
EfficientAndroidLWJGLKeycode.execKey(event, index);
return true;
}

// Some events will be generated an infinite number of times when no consumed
return (event.getFlags() & KeyEvent.FLAG_FALLBACK) == KeyEvent.FLAG_FALLBACK;
}

/** Convert the mouse button, then send it
* @return Whether the event was processed
*/
public static boolean sendMouseButtonUnconverted(int button, boolean status) {
int glfwButton = -256;
switch (button) {
case MotionEvent.BUTTON_PRIMARY:
glfwButton = LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_LEFT;
break;
case MotionEvent.BUTTON_TERTIARY:
glfwButton = LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_MIDDLE;
break;
case MotionEvent.BUTTON_SECONDARY:
glfwButton = LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT;
break;
}
if(glfwButton == -256) return false;
sendMouseButton(glfwButton, status);
return true;
}

@Override
public void onGrabState(boolean isGrabbing) {
mUnityPlayer.post(()->updateGrabState(isGrabbing));
}

// private TouchEventProcessor pickEventProcessor(boolean isGrabbing) {
// return isGrabbing ? mIngameProcessor : mInGUIProcessor;
// }

private void updateGrabState(boolean isGrabbing) {
// if(mLastGrabState != isGrabbing) {
// mCurrentTouchProcessor.cancelPendingActions();
// mCurrentTouchProcessor = pickEventProcessor(isGrabbing);
// mLastGrabState = isGrabbing;
// }
}

@Override
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
return super.onKeyMultiple(keyCode, repeatCount, event);
}
Expand Down Expand Up @@ -232,14 +413,14 @@ public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
mUnityPlayer.windowFocusChanged(hasFocus);
}

// For some reason the multiple keyevent type is not supported by the ndk.
/* // For some reason the multiple keyevent type is not supported by the ndk.
// Force event injection by overriding dispatchKeyEvent().
@Override public boolean dispatchKeyEvent(KeyEvent event)
{
if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
return mUnityPlayer.injectEvent(event);
return super.dispatchKeyEvent(event);
}
}*/

// Pass any events not handled by (unfocused) views straight to UnityPlayer
@Override public boolean onKeyUp(int keyCode, KeyEvent event) {
Expand Down
Loading

0 comments on commit 2578776

Please sign in to comment.