Skip to content

Commit

Permalink
Delayed key release fix (#52)
Browse files Browse the repository at this point in the history
* Fix handling of out-of-order char events when the key is released after being repeated.

Closes #48

* Forward org.lwjgl.input.Mouse setGrabbed calls to lwjglx for better debugger support
  • Loading branch information
eigenraven authored Mar 26, 2023
1 parent 090d31b commit 7186edf
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 21 deletions.
4 changes: 2 additions & 2 deletions src/generated/java/org/lwjgl/input/Mouse.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public static boolean isCreated() {
}

public static boolean isGrabbed() {
throw new UnsupportedOperationException();
return org.lwjglx.input.Mouse.isGrabbed();
}

public static boolean isInsideWindow() {
Expand All @@ -121,7 +121,7 @@ public static void setCursorPosition(int arg0, int arg1) {
}

public static void setGrabbed(boolean arg0) {
throw new UnsupportedOperationException();
org.lwjglx.input.Mouse.setGrabbed(arg0);
}

public static org.lwjgl.input.Cursor setNativeCursor(org.lwjgl.input.Cursor arg0) {
Expand Down
54 changes: 36 additions & 18 deletions src/main/java/org/lwjglx/input/Keyboard.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package org.lwjglx.input;

import java.awt.event.KeyEvent;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

import me.eigenraven.lwjgl3ify.Lwjgl3ify;
import me.eigenraven.lwjgl3ify.core.Config;

import org.apache.commons.lang3.StringUtils;
import org.lwjgl.glfw.GLFW;
import org.lwjglx.LWJGLException;
Expand Down Expand Up @@ -209,31 +213,38 @@ public enum KeyState {
eventQueue.add(new KeyEvent(0, '\0', KeyState.RELEASE, Sys.getNanoTime()));
}

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;
case GLFW.GLFW_RELEASE -> state = KeyState.RELEASE;
case GLFW.GLFW_REPEAT -> {
state = KeyState.REPEAT;
if (!doRepeatEvents) {
return;
}
}
default -> state = KeyState.RELEASE;
public static void addRawKeyEvent(KeyEvent event) {
if (event == null || (event.state == KeyState.REPEAT && !doRepeatEvents)) {
return;
}
if (Config.DEBUG_PRINT_KEY_EVENTS) {
Lwjgl3ify.LOG.info(
"[DEBUG-KEY-QUEUE] queued key event key:{} action:{} state:{} charname:{} naive-char:{}",
event.key,
event.state,
event.state,
java.awt.event.KeyEvent.getKeyText(KeyCodes.lwjglToAwt(KeyCodes.glfwToLwjgl(event.key))),
(event.key >= 32 && event.key < 127) ? ((char) event.key) : '?');
}
try {
eventQueue.add(new KeyEvent(KeyCodes.glfwToLwjgl(key), c, state, Sys.getNanoTime()));
eventQueue.add(event);
} catch (IllegalStateException ignored) {}
}

public static void addKeyEvent(int key, boolean pressed) {
try {
eventQueue.add(new KeyEvent(key, '\0', pressed ? KeyState.PRESS : KeyState.RELEASE, Sys.getNanoTime()));
} catch (IllegalStateException ignored) {}
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 -> KeyState.PRESS;
case GLFW.GLFW_RELEASE -> KeyState.RELEASE;
case GLFW.GLFW_REPEAT -> KeyState.REPEAT;
default -> KeyState.RELEASE;
};
addRawKeyEvent(new KeyEvent(KeyCodes.glfwToLwjgl(key), c, state, Sys.getNanoTime()));
}

public static void addCharEvent(int key, char c) {
if (Config.DEBUG_PRINT_KEY_EVENTS) {
Lwjgl3ify.LOG.info("[DEBUG-KEY-QUEUE] queued char virtual keypress codepoint:{} char:{}", (int) c, c);
}
try {
eventQueue.add(new KeyEvent(KEY_NONE, c, KeyState.PRESS, Sys.getNanoTime()));
} catch (IllegalStateException ignored) {}
Expand Down Expand Up @@ -318,18 +329,25 @@ public static boolean isCreated() {

public static void destroy() {}

public static class KeyEvent {
public static final class KeyEvent {

public int key;
public char aChar;
public KeyState state;
public long nano;
public boolean queueOutOfOrderRelease = false;

public KeyEvent(int key, char aChar, KeyState state, long nano) {
this.key = key;
this.aChar = aChar;
this.state = state;
this.nano = nano;
}

public KeyEvent copy() {
final KeyEvent ev = new KeyEvent(key, aChar, state, nano);
ev.queueOutOfOrderRelease = this.queueOutOfOrderRelease;
return ev;
}
}
}
10 changes: 9 additions & 1 deletion src/main/java/org/lwjglx/opengl/Display.java
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ public void invoke(long window, int key, int scancode, int action, int mods) {
action > 1 ? Keyboard.KeyState.REPEAT : Keyboard.KeyState.PRESS,
Sys.getNanoTime());
} else { // Release event
if (ingredientKeyEvent != null && ingredientKeyEvent.key == KeyCodes.glfwToLwjgl(key)) {
ingredientKeyEvent.queueOutOfOrderRelease = true;
}
Keyboard.addGlfwKeyEvent(window, key, scancode, action, mods, '\0');
}
} else { // Other key with no char event associated
Expand Down Expand Up @@ -209,7 +212,12 @@ public void invoke(long window, int codepoint) {
cancelNextChar = false;
} else if (ingredientKeyEvent != null) {
ingredientKeyEvent.aChar = (char) codepoint; // Send char with ASCII key event here
Keyboard.eventQueue.add(ingredientKeyEvent);
Keyboard.addRawKeyEvent(ingredientKeyEvent);
if (ingredientKeyEvent.queueOutOfOrderRelease) {
ingredientKeyEvent = ingredientKeyEvent.copy();
ingredientKeyEvent.state = Keyboard.KeyState.RELEASE;
Keyboard.addRawKeyEvent(ingredientKeyEvent);
}
ingredientKeyEvent = null;
} else {
Keyboard.addCharEvent(0, (char) codepoint); // Non-ASCII chars
Expand Down

0 comments on commit 7186edf

Please sign in to comment.