-
Notifications
You must be signed in to change notification settings - Fork 148
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
KeyPressedPreferNative: More performat key input
On windows, `Gdx.input.isKeyPressed` uses lwjgl's event system for fetching key inputs. This event-based system sometimes lags due to OS issues, so beatoraja had problem getting key state ASAP. When pressing multiple keys, some keys were registered later than other keys. This commit fixes the issue by utilizing Windows's `GetAsyncKeyState` function for fetching current keyboard state itself. Note that this function will poll key state regardless of whether the window is focused or not, so this could be documented. Reference: previous libgdx input polling system (from LWJGL2) https://github.com/LWJGL/lwjgl/blob/master/src/java/org/lwjgl/opengl/WindowsKeyboard.java
- Loading branch information
Showing
4 changed files
with
216 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
214 changes: 214 additions & 0 deletions
214
src/bms/player/beatoraja/input/KeyPressedPreferNative.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
// Copyright (c) 2024 Park Hyunwoo | ||
// | ||
// This software is provided 'as-is', without any express or implied | ||
// warranty. In no event will the authors be held liable for any damages | ||
// arising from the use of this software. | ||
// | ||
// Permission is granted to anyone to use this software for any purpose, | ||
// including commercial applications, and to alter it and redistribute it | ||
// freely, subject to the following restrictions: | ||
// | ||
// 1. The origin of this software must not be misrepresented; you must not | ||
// claim that you wrote the original software. If you use this software | ||
// in a product, an acknowledgment in the product documentation would be | ||
// appreciated but is not required. | ||
// 2. Altered source versions must be plainly marked as such, and must not be | ||
// misrepresented as being the original software. | ||
// 3. This notice may not be removed or altered from any source distribution. | ||
|
||
package bms.player.beatoraja.input; | ||
|
||
import com.badlogic.gdx.Gdx; | ||
import com.badlogic.gdx.Input; | ||
import com.sun.jna.platform.win32.User32; | ||
import com.sun.jna.platform.win32.Win32VK; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
import static com.sun.jna.platform.win32.Win32VK.*; | ||
|
||
/** | ||
* Performant key getter for beatoraja | ||
* Gdx.input.isKeyPressed uses GLFW event system to process keys, so in case of | ||
* delay or CPU overuse, it might not be performant enough to process keys ASAP. | ||
* <p> | ||
* Windows has `User32.GetAsyncKeyState` as a faster alternative, so this library | ||
* wraps around the api for libgdx. | ||
*/ | ||
public class KeyPressedPreferNative { | ||
// Os utilities | ||
private static String OS = null; | ||
|
||
public static String getOsName() { | ||
if (OS == null) { | ||
OS = System.getProperty("os.name"); | ||
} | ||
return OS; | ||
} | ||
|
||
public static boolean isWindows() { | ||
return getOsName().startsWith("Windows"); | ||
} | ||
|
||
// Windows- specific utilities | ||
public static boolean windowsGetAsyncKeyState(int vKey) { | ||
return (User32.INSTANCE.GetAsyncKeyState(vKey) & 0x8000) != 0; | ||
} | ||
|
||
public static boolean windowsGetAsyncKeyStateVK(Win32VK vKey) { | ||
return (User32.INSTANCE.GetAsyncKeyState(vKey.code) & 0x8000) != 0; | ||
} | ||
|
||
public static boolean windowsIsKeyPressed(int gdxKey) { | ||
// Key list reference: https://github.com/libgdx/libgdx/blob/1.8.0/backends/gdx-backend-lwjgl/src/com/badlogic/gdx/backends/lwjgl/LwjglInput.java | ||
// Vkey reference: https://github.com/LWJGL/lwjgl/blob/master/src/java/org/lwjgl/opengl/WindowsKeycodes.java | ||
switch (gdxKey) { | ||
case Input.Keys.LEFT_BRACKET: | ||
return windowsGetAsyncKeyStateVK(VK_OEM_4); | ||
case Input.Keys.RIGHT_BRACKET: | ||
return windowsGetAsyncKeyStateVK(VK_OEM_6); | ||
case Input.Keys.GRAVE: | ||
return windowsGetAsyncKeyStateVK(VK_OEM_8); | ||
case Input.Keys.STAR: | ||
return windowsGetAsyncKeyStateVK(VK_MULTIPLY); | ||
case Input.Keys.NUM: | ||
return windowsGetAsyncKeyStateVK(VK_NUMLOCK); | ||
case Input.Keys.PERIOD: | ||
return windowsGetAsyncKeyStateVK(VK_OEM_PERIOD); | ||
case Input.Keys.SLASH: | ||
return windowsGetAsyncKeyStateVK(VK_OEM_2); | ||
case Input.Keys.SYM: | ||
return windowsGetAsyncKeyStateVK(VK_RWIN); | ||
case Input.Keys.EQUALS: | ||
return windowsGetAsyncKeyStateVK(VK_OEM_PLUS); | ||
case Input.Keys.COMMA: | ||
return windowsGetAsyncKeyStateVK(VK_OEM_COMMA); | ||
case Input.Keys.ENTER: | ||
return windowsGetAsyncKeyStateVK(VK_RETURN); | ||
case Input.Keys.NUM_0: | ||
case Input.Keys.NUM_1: | ||
case Input.Keys.NUM_2: | ||
case Input.Keys.NUM_3: | ||
case Input.Keys.NUM_4: | ||
case Input.Keys.NUM_5: | ||
case Input.Keys.NUM_6: | ||
case Input.Keys.NUM_7: | ||
case Input.Keys.NUM_8: | ||
case Input.Keys.NUM_9: | ||
return windowsGetAsyncKeyState(0x30 + gdxKey - Input.Keys.NUM_0); | ||
case Input.Keys.A: | ||
case Input.Keys.B: | ||
case Input.Keys.C: | ||
case Input.Keys.D: | ||
case Input.Keys.E: | ||
case Input.Keys.F: | ||
case Input.Keys.G: | ||
case Input.Keys.H: | ||
case Input.Keys.I: | ||
case Input.Keys.J: | ||
case Input.Keys.K: | ||
case Input.Keys.L: | ||
case Input.Keys.M: | ||
case Input.Keys.N: | ||
case Input.Keys.O: | ||
case Input.Keys.P: | ||
case Input.Keys.Q: | ||
case Input.Keys.R: | ||
case Input.Keys.S: | ||
case Input.Keys.T: | ||
case Input.Keys.U: | ||
case Input.Keys.V: | ||
case Input.Keys.W: | ||
case Input.Keys.X: | ||
case Input.Keys.Y: | ||
case Input.Keys.Z: | ||
return windowsGetAsyncKeyState(0x41 + gdxKey - Input.Keys.A); | ||
case Input.Keys.ALT_LEFT: | ||
return windowsGetAsyncKeyStateVK(VK_LMENU); | ||
case Input.Keys.ALT_RIGHT: | ||
return windowsGetAsyncKeyStateVK(VK_RMENU); | ||
case Input.Keys.BACKSLASH: | ||
return windowsGetAsyncKeyStateVK(VK_OEM_5); | ||
case Input.Keys.FORWARD_DEL: | ||
return windowsGetAsyncKeyStateVK(VK_DELETE); | ||
case Input.Keys.DPAD_LEFT: | ||
return windowsGetAsyncKeyStateVK(VK_LEFT); | ||
case Input.Keys.DPAD_RIGHT: | ||
return windowsGetAsyncKeyStateVK(VK_RIGHT); | ||
case Input.Keys.DPAD_UP: | ||
return windowsGetAsyncKeyStateVK(VK_UP); | ||
case Input.Keys.DPAD_DOWN: | ||
return windowsGetAsyncKeyStateVK(VK_DOWN); | ||
case Input.Keys.HOME: | ||
return windowsGetAsyncKeyStateVK(VK_HOME); | ||
case Input.Keys.MINUS: | ||
return windowsGetAsyncKeyStateVK(VK_SUBTRACT); | ||
case Input.Keys.PLUS: | ||
return windowsGetAsyncKeyStateVK(VK_ADD); | ||
case Input.Keys.SEMICOLON: | ||
case Input.Keys.COLON: | ||
return windowsGetAsyncKeyStateVK(VK_OEM_1); | ||
case Input.Keys.SHIFT_LEFT: | ||
return windowsGetAsyncKeyStateVK(VK_LSHIFT); | ||
case Input.Keys.SHIFT_RIGHT: | ||
return windowsGetAsyncKeyStateVK(VK_RSHIFT); | ||
case Input.Keys.SPACE: | ||
return windowsGetAsyncKeyStateVK(VK_SPACE); | ||
case Input.Keys.TAB: | ||
return windowsGetAsyncKeyStateVK(VK_TAB); | ||
case Input.Keys.CONTROL_LEFT: | ||
return windowsGetAsyncKeyStateVK(VK_LCONTROL); | ||
case Input.Keys.CONTROL_RIGHT: | ||
return windowsGetAsyncKeyStateVK(VK_RCONTROL); | ||
case Input.Keys.PAGE_DOWN: | ||
return windowsGetAsyncKeyStateVK(VK_NEXT); | ||
case Input.Keys.PAGE_UP: | ||
return windowsGetAsyncKeyStateVK(VK_PRIOR); | ||
case Input.Keys.ESCAPE: | ||
return windowsGetAsyncKeyStateVK(VK_ESCAPE); | ||
case Input.Keys.END: | ||
return windowsGetAsyncKeyStateVK(VK_END); | ||
case Input.Keys.INSERT: | ||
return windowsGetAsyncKeyStateVK(VK_INSERT); | ||
case Input.Keys.DEL: | ||
return windowsGetAsyncKeyStateVK(VK_BACK); | ||
case Input.Keys.APOSTROPHE: | ||
return windowsGetAsyncKeyStateVK(VK_OEM_7); | ||
case Input.Keys.F1: | ||
case Input.Keys.F2: | ||
case Input.Keys.F3: | ||
case Input.Keys.F4: | ||
case Input.Keys.F5: | ||
case Input.Keys.F6: | ||
case Input.Keys.F7: | ||
case Input.Keys.F8: | ||
case Input.Keys.F9: | ||
case Input.Keys.F10: | ||
case Input.Keys.F11: | ||
case Input.Keys.F12: | ||
return windowsGetAsyncKeyState(gdxKey - Input.Keys.F1 + VK_F1.code); | ||
case Input.Keys.NUMPAD_0: | ||
case Input.Keys.NUMPAD_1: | ||
case Input.Keys.NUMPAD_2: | ||
case Input.Keys.NUMPAD_3: | ||
case Input.Keys.NUMPAD_4: | ||
case Input.Keys.NUMPAD_5: | ||
case Input.Keys.NUMPAD_6: | ||
case Input.Keys.NUMPAD_7: | ||
case Input.Keys.NUMPAD_8: | ||
case Input.Keys.NUMPAD_9: | ||
return windowsGetAsyncKeyState(gdxKey - Input.Keys.NUMPAD_0 + VK_NUMPAD0.code); | ||
default: // Fallback | ||
// TODO: above list should be exhaustive. Show error | ||
return Gdx.input.isKeyJustPressed(gdxKey); | ||
} | ||
} | ||
|
||
public static boolean isKeyPressed(int gdxKey) { | ||
if (isWindows()) { | ||
return windowsIsKeyPressed(gdxKey); | ||
} | ||
return Gdx.input.isKeyPressed(gdxKey); | ||
} | ||
} |