From 7dea895469f45a799ccda8f409f9c83d607b2628 Mon Sep 17 00:00:00 2001 From: ShirosakiMio <852468399@qq.com> Date: Tue, 12 Mar 2024 17:31:34 +0800 Subject: [PATCH] add support for gamepad --- .../tungsten/fcl/activity/JVMActivity.java | 10 ++ .../com/tungsten/fcl/control/FCLInput.java | 141 +++++++++++++++++- .../keycodes/GamepadKeycodeMap.java | 54 +++++++ 3 files changed, 200 insertions(+), 5 deletions(-) create mode 100644 FCLauncher/src/main/java/com/tungsten/fclauncher/keycodes/GamepadKeycodeMap.java diff --git a/FCL/src/main/java/com/tungsten/fcl/activity/JVMActivity.java b/FCL/src/main/java/com/tungsten/fcl/activity/JVMActivity.java index c2864e23c..745eb0cb0 100644 --- a/FCL/src/main/java/com/tungsten/fcl/activity/JVMActivity.java +++ b/FCL/src/main/java/com/tungsten/fcl/activity/JVMActivity.java @@ -5,6 +5,7 @@ import android.graphics.SurfaceTexture; import android.os.Bundle; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.Surface; import android.view.TextureView; import android.view.ViewGroup; @@ -155,6 +156,15 @@ public boolean dispatchKeyEvent(KeyEvent event) { return handleEvent; } + @Override + public boolean dispatchGenericMotionEvent(MotionEvent event) { + boolean handleEvent = true; + if (menu != null && menuType == MenuType.GAME) { + handleEvent = menu.getInput().handleGenericMotionEvent(event); + } + return handleEvent; + } + @Override protected void onPostResume() { super.onPostResume(); diff --git a/FCL/src/main/java/com/tungsten/fcl/control/FCLInput.java b/FCL/src/main/java/com/tungsten/fcl/control/FCLInput.java index b1cc946e8..1992d957b 100644 --- a/FCL/src/main/java/com/tungsten/fcl/control/FCLInput.java +++ b/FCL/src/main/java/com/tungsten/fcl/control/FCLInput.java @@ -1,5 +1,6 @@ package com.tungsten.fcl.control; +import android.view.Choreographer; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; @@ -10,6 +11,7 @@ import com.tungsten.fclauncher.keycodes.AndroidKeycodeMap; import com.tungsten.fclauncher.keycodes.FCLKeycodes; import com.tungsten.fclauncher.bridge.FCLBridge; +import com.tungsten.fclauncher.keycodes.GamepadKeycodeMap; import java.util.HashMap; @@ -26,6 +28,15 @@ public class FCLInput implements View.OnCapturedPointerListener, View.OnGenericM private final int screenWidth; private final int screenHeight; + //for gamepad + private int dpadLastKey = -1; + private int currentDirection = -1; + private long lastFrameTime; + private Choreographer choreographer; + private float lastXAxis; + private float lastYAxis; + + public static final HashMap MOUSE_MAP = new HashMap() { { put(MOUSE_LEFT, FCLBridge.Button1); @@ -132,9 +143,20 @@ private boolean handleExternalMouseEvent(MotionEvent event) { @Override public boolean onCapturedPointer(View view, MotionEvent event) { + return handleMouse(event, 0); + } + + private boolean handleMouse(MotionEvent event, float deltaTimeScale) { if (menu instanceof GameMenu) { - int deltaX = (int) (event.getX() * ((GameMenu) menu).getMenuSetting().getMouseSensitivity()); - int deltaY = (int) (event.getY() * ((GameMenu) menu).getMenuSetting().getMouseSensitivity()); + int deltaX; + int deltaY; + if (event != null) { + deltaX = (int) (event.getX() * ((GameMenu) menu).getMenuSetting().getMouseSensitivity()); + deltaY = (int) (event.getY() * ((GameMenu) menu).getMenuSetting().getMouseSensitivity()); + } else { + deltaX = (int) (lastXAxis * deltaTimeScale * 10 * ((GameMenu) menu).getMenuSetting().getMouseSensitivity()); + deltaY = (int) (lastYAxis * deltaTimeScale * 10 * ((GameMenu) menu).getMenuSetting().getMouseSensitivity()); + } if (menu.getCursorMode() == FCLBridge.CursorEnabled) { int targetX = Math.max(0, Math.min(screenWidth, ((GameMenu) menu).getCursorX() + deltaX)); int targetY = Math.max(0, Math.min(screenHeight, ((GameMenu) menu).getCursorY() + deltaY)); @@ -153,10 +175,11 @@ public boolean onCapturedPointer(View view, MotionEvent event) { setPointerId(null); } } - return handleExternalMouseEvent(event); - } else { - return false; + if (event != null){ + return handleExternalMouseEvent(event); + } } + return false; } @Override @@ -187,6 +210,13 @@ public boolean handleKeyEvent(KeyEvent event) { return true; } } + if (event.getDevice() != null && ((event.getSource() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)) { + if (event.getRepeatCount() == 0) { + int converted = GamepadKeycodeMap.convert(event.getKeyCode()); + sendKeyEvent(converted, event.getAction() == KeyEvent.ACTION_DOWN); + } + return true; + } if ((event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) == KeyEvent.FLAG_SOFT_KEYBOARD) { if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) return true; @@ -209,4 +239,105 @@ public boolean handleKeyEvent(KeyEvent event) { } return true; } + + public boolean handleGenericMotionEvent(MotionEvent event) { + if (event.getDevice() != null && event.getSource() == InputDevice.SOURCE_JOYSTICK) { + if (choreographer == null) { + choreographer = Choreographer.getInstance(); + Choreographer.FrameCallback frameCallback = new Choreographer.FrameCallback() { + @Override + public void doFrame(long frameTimeNanos) { + doTick(); + choreographer.postFrameCallback(this); + } + }; + choreographer.postFrameCallback(frameCallback); + } + if (event.getAction() == MotionEvent.ACTION_MOVE) { + handleDPad(event); + handleLeftJoyStick(event); + handleRightJoyStick(event); + } + return true; + } + return false; + } + + private void handleDPad(MotionEvent event) { + float xAxis = event.getAxisValue(MotionEvent.AXIS_HAT_X); + float yAxis = event.getAxisValue(MotionEvent.AXIS_HAT_Y); + if (Float.compare(xAxis, -1.0f) == 0) { + dpadLastKey = KeyEvent.KEYCODE_DPAD_LEFT; + } else if (Float.compare(xAxis, 1.0f) == 0) { + dpadLastKey = KeyEvent.KEYCODE_DPAD_RIGHT; + } else if (Float.compare(yAxis, -1.0f) == 0) { + dpadLastKey = KeyEvent.KEYCODE_DPAD_UP; + } else if (Float.compare(yAxis, 1.0f) == 0) { + dpadLastKey = KeyEvent.KEYCODE_DPAD_DOWN; + } + sendKeyEvent(GamepadKeycodeMap.convert(dpadLastKey), (xAxis != 0 || yAxis != 0)); + } + + private void handleLeftJoyStick(MotionEvent event) { + float xAxis = event.getAxisValue(MotionEvent.AXIS_X); + float yAxis = event.getAxisValue(MotionEvent.AXIS_Y); + double dist = Math.hypot(Math.abs(xAxis), Math.abs(yAxis)); + //todo: custom deadzone + if (dist >= 0.2) { + double degrees = Math.toDegrees(-Math.atan2(yAxis, xAxis)); + if (degrees < 0) { + degrees += 360; + } + int lastDirection = currentDirection; + currentDirection = ((int) ((degrees + 22.5) / 45)) % 8; + sendDirection(lastDirection, false); + sendDirection(currentDirection, true); + } else { + sendDirection(0, false); + sendDirection(2, false); + sendDirection(4, false); + sendDirection(6, false); + } + } + + private void sendDirection(int direction, boolean press) { + if ((direction & 1) == 0) { + sendKeyEvent(GamepadKeycodeMap.convert(GamepadKeycodeMap.LEFT_JOYSTICK_RIGHT + direction), press); + } else { + sendKeyEvent(GamepadKeycodeMap.convert(GamepadKeycodeMap.LEFT_JOYSTICK_RIGHT + direction - 1), press); + int keyCode = GamepadKeycodeMap.LEFT_JOYSTICK_RIGHT + direction + 1; + if (keyCode == 2008) { + keyCode = 0; + } + sendKeyEvent(GamepadKeycodeMap.convert(keyCode), press); + } + } + + private void doTick() { + long newFrameTime = System.nanoTime(); + if (lastXAxis != 0 || lastYAxis != 0) { + newFrameTime = System.nanoTime(); + float deltaTimeScale = ((newFrameTime - lastFrameTime) / 16666666f); + handleMouse(null, deltaTimeScale); + } + lastFrameTime = newFrameTime; + } + + private void handleRightJoyStick(MotionEvent event) { + float xAxis = event.getAxisValue(MotionEvent.AXIS_Z); + float yAxis = event.getAxisValue(MotionEvent.AXIS_RZ); + double dist = Math.hypot(Math.abs(xAxis), Math.abs(yAxis)); + //todo: custom deadzone + if (dist < 0.2) { + lastXAxis = 0; + lastYAxis = 0; + return; + } + if (lastXAxis != xAxis || lastYAxis != yAxis) { + lastXAxis = xAxis; + lastYAxis = yAxis; + doTick(); + } + } + } diff --git a/FCLauncher/src/main/java/com/tungsten/fclauncher/keycodes/GamepadKeycodeMap.java b/FCLauncher/src/main/java/com/tungsten/fclauncher/keycodes/GamepadKeycodeMap.java new file mode 100644 index 000000000..0ce297c8a --- /dev/null +++ b/FCLauncher/src/main/java/com/tungsten/fclauncher/keycodes/GamepadKeycodeMap.java @@ -0,0 +1,54 @@ +package com.tungsten.fclauncher.keycodes; + +import android.view.KeyEvent; + +import java.util.HashMap; + +public class GamepadKeycodeMap { + private static final HashMap KEY_MAP = new HashMap<>(); + private static final int MOUSE_LEFT = 1000; + private static final int MOUSE_MIDDLE = 1001; + private static final int MOUSE_RIGHT = 1002; + private static final int MOUSE_SCROLL_UP = 1003; + private static final int MOUSE_SCROLL_DOWN = 1004; + public static final int LEFT_JOYSTICK_RIGHT = 2000; + public static final int LEFT_JOYSTICK_UP = 2002; + public static final int LEFT_JOYSTICK_LEFT = 2004; + public static final int LEFT_JOYSTICK_DOWN = 2006; + + private static void add(int key, int value) { + KEY_MAP.put(key, value); + } + + //todo: custom keycode + public static void modify(int key, int value) { + KEY_MAP.replace(key, value); + } + + public static int convert(int key) { + return KEY_MAP.getOrDefault(key, -1); + } + + static { + add(KeyEvent.KEYCODE_BUTTON_A, FCLKeycodes.KEY_SPACE); + add(KeyEvent.KEYCODE_BUTTON_B, FCLKeycodes.KEY_Q); + add(KeyEvent.KEYCODE_BUTTON_X, FCLKeycodes.KEY_E); + add(KeyEvent.KEYCODE_BUTTON_Y, FCLKeycodes.KEY_F); + add(KeyEvent.KEYCODE_BUTTON_L1, MOUSE_SCROLL_UP); + add(KeyEvent.KEYCODE_BUTTON_R1, MOUSE_SCROLL_DOWN); + add(KeyEvent.KEYCODE_BUTTON_L2, MOUSE_LEFT); + add(KeyEvent.KEYCODE_BUTTON_R2, MOUSE_RIGHT); + add(KeyEvent.KEYCODE_BUTTON_THUMBL, FCLKeycodes.KEY_LEFTCTRL); + add(KeyEvent.KEYCODE_BUTTON_THUMBR, FCLKeycodes.KEY_LEFTSHIFT); + add(KeyEvent.KEYCODE_BUTTON_START, FCLKeycodes.KEY_ESC); + add(KeyEvent.KEYCODE_BUTTON_SELECT, FCLKeycodes.KEY_TAB); + add(KeyEvent.KEYCODE_DPAD_UP, FCLKeycodes.KEY_LEFTSHIFT); + add(KeyEvent.KEYCODE_DPAD_DOWN, FCLKeycodes.KEY_O); + add(KeyEvent.KEYCODE_DPAD_LEFT, FCLKeycodes.KEY_J); + add(KeyEvent.KEYCODE_DPAD_RIGHT, FCLKeycodes.KEY_K); + add(LEFT_JOYSTICK_UP, FCLKeycodes.KEY_W); + add(LEFT_JOYSTICK_DOWN, FCLKeycodes.KEY_S); + add(LEFT_JOYSTICK_LEFT, FCLKeycodes.KEY_A); + add(LEFT_JOYSTICK_RIGHT, FCLKeycodes.KEY_D); + } +}