diff --git a/src/main/java/me/eigenraven/lwjgl3ify/core/Config.java b/src/main/java/me/eigenraven/lwjgl3ify/core/Config.java index c256bb19..662d252c 100644 --- a/src/main/java/me/eigenraven/lwjgl3ify/core/Config.java +++ b/src/main/java/me/eigenraven/lwjgl3ify/core/Config.java @@ -49,11 +49,6 @@ public class Config { public static boolean DEBUG_PRINT_KEY_EVENTS = false; public static boolean DEBUG_PRINT_MOUSE_EVENTS = false; - public static boolean MBE_ENABLED = true; - public static boolean IME_ENABLED = false; - public static boolean IME_F12_TOGGLE = false; - public static boolean IME_SYS_TOGGLE = false; - public static boolean SHOW_JAVA_VERSION = true; public static boolean SHOW_LWJGL_VERSION = true; @@ -137,17 +132,6 @@ public static void reloadConfigObject() { DEBUG_PRINT_MOUSE_EVENTS, "Print mouse-related events to the log"); - MBE_ENABLED = config - .getBoolean("handleMultibyteInput", CATEGORY_CORE, MBE_ENABLED, "Enables multibyte character input."); - IME_ENABLED = config.getBoolean("enabled", CATEGORY_IME, IME_ENABLED, "Enables IME support for CJK input."); - IME_F12_TOGGLE = config - .getBoolean("f12Toggle", CATEGORY_IME, IME_F12_TOGGLE, "Enables switching IME mode by pressing F12."); - IME_SYS_TOGGLE = config.getBoolean( - "sysToggle", - CATEGORY_IME, - IME_SYS_TOGGLE, - "Enables switching IME mode by pressing Ctrl/LWin + Shift/Space"); - SHOW_JAVA_VERSION = config .getBoolean("showJavaVersion", CATEGORY_CORE, SHOW_JAVA_VERSION, "Show java version in the debug hud"); SHOW_LWJGL_VERSION = config.getBoolean( diff --git a/src/main/java/org/lwjglx/input/Keyboard.java b/src/main/java/org/lwjglx/input/Keyboard.java index bb1df21b..68bbcb9e 100644 --- a/src/main/java/org/lwjglx/input/Keyboard.java +++ b/src/main/java/org/lwjglx/input/Keyboard.java @@ -2,13 +2,10 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; - -import me.eigenraven.lwjgl3ify.core.Config; import org.apache.commons.lang3.StringUtils; import org.lwjgl.glfw.GLFW; @@ -163,10 +160,7 @@ public class Keyboard { public static final int keyCount; - private static EventQueue queue = new EventQueue(128); - private static BlockingQueue imeCharQueue = new ArrayBlockingQueue<>(128); - - private enum KeyState { + public enum KeyState { PRESS(true), RELEASE(false), @@ -181,19 +175,8 @@ private enum KeyState { private static boolean doRepeatEvents = true; - private static int[] keyEvents = new int[queue.getMaxEvents()]; - private static char[] keySpecificChars = new char[queue.getMaxEvents()]; - private static KeyState[] keyEventStates = new KeyState[queue.getMaxEvents()]; - - static { - Arrays.fill(keyEventStates, KeyState.RELEASE); - } - - private static long[] nanoTimeEvents = new long[queue.getMaxEvents()]; - private static char[] keyEventChars = new char[Short.MAX_VALUE]; - public static final int KEYBOARD_SIZE = Short.MAX_VALUE; - + public static Queue eventQueue = new ArrayBlockingQueue<>(256); private static final String[] keyName = new String[Short.MAX_VALUE]; private static final Map keyMap = new HashMap<>(Short.MAX_VALUE); @@ -223,13 +206,10 @@ private enum KeyState { } keyMap.put(keyName[i], i); } - for (int key = 32; key < 128; key++) { - keyEventChars[KeyCodes.glfwToLwjgl(key)] = (char) key; - } - keyEventChars[KEY_NONE] = '\0'; + eventQueue.add(new KeyEvent(0, '\0', KeyState.RELEASE, Sys.getNanoTime())); } - public static void addGlfwKeyEvent(long window, int key, int scancode, int action, int mods) { + public static void addGlfwKeyEvent(long window, int key, int scancode, int action, int mods, char c) { final KeyState state; switch (action) { case GLFW.GLFW_PRESS -> state = KeyState.PRESS; @@ -242,52 +222,21 @@ public static void addGlfwKeyEvent(long window, int key, int scancode, int actio } default -> state = KeyState.RELEASE; } - final int nextPos = queue.getNextPos(); - keyEvents[nextPos] = KeyCodes.glfwToLwjgl(key); - keyEventStates[nextPos] = state; - nanoTimeEvents[nextPos] = Sys.getNanoTime(); - keySpecificChars[nextPos] = '\0'; - - queue.add(); + try { + eventQueue.add(new KeyEvent(KeyCodes.glfwToLwjgl(key), c, state, Sys.getNanoTime())); + } catch (IllegalStateException ignored) {} } public static void addKeyEvent(int key, boolean pressed) { - final int nextPos = queue.getNextPos(); - keyEvents[nextPos] = KeyCodes.glfwToLwjgl(key); - keyEventStates[nextPos] = pressed ? KeyState.PRESS : KeyState.RELEASE; - nanoTimeEvents[nextPos] = Sys.getNanoTime(); - keySpecificChars[nextPos] = '\0'; - - queue.add(); - } - - private static void duplicateKeyEvent() { - final int nextPos = queue.getNextPos(); - final int prevPos = queue.getLastWrittenPos(); - keyEvents[nextPos] = keyEvents[prevPos]; - keyEventStates[nextPos] = keyEventStates[prevPos]; - nanoTimeEvents[nextPos] = nanoTimeEvents[prevPos]; - keySpecificChars[nextPos] = keySpecificChars[prevPos]; - queue.add(); + try { + eventQueue.add(new KeyEvent(key, '\0', pressed ? KeyState.PRESS : KeyState.RELEASE, Sys.getNanoTime())); + } catch (IllegalStateException ignored) {} } public static void addCharEvent(int key, char c) { - final int nextPos = queue.getNextPos(); - final int prevPos = queue.getLastWrittenPos(); - int index = KeyCodes.glfwToLwjgl(key); - keyEventChars[index] = c; - if (Config.MBE_ENABLED) { - if (keySpecificChars[prevPos] == '\0') { - keySpecificChars[prevPos] = c; - } else { - duplicateKeyEvent(); - keySpecificChars[nextPos] = c; - } - } - } - - public static void addIMECharEvent(char c) { - imeCharQueue.offer(c); + try { + eventQueue.add(new KeyEvent(KEY_NONE, c, KeyState.PRESS, Sys.getNanoTime())); + } catch (IllegalStateException ignored) {} } public static void create() throws LWJGLException {} @@ -313,41 +262,36 @@ public static int getKeyCount() { } public static int getNumKeyboardEvents() { - return queue.getEventCount(); + return eventQueue.size(); } public static boolean isRepeatEvent() { - return keyEventStates[queue.getCurrentPos()] == KeyState.REPEAT; + return eventQueue.peek().state == KeyState.REPEAT; } public static boolean next() { - return queue.next(); + boolean next = eventQueue.size() > 1; + if (next) { + eventQueue.remove(); + } + return next; } public static int getEventKey() { - return keyEvents[queue.getCurrentPos()]; + return eventQueue.peek().key; } public static char getEventCharacter() { - if (!imeCharQueue.isEmpty() && Display.imeOn) { - return imeCharQueue.remove(); - } - final int eventKey = getEventKey(); - final char eventSpecificChar = keySpecificChars[queue.getCurrentPos()]; - // On some systems it seems esc and backspace can generate broken chars sometimes, make sure they always work - return switch (eventKey) { - case KEY_ESCAPE -> '\0'; - case KEY_BACK -> '\b'; - default -> (eventSpecificChar != '\0') ? eventSpecificChar : keyEventChars[eventKey]; - }; + return eventQueue.peek().aChar; + } public static boolean getEventKeyState() { - return keyEventStates[queue.getCurrentPos()].isPressed || (!imeCharQueue.isEmpty() && Display.imeOn); + return eventQueue.peek().state.isPressed; } public static long getEventNanoseconds() { - return nanoTimeEvents[queue.getCurrentPos()]; + return eventQueue.peek().nano; } public static String getKeyName(int key) { @@ -373,4 +317,19 @@ public static boolean isCreated() { } public static void destroy() {} + + public static class KeyEvent { + + public int key; + public char aChar; + public KeyState state; + public long nano; + + public KeyEvent(int key, char aChar, KeyState state, long nano) { + this.key = key; + this.aChar = aChar; + this.state = state; + this.nano = nano; + } + } } diff --git a/src/main/java/org/lwjglx/opengl/Display.java b/src/main/java/org/lwjglx/opengl/Display.java index 305c563c..fbf327eb 100644 --- a/src/main/java/org/lwjglx/opengl/Display.java +++ b/src/main/java/org/lwjglx/opengl/Display.java @@ -57,7 +57,8 @@ public class Display { private static boolean latestResized = false; private static int latestWidth = 0; private static int latestHeight = 0; - public static boolean imeOn = false; + private static boolean cancelNextChar = false; + private static Keyboard.KeyEvent ingredientKeyEvent; private static ByteBuffer[] savedIcons; static { @@ -155,7 +156,7 @@ public static void create() { public void invoke(long window, int key, int scancode, int action, int mods) { if (Config.DEBUG_PRINT_KEY_EVENTS) { Lwjgl3ify.LOG.info( - "[DEBUG-KEY] key window:{} key:{} scancode:{} action:{} mods:{} charname:{} char:{}", + "[DEBUG-KEY] key window:{} key:{} scancode:{} action:{} mods:{} charname:{} naive-char:{}", window, key, scancode, @@ -164,33 +165,30 @@ public void invoke(long window, int key, int scancode, int action, int mods) { KeyEvent.getKeyText(KeyCodes.lwjglToAwt(KeyCodes.glfwToLwjgl(key))), (key >= 32 && key < 127) ? ((char) key) : '?'); } - latestEventKey = key; - if (Config.IME_ENABLED) { - if (Config.IME_F12_TOGGLE && key == GLFW_KEY_F12 && action == 0) { - imeOn = !imeOn; + cancelNextChar = false; + if (key > GLFW_KEY_SPACE && key <= GLFW_KEY_GRAVE_ACCENT) { // Handle keys have a char. Exclude space to + // avoid extra input when switching IME + if ((GLFW_MOD_CONTROL & mods) != 0) { // Handle ctrl + x/c/v. + Keyboard.addGlfwKeyEvent(window, key, scancode, action, mods, (char) (key & 0x1f)); + cancelNextChar = true; // Cancel char event from ctrl key since its already handled here + } else if (action > 0) { // Delay press and repeat key event to actual char input. There is ALWAYS a + // char after them + ingredientKeyEvent = new Keyboard.KeyEvent( + KeyCodes.glfwToLwjgl(key), + '\0', + action > 1 ? Keyboard.KeyState.REPEAT : Keyboard.KeyState.PRESS, + Sys.getNanoTime()); + } else { // Release event + Keyboard.addGlfwKeyEvent(window, key, scancode, action, mods, '\0'); } - if (Config.IME_SYS_TOGGLE - && (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL) - || Keyboard.isKeyDown(Keyboard.KEY_LWIN))) { - if (key == GLFW_KEY_SPACE || key == GLFW_KEY_LEFT_SHIFT || key == GLFW_KEY_RIGHT_SHIFT) { - imeOn = !imeOn; - return; - } - } - if (imeOn) { - if (key > 256) { - Keyboard.addGlfwKeyEvent(window, key, scancode, action, mods); - } - } else { - Keyboard.addGlfwKeyEvent(window, key, scancode, action, mods); - } - } else { - Keyboard.addGlfwKeyEvent(window, key, scancode, action, mods); - } - // Ctrl should generate ASCII modifier keys (0x01-0x1F), glfw does not give use char events for this - if ((mods & GLFW_MOD_CONTROL) != 0 && key >= GLFW_KEY_A && key <= GLFW_KEY_Z) { - int codepoint = key & 0x1F; - Keyboard.addCharEvent(key, (char) codepoint); + } else { // Other key with no char event associated + char mappedChar = switch (key) { + case GLFW_KEY_ENTER -> 0x0D; + case GLFW_KEY_ESCAPE -> 0x1B; + case GLFW_KEY_TAB -> 0x09; + default -> '\0'; + }; + Keyboard.addGlfwKeyEvent(window, key, scancode, action, mods, mappedChar); } } }; @@ -206,13 +204,14 @@ public void invoke(long window, int codepoint) { codepoint, (char) codepoint); } - if (imeOn) { - Keyboard.addKeyEvent(GLFW_KEY_UNKNOWN, true); - Keyboard.addIMECharEvent((char) codepoint); - Keyboard.addKeyEvent(GLFW_KEY_UNKNOWN, false); - + if (cancelNextChar) { // Char event being cancelled + cancelNextChar = false; + } else if (ingredientKeyEvent != null) { + ingredientKeyEvent.aChar = (char) codepoint; // Send char with ASCII key event here + Keyboard.eventQueue.add(ingredientKeyEvent); + ingredientKeyEvent = null; } else { - Keyboard.addCharEvent(latestEventKey, (char) codepoint); + Keyboard.addCharEvent(0, (char) codepoint); // Non-ASCII chars } } };