diff --git a/src/bms/player/beatoraja/config/KeyConfiguration.java b/src/bms/player/beatoraja/config/KeyConfiguration.java index e8f2b79f9..6513e7160 100644 --- a/src/bms/player/beatoraja/config/KeyConfiguration.java +++ b/src/bms/player/beatoraja/config/KeyConfiguration.java @@ -146,7 +146,7 @@ public void render() { int[] keysa = KEYSA[mode]; if (keyinput) { - if (keyinput && input.getKeyBoardInputProcesseor().getLastPressedKey() != -1) { + if (keyinput && input.getKeyBoardInputProcesseor().getLibgdxLastPressedKey() != -1) { setKeyboardKeyAssign(keysa[cursorpos]); // System.out.println(input.getKeyBoardInputProcesseor().getLastPressedKey()); keyinput = false; @@ -235,7 +235,10 @@ public void render() { midiconfig.setKeyAssign(MODE_HINT[mode], true); } - if (input.isControlKeyPressed(ControlKeys.ENTER)) { + // We get newly assigned keycodes via `getLibgdxLastPressedKey`. Since this + // function is incompatible with other keyboard relate functions, we cannot + // use `input.isControlKeyPressed` here. See `getLibgdxLastPressedKey` documentation. + if (input.getKeyBoardInputProcesseor().getLibgdxLastPressedKey() == ControlKeys.ENTER.keycode) { setKeyAssignMode(cursorpos); } @@ -316,7 +319,7 @@ public void render() { } public void setKeyAssignMode(final int index) { - input.getKeyBoardInputProcesseor().setLastPressedKey(-1); + input.getKeyBoardInputProcesseor().setLibgdxLastPresssedKey(-1); input.getKeyBoardInputProcesseor().getMouseScratchInput().setLastMouseScratch(-1); for (BMControllerInputProcessor bmc : controllers) { bmc.setLastPressedButton(-1); @@ -396,16 +399,16 @@ private int getKeyboardKeyAssign(int index) { } private void setKeyboardKeyAssign(int index) { - if (keyboard.isReservedKey(keyboard.getLastPressedKey())) { + if (keyboard.isReservedKey(keyboard.getLibgdxLastPressedKey())) { return; } resetKeyAssign(index); if (index >= 0) { - keyboardConfig.getKeyAssign()[index] = keyboard.getLastPressedKey(); + keyboardConfig.getKeyAssign()[index] = keyboard.getLibgdxLastPressedKey(); } else if (index == -1) { - keyboardConfig.setStart(keyboard.getLastPressedKey()); + keyboardConfig.setStart(keyboard.getLibgdxLastPressedKey()); } else if (index == -2) { - keyboardConfig.setSelect(keyboard.getLastPressedKey()); + keyboardConfig.setSelect(keyboard.getLibgdxLastPressedKey()); } } diff --git a/src/bms/player/beatoraja/input/KeyBoardInputProcesseor.java b/src/bms/player/beatoraja/input/KeyBoardInputProcesseor.java index 48d6a8cac..ef8fc0409 100644 --- a/src/bms/player/beatoraja/input/KeyBoardInputProcesseor.java +++ b/src/bms/player/beatoraja/input/KeyBoardInputProcesseor.java @@ -31,7 +31,7 @@ public class KeyBoardInputProcesseor extends BMSPlayerInputDevice implements Inp /** * 最後に押されたキー */ - private int lastPressedKey = -1; + private int libgdxLastPressedKey = -1; private boolean textmode = false; @@ -77,7 +77,7 @@ public void setConfig(KeyboardConfig config) { } public boolean keyDown(int keycode) { - setLastPressedKey(keycode); + setLibgdxLastPresssedKey(keycode); return true; } @@ -92,7 +92,7 @@ public boolean keyUp(int keycode) { public void clear() { // Arrays.fill(keystate, false); Arrays.fill(keytime, Long.MIN_VALUE); - lastPressedKey = -1; + libgdxLastPressedKey = -1; mouseScratchInput.clear(); } @@ -102,7 +102,8 @@ public void poll(final long microtime) { if(keys[i] < 0) { continue; } - final boolean pressed = Gdx.input.isKeyPressed(keys[i]); + + final boolean pressed = KeyPressedPreferNative.isKeyPressed(keys[i]); if (pressed != keystate[keys[i]] && microtime >= keytime[keys[i]] + duration * 1000) { keystate[keys[i]] = pressed; keytime[keys[i]] = microtime; @@ -111,12 +112,12 @@ public void poll(final long microtime) { } } - final boolean startpressed = Gdx.input.isKeyPressed(control[0]); + final boolean startpressed = KeyPressedPreferNative.isKeyPressed(control[0]); if (startpressed != keystate[control[0]]) { keystate[control[0]] = startpressed; this.bmsPlayerInputProcessor.startChanged(startpressed); } - final boolean selectpressed = Gdx.input.isKeyPressed(control[1]); + final boolean selectpressed = KeyPressedPreferNative.isKeyPressed(control[1]); if (selectpressed != keystate[control[1]]) { keystate[control[1]] = selectpressed; this.bmsPlayerInputProcessor.setSelectPressed(selectpressed); @@ -124,7 +125,7 @@ public void poll(final long microtime) { } for (ControlKeys key : ControlKeys.values()) { - final boolean pressed = Gdx.input.isKeyPressed(key.keycode); + final boolean pressed = KeyPressedPreferNative.isKeyPressed(key.keycode); if (!(textmode && key.text) && pressed != keystate[key.keycode]) { keystate[key.keycode] = pressed; keytime[key.keycode] = microtime; @@ -136,9 +137,9 @@ public void poll(final long microtime) { } private int currentlyHeldModifiers() { - boolean shift = Gdx.input.isKeyPressed(Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Keys.SHIFT_RIGHT); - boolean ctrl = Gdx.input.isKeyPressed(Keys.CONTROL_LEFT) || Gdx.input.isKeyPressed(Keys.CONTROL_RIGHT); - boolean alt = Gdx.input.isKeyPressed(Keys.ALT_LEFT) || Gdx.input.isKeyPressed(Keys.ALT_RIGHT); + boolean shift = KeyPressedPreferNative.isKeyPressed(Keys.SHIFT_LEFT) || KeyPressedPreferNative.isKeyPressed(Keys.SHIFT_RIGHT); + boolean ctrl = KeyPressedPreferNative.isKeyPressed(Keys.CONTROL_LEFT) || KeyPressedPreferNative.isKeyPressed(Keys.CONTROL_RIGHT); + boolean alt = KeyPressedPreferNative.isKeyPressed(Keys.ALT_LEFT) || KeyPressedPreferNative.isKeyPressed(Keys.ALT_RIGHT); return (shift ? MASK_SHIFT : 0) | (ctrl ? MASK_CTRL : 0) | (alt ? MASK_ALT : 0); } @@ -214,12 +215,19 @@ public boolean touchUp(int arg0, int arg1, int arg2, int arg3) { return false; } - public int getLastPressedKey() { - return lastPressedKey; + /** + * Get last key input from libgdx event system. Other functions like 'isKeyPressed' may utilize native + * system APIs for faster key input, so those functions may reflect key state FASTER than this functions. + * Don't mix `getLibgdxLastPressedKey` and other keyboard state functions. + * + * @return Last inputed key, as reported by libgdx event system. + */ + public int getLibgdxLastPressedKey() { + return libgdxLastPressedKey; } - public void setLastPressedKey(int lastPressedKey) { - this.lastPressedKey = lastPressedKey; + public void setLibgdxLastPresssedKey(int lastPressedKey) { + this.libgdxLastPressedKey = lastPressedKey; } public MouseScratchInput getMouseScratchInput() { diff --git a/src/bms/player/beatoraja/input/KeyPressedPreferNative.java b/src/bms/player/beatoraja/input/KeyPressedPreferNative.java new file mode 100644 index 000000000..ca702042a --- /dev/null +++ b/src/bms/player/beatoraja/input/KeyPressedPreferNative.java @@ -0,0 +1,249 @@ +// 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 org.lwjgl.LWJGLUtil; +import org.lwjgl.opengl.Display; + +import java.lang.reflect.Method; + +import static bms.player.beatoraja.input.WinNativeMethods.GetForegroundWindow; +import static bms.player.beatoraja.input.WinNativeMethods.isKeyPressedAsync; +import static bms.player.beatoraja.input.WinVKCode.*; + +/** + * 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. + *

+ * Windows has `User32.GetAsyncKeyState` as a faster alternative, so this library + * wraps around the api for libgdx. + */ +public class KeyPressedPreferNative { + private static long beatorajaHWND = 0; + + /** + * Get HWND pointer value of beatoraja window. + * + * @return + */ + private static long windowsGetBeatorajaHWND() { + if (beatorajaHWND == 0) { + // Reflection hack. + try { + Method getImplementationMethod = Display.class.getDeclaredMethod("getImplementation"); + getImplementationMethod.setAccessible(true); + Object implementation = getImplementationMethod.invoke(null); + Class windowsDisplayClass = Class.forName("org.lwjgl.opengl.WindowsDisplay"); + + if (!windowsDisplayClass.isInstance(implementation)) { + throw new Exception("The current platform must be Windows!"); + } + + Method getHwndMethod = windowsDisplayClass.getDeclaredMethod("getHwnd"); + getHwndMethod.setAccessible(true); + + beatorajaHWND = (long) getHwndMethod.invoke(implementation); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } + return beatorajaHWND; + } + + private static boolean isKeyPressedAsyncVK(WinVKCode vk) { + return isKeyPressedAsync(vk.code); + } + + private static boolean windowsIsKeyPressed(int gdxKey) { + // Note: GetAsyncKeyState checks if the key is pressed regardless of whether the application window + // is in focus or not. This may severely interfere with user usability when beatoraja is NOT + // in foreground. (e.g minimized). We check if the beatoraja is the focused window before + // using GetAsyncKeyState. + long foregroundWindow = GetForegroundWindow(); + if (foregroundWindow != windowsGetBeatorajaHWND()) { + return Gdx.input.isKeyPressed(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 isKeyPressedAsyncVK(VK_OEM_4); + case Input.Keys.RIGHT_BRACKET: + return isKeyPressedAsyncVK(VK_OEM_6); + case Input.Keys.GRAVE: + return isKeyPressedAsyncVK(VK_OEM_8); + case Input.Keys.STAR: + return isKeyPressedAsyncVK(VK_MULTIPLY); + case Input.Keys.NUM: + return isKeyPressedAsyncVK(VK_NUMLOCK); + case Input.Keys.PERIOD: + return isKeyPressedAsyncVK(VK_OEM_PERIOD); + case Input.Keys.SLASH: + return isKeyPressedAsyncVK(VK_OEM_2); + case Input.Keys.SYM: + return isKeyPressedAsyncVK(VK_RWIN); + case Input.Keys.EQUALS: + return isKeyPressedAsyncVK(VK_OEM_PLUS); + case Input.Keys.COMMA: + return isKeyPressedAsyncVK(VK_OEM_COMMA); + case Input.Keys.ENTER: + return isKeyPressedAsyncVK(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 isKeyPressedAsync(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 isKeyPressedAsync(0x41 + gdxKey - Input.Keys.A); + + // Some people *might* want to use control keys for gameplays, so + // unlike F1~F12 keys we just use GetAsyncKeyState here. + case Input.Keys.ALT_LEFT: + return isKeyPressedAsyncVK(VK_LMENU); + case Input.Keys.ALT_RIGHT: + return isKeyPressedAsyncVK(VK_RMENU); + case Input.Keys.BACKSLASH: + return isKeyPressedAsyncVK(VK_OEM_5); + case Input.Keys.FORWARD_DEL: + return isKeyPressedAsyncVK(VK_DELETE); + case Input.Keys.DPAD_LEFT: + return isKeyPressedAsyncVK(VK_LEFT); + case Input.Keys.DPAD_RIGHT: + return isKeyPressedAsyncVK(VK_RIGHT); + case Input.Keys.DPAD_UP: + return isKeyPressedAsyncVK(VK_UP); + case Input.Keys.DPAD_DOWN: + return isKeyPressedAsyncVK(VK_DOWN); + case Input.Keys.HOME: + return isKeyPressedAsyncVK(VK_HOME); + case Input.Keys.MINUS: + return isKeyPressedAsyncVK(VK_SUBTRACT); + case Input.Keys.PLUS: + return isKeyPressedAsyncVK(VK_ADD); + case Input.Keys.SEMICOLON: + case Input.Keys.COLON: + return isKeyPressedAsyncVK(VK_OEM_1); + case Input.Keys.SHIFT_LEFT: + return isKeyPressedAsyncVK(VK_LSHIFT); + case Input.Keys.SHIFT_RIGHT: + return isKeyPressedAsyncVK(VK_RSHIFT); + case Input.Keys.SPACE: + return isKeyPressedAsyncVK(VK_SPACE); + case Input.Keys.TAB: + return isKeyPressedAsyncVK(VK_TAB); + case Input.Keys.CONTROL_LEFT: + return isKeyPressedAsyncVK(VK_LCONTROL); + case Input.Keys.CONTROL_RIGHT: + return isKeyPressedAsyncVK(VK_RCONTROL); + case Input.Keys.PAGE_DOWN: + return isKeyPressedAsyncVK(VK_NEXT); + case Input.Keys.PAGE_UP: + return isKeyPressedAsyncVK(VK_PRIOR); + case Input.Keys.ESCAPE: + return isKeyPressedAsyncVK(VK_ESCAPE); + case Input.Keys.END: + return isKeyPressedAsyncVK(VK_END); + case Input.Keys.INSERT: + return isKeyPressedAsyncVK(VK_INSERT); + case Input.Keys.DEL: + return isKeyPressedAsyncVK(VK_BACK); + case Input.Keys.APOSTROPHE: + return isKeyPressedAsyncVK(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: { + if (isKeyPressedAsync(VK_F1.code + (gdxKey - Input.Keys.F1))) { + // If we just use GetAsyncKeyState for polling F(\d+) keys, + // common keystrokes like Alt+F4 would not work. + // I suspect nobody would use function keys for gaming, so + // some delay in processing keys won't matter that much? + return Gdx.input.isKeyPressed(gdxKey); + } + return false; + } + 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 isKeyPressedAsync(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) { + int platform = LWJGLUtil.getPlatform(); + if (platform == LWJGLUtil.PLATFORM_WINDOWS) { + return windowsIsKeyPressed(gdxKey); + } else { + return Gdx.input.isKeyPressed(gdxKey); + } + } +} diff --git a/src/bms/player/beatoraja/input/WinNativeMethods.java b/src/bms/player/beatoraja/input/WinNativeMethods.java new file mode 100644 index 000000000..c890e5319 --- /dev/null +++ b/src/bms/player/beatoraja/input/WinNativeMethods.java @@ -0,0 +1,66 @@ +package bms.player.beatoraja.input; + +import com.badlogic.gdx.Gdx; + +import java.lang.reflect.Method; + +/** + * lwjgl already has several JNI accessing windows APIs. + * Rather than maintaining our own JNI classes, we can just "use" them. + */ +public class WinNativeMethods { + /// HWND GetForegroundWindow(); + private static Method lwjgl_getForegroundWindow = null; + private static Long foregroundWindowCache = null; + + static long GetForegroundWindow() { + if (lwjgl_getForegroundWindow == null) { + try { + Class windowsKeyboardClass = Class.forName("org.lwjgl.opengl.WindowsDisplay"); + Method getForegroundWindow = windowsKeyboardClass.getDeclaredMethod("getForegroundWindow"); + getForegroundWindow.setAccessible(true); + lwjgl_getForegroundWindow = getForegroundWindow; + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } + + if (foregroundWindowCache == null) { + try { + foregroundWindowCache = (long) lwjgl_getForegroundWindow.invoke(null); + Gdx.app.postRunnable(new Runnable() { + @Override + public void run() { + foregroundWindowCache = null; + } + }); + return foregroundWindowCache; + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } else { + return foregroundWindowCache; + } + } + + /// bool isKeyPressedAsync + + static Method lwjgl_isKeyPressedAsync = null; + public static boolean isKeyPressedAsync(int vKey) { + if (lwjgl_isKeyPressedAsync == null) { + try { + Class windowsKeyboardClass = Class.forName("org.lwjgl.opengl.WindowsKeyboard"); + Method isKeyPressedAsync = windowsKeyboardClass.getDeclaredMethod("isKeyPressedAsync", int.class); + isKeyPressedAsync.setAccessible(true); + lwjgl_isKeyPressedAsync = isKeyPressedAsync; + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } + try { + return (Boolean) lwjgl_isKeyPressedAsync.invoke(null, vKey); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } +} diff --git a/src/bms/player/beatoraja/input/WinVKCode.java b/src/bms/player/beatoraja/input/WinVKCode.java new file mode 100644 index 000000000..744022fd1 --- /dev/null +++ b/src/bms/player/beatoraja/input/WinVKCode.java @@ -0,0 +1,203 @@ +package bms.player.beatoraja.input; + +public enum WinVKCode { + VK_LBUTTON(0x01), + VK_RBUTTON(0x02), + VK_CANCEL(0x03), + VK_MBUTTON(0x04), + VK_XBUTTON1(0x05), + VK_XBUTTON2(0x06), + VK_BACK(0x08), + VK_TAB(0x09), + VK_CLEAR(0x0C), + VK_RETURN(0x0D), + VK_SHIFT(0x10), + VK_CONTROL(0x11), + VK_MENU(0x12), + VK_PAUSE(0x13), + VK_CAPITAL(0x14), + VK_KANA(0x15), + VK_HANGEUL(0x15), + VK_HANGUL(0x15), + VK_IME_ON(0x16), + VK_JUNJA(0x17), + VK_FINAL(0x18), + VK_HANJA(0x19), + VK_KANJI(0x19), + VK_IME_OFF(0x1A), + VK_ESCAPE(0x1B), + VK_CONVERT(0x1C), + VK_NONCONVERT(0x1D), + VK_ACCEPT(0x1E), + VK_MODECHANGE(0x1F), + VK_SPACE(0x20), + VK_PRIOR(0x21), + VK_NEXT(0x22), + VK_END(0x23), + VK_HOME(0x24), + VK_LEFT(0x25), + VK_UP(0x26), + VK_RIGHT(0x27), + VK_DOWN(0x28), + VK_SELECT(0x29), + VK_PRINT(0x2A), + VK_EXECUTE(0x2B), + VK_SNAPSHOT(0x2C), + VK_INSERT(0x2D), + VK_DELETE(0x2E), + VK_HELP(0x2F), + VK_LWIN(0x5B), + VK_RWIN(0x5C), + VK_APPS(0x5D), + VK_SLEEP(0x5F), + VK_NUMPAD0(0x60), + VK_NUMPAD1(0x61), + VK_NUMPAD2(0x62), + VK_NUMPAD3(0x63), + VK_NUMPAD4(0x64), + VK_NUMPAD5(0x65), + VK_NUMPAD6(0x66), + VK_NUMPAD7(0x67), + VK_NUMPAD8(0x68), + VK_NUMPAD9(0x69), + VK_MULTIPLY(0x6A), + VK_ADD(0x6B), + VK_SEPARATOR(0x6C), + VK_SUBTRACT(0x6D), + VK_DECIMAL(0x6E), + VK_DIVIDE(0x6F), + VK_F1(0x70), + VK_F2(0x71), + VK_F3(0x72), + VK_F4(0x73), + VK_F5(0x74), + VK_F6(0x75), + VK_F7(0x76), + VK_F8(0x77), + VK_F9(0x78), + VK_F10(0x79), + VK_F11(0x7A), + VK_F12(0x7B), + VK_F13(0x7C), + VK_F14(0x7D), + VK_F15(0x7E), + VK_F16(0x7F), + VK_F17(0x80), + VK_F18(0x81), + VK_F19(0x82), + VK_F20(0x83), + VK_F21(0x84), + VK_F22(0x85), + VK_F23(0x86), + VK_F24(0x87), + VK_NAVIGATION_VIEW(0x88), + VK_NAVIGATION_MENU(0x89), + VK_NAVIGATION_UP(0x8A), + VK_NAVIGATION_DOWN(0x8B), + VK_NAVIGATION_LEFT(0x8C), + VK_NAVIGATION_RIGHT(0x8D), + VK_NAVIGATION_ACCEPT(0x8E), + VK_NAVIGATION_CANCEL(0x8F), + VK_NUMLOCK(0x90), + VK_SCROLL(0x91), + VK_OEM_NEC_EQUAL(0x92), + VK_OEM_FJ_JISHO(0x92), + VK_OEM_FJ_MASSHOU(0x93), + VK_OEM_FJ_TOUROKU(0x94), + VK_OEM_FJ_LOYA(0x95), + VK_OEM_FJ_ROYA(0x96), + VK_LSHIFT(0xA0), + VK_RSHIFT(0xA1), + VK_LCONTROL(0xA2), + VK_RCONTROL(0xA3), + VK_LMENU(0xA4), + VK_RMENU(0xA5), + VK_BROWSER_BACK(0xA6), + VK_BROWSER_FORWARD(0xA7), + VK_BROWSER_REFRESH(0xA8), + VK_BROWSER_STOP(0xA9), + VK_BROWSER_SEARCH(0xAA), + VK_BROWSER_FAVORITES(0xAB), + VK_BROWSER_HOME(0xAC), + VK_VOLUME_MUTE(0xAD), + VK_VOLUME_DOWN(0xAE), + VK_VOLUME_UP(0xAF), + VK_MEDIA_NEXT_TRACK(0xB0), + VK_MEDIA_PREV_TRACK(0xB1), + VK_MEDIA_STOP(0xB2), + VK_MEDIA_PLAY_PAUSE(0xB3), + VK_LAUNCH_MAIL(0xB4), + VK_LAUNCH_MEDIA_SELECT(0xB5), + VK_LAUNCH_APP1(0xB6), + VK_LAUNCH_APP2(0xB7), + VK_OEM_1(0xBA), + VK_OEM_PLUS(0xBB), + VK_OEM_COMMA(0xBC), + VK_OEM_MINUS(0xBD), + VK_OEM_PERIOD(0xBE), + VK_OEM_2(0xBF), + VK_OEM_3(0xC0), + VK_GAMEPAD_A(0xC3), + VK_GAMEPAD_B(0xC4), + VK_GAMEPAD_X(0xC5), + VK_GAMEPAD_Y(0xC6), + VK_GAMEPAD_RIGHT_SHOULDER(0xC7), + VK_GAMEPAD_LEFT_SHOULDER(0xC8), + VK_GAMEPAD_LEFT_TRIGGER(0xC9), + VK_GAMEPAD_RIGHT_TRIGGER(0xCA), + VK_GAMEPAD_DPAD_UP(0xCB), + VK_GAMEPAD_DPAD_DOWN(0xCC), + VK_GAMEPAD_DPAD_LEFT(0xCD), + VK_GAMEPAD_DPAD_RIGHT(0xCE), + VK_GAMEPAD_MENU(0xCF), + VK_GAMEPAD_VIEW(0xD0), + VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON(0xD1), + VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON(0xD2), + VK_GAMEPAD_LEFT_THUMBSTICK_UP(0xD3), + VK_GAMEPAD_LEFT_THUMBSTICK_DOWN(0xD4), + VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT(0xD5), + VK_GAMEPAD_LEFT_THUMBSTICK_LEFT(0xD6), + VK_GAMEPAD_RIGHT_THUMBSTICK_UP(0xD7), + VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN(0xD8), + VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT(0xD9), + VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT(0xDA), + VK_OEM_4(0xDB), + VK_OEM_5(0xDC), + VK_OEM_6(0xDD), + VK_OEM_7(0xDE), + VK_OEM_8(0xDF), + VK_OEM_AX(0xE1), + VK_OEM_102(0xE2), + VK_ICO_HELP(0xE3), + VK_ICO_00(0xE4), + VK_PROCESSKEY(0xE5), + VK_ICO_CLEAR(0xE6), + VK_PACKET(0xE7), + VK_OEM_RESET(0xE9), + VK_OEM_JUMP(0xEA), + VK_OEM_PA1(0xEB), + VK_OEM_PA2(0xEC), + VK_OEM_PA3(0xED), + VK_OEM_WSCTRL(0xEE), + VK_OEM_CUSEL(0xEF), + VK_OEM_ATTN(0xF0), + VK_OEM_FINISH(0xF1), + VK_OEM_COPY(0xF2), + VK_OEM_AUTO(0xF3), + VK_OEM_ENLW(0xF4), + VK_OEM_BACKTAB(0xF5), + VK_ATTN(0xF6), + VK_CRSEL(0xF7), + VK_EXSEL(0xF8), + VK_EREOF(0xF9), + VK_PLAY(0xFA), + VK_ZOOM(0xFB), + VK_NONAME(0xFC), + VK_PA1(0xFD), + VK_OEM_CLEAR(0xFE); + + final int code; + WinVKCode(int code) { + this.code = code; + } +}