diff options
author | Sven Gothel <[email protected]> | 2013-01-29 21:27:57 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2013-01-29 21:27:57 +0100 |
commit | 90e136b65a10d8daf8c3a2df6cc193e55a63722c (patch) | |
tree | 863f8a3679a70065b031aaf83bd7cc6053fd69ab /src | |
parent | 2f63a43fd6ff9964251c43e248c51bc821f3ecbd (diff) |
Fix Bug 678: Deliver key-char value for printable chars on all KeyEventListener (-> On Windows as well)
The following is observed, where t0 and t1 refer to subsequent different timestamps:
NEWT delivery order:
PRESSED (t0), RELEASED (t1) and TYPED (t1)
WINDOWS delivery order:
PRESSED (t0), TYPED (t0) and RELEASED (t1)
Windows Auto-Repeat:
PRESSED (t0), TYPED (t0)
Hence we changed the event reorder-code in NEWT to trigger NEWT-PRESSED on
Windows-TYPED for printable chars, assuring key-char values on all listener callbacks.
- KeyEvent.getKeyChar(): Removed disclaimer dedicated for Windows
- Keyevent.isActionKey(): Completed for all NEWT non-printable action keys; Added static variant
- Keyevent.isPrintableKey(): NEW: returns !isModifierKey(keyCode) && !isActionKey(keyCode) ; With static variant
- Windows WindowDriver:
- EVENT_KEY_PRESSED handles non-printable chars only
- EVENT_KEY_TYPE handles printable chars only
- Native: VK_DELETE passes keyCode
- Unit tests: Wait for completion 1s -> 2s
Diffstat (limited to 'src')
6 files changed, 143 insertions, 71 deletions
diff --git a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java index 289aa31f6..ff67b7f57 100644 --- a/src/newt/classes/com/jogamp/newt/event/KeyEvent.java +++ b/src/newt/classes/com/jogamp/newt/event/KeyEvent.java @@ -74,12 +74,7 @@ public class KeyEvent extends InputEvent } /** - * Returns the character matching the {@link #getKeyCode() virtual key code}, if exist. - * <p> - * <b>Disclaimer</b>: Only valid on all platforms at {@link KeyListener#keyTyped(KeyEvent)}. - * Precisely, on the Windows platform we currently cannot deliver the proper character - * in case of shifted keys where no uppercase exists, e.g. 'shift + 1' doesn't produce '!'. - * </p> + * Returns the character matching the {@link #getKeyCode() virtual key code}. */ public char getKeyChar() { return keyChar; @@ -111,7 +106,12 @@ public class KeyEvent extends InputEvent } } - /** Returns true if <code>keyCode</code> represents a modifier key, i.e. one of {@link #VK_SHIFT}, {@link #VK_CONTROL}, {@link #VK_ALT}, {@link #VK_ALT_GRAPH}, {@link #VK_META}. */ + /** + * Returns true if the given <code>keyCode</code> represents a non-printable modifier key. + * <p> + * A modifier key is one of {@link #VK_SHIFT}, {@link #VK_CONTROL}, {@link #VK_ALT}, {@link #VK_ALT_GRAPH}, {@link #VK_META}. + * </p> + */ public static boolean isModifierKey(int keyCode) { switch (keyCode) { case VK_SHIFT: @@ -125,58 +125,116 @@ public class KeyEvent extends InputEvent } } - /** Returns true if {@link #getKeyCode()} represents a modifier key, i.e. one of {@link #VK_SHIFT}, {@link #VK_CONTROL}, {@link #VK_ALT}, {@link #VK_ALT_GRAPH}, {@link #VK_META}. */ + /** + * Returns true if {@link #getKeyCode()} represents a non-printable modifier key. + * <p> + * See {@link #isModifierKey(int)} for details. + * </p> + */ public boolean isModifierKey() { return isModifierKey(keyCode); } - - public boolean isActionKey() { + + /** + * Returns true if the given <code>keyCode</code> represents a non-printable action key, which is not a {@link #isModifierKey(int) modifier key}. + * <p> + * An action key is one of {@link #VK_HOME}, {@link #VK_END}, {@link #VK_PAGE_UP}, {@link #VK_PAGE_DOWN}, {@link #VK_UP}, {@link #VK_PAGE_DOWN}, + * {@link #VK_LEFT}, {@link #VK_RIGHT}, {@link #VK_F1}-{@link #VK_F24}, {@link #VK_PRINTSCREEN}, {@link #VK_CAPS_LOCK}, {@link #VK_PAUSE}, + * {@link #VK_INSERT}, {@link #VK_HELP}, {@link #VK_WINDOWS}, etc ... + * </p> + */ + public static boolean isActionKey(int keyCode) { + if( ( VK_F1 <= keyCode && keyCode <= VK_F24 ) || + ( VK_ALL_CANDIDATES <= keyCode && keyCode <= VK_INPUT_METHOD_ON_OFF ) || + ( VK_CUT <= keyCode && keyCode <= VK_STOP ) ) { + return true; + } + switch (keyCode) { - case VK_HOME: - case VK_END: + case VK_CANCEL: + case VK_CLEAR: + case VK_PAUSE: + case VK_CAPS_LOCK: + case VK_ESCAPE: case VK_PAGE_UP: case VK_PAGE_DOWN: - case VK_UP: - case VK_DOWN: + case VK_END: + case VK_HOME: case VK_LEFT: + case VK_UP: case VK_RIGHT: - - case VK_F1: - case VK_F2: - case VK_F3: - case VK_F4: - case VK_F5: - case VK_F6: - case VK_F7: - case VK_F8: - case VK_F9: - case VK_F10: - case VK_F11: - case VK_F12: - case VK_F13: - case VK_F14: - case VK_F15: - case VK_F16: - case VK_F17: - case VK_F18: - case VK_F19: - case VK_F20: - case VK_F21: - case VK_F22: - case VK_F23: - case VK_F24: + case VK_DOWN: + case VK_DELETE: + case VK_NUM_LOCK: + case VK_SCROLL_LOCK: + case VK_PRINTSCREEN: - case VK_CAPS_LOCK: - case VK_PAUSE: case VK_INSERT: - case VK_HELP: + case VK_META: + case VK_KP_UP: + case VK_KP_DOWN: + case VK_KP_LEFT: + case VK_KP_RIGHT: + + case VK_DEAD_VOICED_SOUND: + case VK_DEAD_SEMIVOICED_SOUND: + case VK_WINDOWS: + case VK_CONTEXT_MENU: + case VK_FINAL: + + case VK_CONVERT: + case VK_NONCONVERT: + case VK_ACCEPT: + case VK_MODECHANGE: + + case VK_KANA: + case VK_KANJI: + + case VK_ALPHANUMERIC: + case VK_KATAKANA: + case VK_HIRAGANA: + case VK_FULL_WIDTH: + case VK_HALF_WIDTH: + case VK_ROMAN_CHARACTERS: + + case VK_COMPOSE: + case VK_BEGIN: + return true; } return false; } + + /** + * Returns true if {@link #getKeyCode() keyCode} represents a non-printable action key, which is not a {@link #isModifierKey(int) modifier key}. + * <p> + * See {@link #isActionKey(int)} for details. + * </p> + */ + public boolean isActionKey() { + return isActionKey(keyCode); + } + + /** + * Returns true if given <code>keyKode</code> represents a printable character, which is neither a {@link #isModifierKey(int) modifier key} + * nor an {@link #isActionKey(int) action key}. + * Otherwise returns <code>false</code>. + */ + public static boolean isPrintableKey(int keyCode) { + return !isModifierKey(keyCode) && !isActionKey(keyCode); + } + /** + * Returns true if {@link #getKeyCode() keyCode} represents a printable character, which is neither a {@link #isModifierKey(int) modifier key} + * nor an {@link #isActionKey(int) action key}. + * Otherwise returns <code>false</code>. + */ + public boolean isPrintableKey() { + return isPrintableKey(keyCode); + } + private final int keyCode; private final char keyChar; diff --git a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java index b2e518f45..650958f25 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/WindowDriver.java @@ -273,53 +273,67 @@ public class WindowDriver extends WindowImpl { private IntIntHashMap typedKeyCode2KeyChar = new IntIntHashMap(KeyEvent.VK_CONTEXT_MENU+1); private final void handleKeyEvent(boolean send, boolean wait, int eventType, int modifiers, int keyCode, char keyChar) { - final boolean isModifierKeyCode = KeyEvent.isModifierKey(keyCode); - // System.err.println("*** handleKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+", keyCode "+toHexString(keyCode)+", keyChar <"+keyChar+">, mods "+toHexString(modifiers)+", was: pressed "+isKeyPressed(keyCode)+", repeat "+isKeyInAutoRepeat(keyCode)+", isModifierKeyCode "+isModifierKeyCode); + final boolean isPrintableKey = KeyEvent.isPrintableKey(keyCode); + final boolean isModifierKey = KeyEvent.isModifierKey(keyCode); + // System.err.println("*** handleKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+", keyCode "+toHexString(keyCode)+", keyChar <"+keyChar+">, mods "+toHexString(modifiers)+", was: pressed "+isKeyPressed(keyCode)+", repeat "+isKeyInAutoRepeat(keyCode)+", printableKey "+isPrintableKey+" [modifierKey "+isModifierKey+"] - "+System.currentTimeMillis()); - // Reorder: WINDOWS delivery order is PRESSED, TYPED and RELEASED -> NEWT order: PRESSED, RELEASED and TYPED - // Auto-Repeat: WINDOWS delivers only PRESSED and TYPED. + // Reorder: WINDOWS delivery order is PRESSED (t0), TYPED (t0) and RELEASED (t1) -> NEWT order: PRESSED (t0), RELEASED (t1) and TYPED (t1) + // Auto-Repeat: WINDOWS delivers only PRESSED (t0) and TYPED (t0). switch(eventType) { case KeyEvent.EVENT_KEY_RELEASED: + final int keyCharTyped = typedKeyCode2KeyChar.put(keyCode, 0); + if( 0 != keyCharTyped ) { + keyChar = (char)keyCharTyped; + } if( isKeyCodeTracked(keyCode) ) { - if( keyRepeatState.put(keyCode, false) && !isModifierKeyCode ) { + if( keyRepeatState.put(keyCode, false) && !isModifierKey ) { // AR out - send out missing PRESSED emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_PRESSED, modifiers | InputEvent.AUTOREPEAT_MASK, keyCode, keyChar); } keyPressedState.put(keyCode, false); } - final int keyCharTyped = typedKeyCode2KeyChar.put(keyCode, 0); - if( 0 != keyCharTyped ) { - keyChar = (char)keyCharTyped; - } emitKeyEvent(send, wait, eventType, modifiers, keyCode, keyChar); emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, keyChar); break; case KeyEvent.EVENT_KEY_PRESSED: - if( isKeyCodeTracked(keyCode) ) { - if( keyPressedState.put(keyCode, true) ) { - // key was already pressed - if( keyRepeatState.put(keyCode, true) && !isModifierKeyCode ) { - emitKeyEvent(send, wait, eventType, modifiers | InputEvent.AUTOREPEAT_MASK, keyCode, keyChar); - } // else AR in - skip already send PRESSED ; or ALT - } else { - emitKeyEvent(send, wait, eventType, modifiers, keyCode, keyChar); + // TYPED is delivered right after PRESSED for printable keys and contains the key-char information, + // hence we only deliver non printable keys (action and modifier keys) here. + // Modifier keys shall not AR. + if( !isPrintableKey ) { + if( !handlePressTypedAutoRepeat(isModifierKey, send, wait, modifiers, keyCode, keyChar) ) { + emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keyChar); } - } else { - emitKeyEvent(send, wait, eventType, modifiers, keyCode, keyChar); } break; case KeyEvent.EVENT_KEY_TYPED: - if( 1 == isKeyInAutoRepeat(keyCode) ) { - modifiers |= InputEvent.AUTOREPEAT_MASK; - emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keyChar); - emitKeyEvent(send, wait, eventType, modifiers, keyCode, keyChar); - } else if( 0 != keyCode ) { + if( isPrintableKey ) { typedKeyCode2KeyChar.put(keyCode, keyChar); + if( !handlePressTypedAutoRepeat(isModifierKey, send, wait, modifiers, keyCode, keyChar) ) { + emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keyChar); + } } break; } } + private final boolean handlePressTypedAutoRepeat(boolean isModifierKey, boolean send, boolean wait, int modifiers, int keyCode, char keyChar) { + if( isKeyCodeTracked(keyCode) && keyPressedState.put(keyCode, true) ) { + final boolean preKeyRepeatState = keyRepeatState.put(keyCode, true); + if( !isModifierKey ) { + // AR: Key was already pressed: Either [enter | within] AR mode + modifiers |= InputEvent.AUTOREPEAT_MASK; + if( preKeyRepeatState ) { + // AR: Within AR mode + emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_PRESSED, modifiers, keyCode, keyChar); + } // else { AR: Enter AR mode - skip already send PRESSED ; or ALT } + emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keyChar); + emitKeyEvent(send, wait, KeyEvent.EVENT_KEY_TYPED, modifiers, keyCode, keyChar); + } + return true; + } + return false; + } + @Override public void sendKeyEvent(int eventType, int modifiers, int keyCode, char keyChar) { handleKeyEvent(true, false, eventType, modifiers, keyCode, keyChar); diff --git a/src/newt/native/WindowsWindow.c b/src/newt/native/WindowsWindow.c index 17b93cfce..24d513c68 100644 --- a/src/newt/native/WindowsWindow.c +++ b/src/newt/native/WindowsWindow.c @@ -605,7 +605,7 @@ static int WmKeyDown(JNIEnv *env, jobject window, UINT wkey, WORD repCnt, BYTE s (*env)->CallVoidMethod(env, window, sendKeyEventID, (jint) EVENT_KEY_TYPED, modifiers, - (jint) 0, + (jint) jkey, (jchar) '\177'); } diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodeModifiersAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodeModifiersAWT.java index 478f6eb8b..ec06379e0 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodeModifiersAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodeModifiersAWT.java @@ -146,7 +146,7 @@ public class TestNewtKeyCodeModifiersAWT extends UITestCase { AWTRobotUtil.keyPress(0, robot, false, KeyEvent.VK_P, 10); // release+typed P AWTRobotUtil.keyPress(0, robot, false, modifierKey, 100); // release+typed MOD robot.waitForIdle(); - for(int j=0; j < 10 && keyAdapter.getQueueSize() < 3+6; j++) { // wait until events are collected + for(int j=0; j < 20 && keyAdapter.getQueueSize() < 3+6; j++) { // wait until events are collected robot.delay(100); } NEWTKeyUtil.validateKeyAdapterStats(keyAdapter, 3+6, 0); @@ -185,7 +185,7 @@ public class TestNewtKeyCodeModifiersAWT extends UITestCase { AWTRobotUtil.keyPress(0, robot, false, m1k, 10); // release+typed MOD robot.waitForIdle(); - for(int j=0; j < 10 && keyAdapter.getQueueSize() < 3*4; j++) { // wait until events are collected + for(int j=0; j < 20 && keyAdapter.getQueueSize() < 3*4; j++) { // wait until events are collected robot.delay(100); } NEWTKeyUtil.validateKeyAdapterStats(keyAdapter, 3*4, 0); diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodesAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodesAWT.java index e43072961..ef4b17375 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodesAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyCodesAWT.java @@ -174,7 +174,7 @@ public class TestNewtKeyCodesAWT extends UITestCase { robot.waitForIdle(); } final int codeCount = codeSeg.max - codeSeg.min + 1; - for(int j=0; j < 10 && keyAdapter.getQueueSize() < 3 * codeCount; j++) { // wait until events are collected + for(int j=0; j < 20 && keyAdapter.getQueueSize() < 3 * codeCount; j++) { // wait until events are collected robot.delay(100); } final ArrayList<EventObject> events = new ArrayList<EventObject>(keyAdapter.getQueued()); diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyEventAutoRepeatAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyEventAutoRepeatAWT.java index ca612bac6..00fbc0500 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyEventAutoRepeatAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtKeyEventAutoRepeatAWT.java @@ -189,7 +189,7 @@ public class TestNewtKeyEventAutoRepeatAWT extends UITestCase { robot.waitForIdle(); AWTRobotUtil.keyPress(0, robot, false, java.awt.event.KeyEvent.VK_B, 250); robot.waitForIdle(); - for(int j=0; j < 10 && keyAdapter.getQueueSize() < firstIdx+3; j++) { // wait until events are collected + for(int j=0; j < 20 && keyAdapter.getQueueSize() < firstIdx+3; j++) { // wait until events are collected robot.delay(100); } firstIdx = keyEvents.size(); |