diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLKeyboard.java b/src/java.desktop/unix/classes/sun/awt/wl/WLKeyboard.java index 4af55511ba99..809a396115a7 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLKeyboard.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLKeyboard.java @@ -38,13 +38,16 @@ class WLKeyboard { } private class KeyRepeatManager { + // Methods and fields should only be accessed from the EDT private final Timer timer = new Timer("WLKeyboard.KeyRepeatManager", true); private TimerTask currentRepeatTask; private int delayBeforeRepeatMillis; private int delayBetweenRepeatMillis; + // called from native code void setRepeatInfo(int charsPerSecond, int delayMillis) { // this function receives (0, 0) when key repeat is disabled + assert EventQueue.isDispatchThread(); this.delayBeforeRepeatMillis = delayMillis; if (charsPerSecond > 0) { this.delayBetweenRepeatMillis = (int) (1000.0 / charsPerSecond); @@ -59,26 +62,33 @@ class WLKeyboard { return this.delayBeforeRepeatMillis > 0 || this.delayBetweenRepeatMillis > 0; } + // called from native code void cancelRepeat() { + assert EventQueue.isDispatchThread(); if (currentRepeatTask != null) { currentRepeatTask.cancel(); currentRepeatTask = null; } } + // called from native code void startRepeat(long timestamp, int keycode) { + assert EventQueue.isDispatchThread(); cancelRepeat(); if (keycode == 0 || !isRepeatEnabled()) { return; } long delta = timestamp - System.currentTimeMillis(); + currentRepeatTask = new TimerTask() { @Override public void run() { try { EventQueue.invokeAndWait(() -> { - handleKeyPress(delta + System.currentTimeMillis(), keycode, true); + if (this == currentRepeatTask) { + handleKeyPress(delta + System.currentTimeMillis(), keycode, true); + } }); } catch (InterruptedException ignored) { } catch (InvocationTargetException e) { @@ -136,6 +146,7 @@ class WLKeyboard { } public void onLostFocus() { + assert EventQueue.isDispatchThread(); keyRepeatManager.cancelRepeat(); cancelCompose(); } diff --git a/src/java.desktop/unix/native/libawt_wlawt/WLKeyboard.c b/src/java.desktop/unix/native/libawt_wlawt/WLKeyboard.c index d91ef2267acd..5f0d06541baf 100644 --- a/src/java.desktop/unix/native/libawt_wlawt/WLKeyboard.c +++ b/src/java.desktop/unix/native/libawt_wlawt/WLKeyboard.c @@ -1204,6 +1204,9 @@ getJavaKeyCharForKeycode(xkb_keycode_t xkbKeycode) { // Posts an XKB keysym as KEY_TYPED events, without consulting the current compose state. static void handleKeyTypeNoCompose(long timestamp, xkb_keycode_t xkbKeycode) { +#ifdef WL_KEYBOARD_DEBUG + fprintf(stderr, "handleKeyTypeNoCompose: xkbKeycode = %d\n", xkbKeycode); +#endif int bufSize = xkb.state_key_get_utf8(keyboard.state, xkbKeycode, NULL, 0) + 1; char buf[bufSize]; xkb.state_key_get_utf8(keyboard.state, xkbKeycode, buf, bufSize); @@ -1213,8 +1216,17 @@ handleKeyTypeNoCompose(long timestamp, xkb_keycode_t xkbKeycode) { // Handles generating KEY_TYPED events for an XKB keysym, translating it using the active compose state static void handleKeyType(long timestamp, xkb_keycode_t xkbKeycode) { +#ifdef WL_KEYBOARD_DEBUG + fprintf(stderr, "handleKeyType(xkbKeycode = %d)\n", xkbKeycode); +#endif xkb_keysym_t keysym = xkb.state_key_get_one_sym(keyboard.state, xkbKeycode); +#ifdef WL_KEYBOARD_DEBUG + char buf[256]; + xkb.keysym_get_name(keysym, buf, sizeof buf); + fprintf(stderr, "handleKeyType: keysym = %d (%s)\n", keysym, buf); +#endif + if (!keyboard.composeState || (xkb.compose_state_feed(keyboard.composeState, keysym) == XKB_COMPOSE_FEED_IGNORED)) { handleKeyTypeNoCompose(timestamp, xkbKeycode); @@ -1250,12 +1262,24 @@ handleKeyType(long timestamp, xkb_keycode_t xkbKeycode) { // 2. From the key repeat manager. In this case, isRepeat = true and isPressed = true. static void handleKey(long timestamp, uint32_t keycode, bool isPressed, bool isRepeat) { +#ifdef WL_KEYBOARD_DEBUG + fprintf(stderr, "handleKey(keycode = %d, isPressed = %d, isRepeat = %d)\n", keycode, isPressed, isRepeat); +#endif JNIEnv *env = getEnv(); xkb_keycode_t xkbKeycode = keycode + 8; + bool keyRepeats = xkb.keymap_key_repeats(keyboard.keymap, xkbKeycode); xkb_keysym_t keysym = translateKeycodeToKeysym(keycode, TRANSLATE_USING_ACTIVE_LAYOUT); xkb_keysym_t qwertyKeysym = translateKeycodeToKeysym(keycode, TRANSLATE_USING_QWERTY); +#ifdef WL_KEYBOARD_DEBUG + char buf[256]; + xkb.keysym_get_name(keysym, buf, sizeof buf); + fprintf(stderr, "handleKey: keysym = %d (%s)\n", keysym, buf); + xkb.keysym_get_name(qwertyKeysym, buf, sizeof buf); + fprintf(stderr, "handleKey: qwertyKeysym = %d (%s)\n", qwertyKeysym, buf); +#endif + int javaKeyCode, javaExtendedKeyCode, javaKeyLocation; // If the national layouts support is enabled, and the current keyboard is not ascii-capable, @@ -1288,6 +1312,10 @@ handleKey(long timestamp, uint32_t keycode, bool isPressed, bool isRepeat) { } } +#ifdef WL_KEYBOARD_DEBUG + fprintf(stderr, "handleKey: javaKeyCode = %d\n", javaKeyCode); +#endif + struct WLKeyEvent event = { .timestamp = timestamp, .id = isPressed ? java_awt_event_KeyEvent_KEY_PRESSED : java_awt_event_KeyEvent_KEY_RELEASED, @@ -1303,11 +1331,11 @@ handleKey(long timestamp, uint32_t keycode, bool isPressed, bool isRepeat) { if (isPressed) { handleKeyType(timestamp, xkbKeycode); - if (!isRepeat && xkb.keymap_key_repeats(keyboard.keymap, xkbKeycode)) { + if (!isRepeat && keyRepeats) { (*env)->CallVoidMethod(env, keyboard.keyRepeatManager, startRepeatMID, timestamp, keycode); JNU_CHECK_EXCEPTION(env); } - } else { + } else if (keyRepeats) { (*env)->CallVoidMethod(env, keyboard.keyRepeatManager, cancelRepeatMID); JNU_CHECK_EXCEPTION(env); }