I would like to write an application where the keybindings are specific to the location of the key on the keyboard, not the character they are mapped to. For example, the key that is between t and u on a US keyboard should perform a specific function, regardless of whether it is Y as it is in the US or Z as it is in Germany.
I think the way to do this would be to get the actual scan code given by the keyboard to the OS to represent the key that was pressed. How can I do this in java?
Or is there another way to achieve the same functionality?
Excerpt from Oracle's KeyEvent source code:
//set from native code.
private transient long rawCode = 0;
private transient long primaryLevelUnicode = 0;
private transient long scancode = 0; // for MS Windows only
private transient long extendedKeyCode = 0;
First, I thought about parsing the KeyEvent's toString() return value because it contains the scancode. But then I wrote a utility method (Tried successfully on Windows 8.) that uses reflection:
final public static Integer getScancodeFromKeyEvent(final KeyEvent keyEvent) {
Integer ret;
Field field;
try {
field = KeyEvent.class.getDeclaredField("scancode");
} catch (NoSuchFieldException nsfe) {
System.err.println("ATTENTION! The KeyEvent object does not have a field named \"scancode\"! (Which is kinda weird.)");
nsfe.printStackTrace();
return null;
}
try {
field.setAccessible(true);
} catch (SecurityException se) {
System.err.println("ATTENTION! Changing the accessibility of the KeyEvent class' field \"scancode\" caused a security exception!");
se.printStackTrace();
return null;
}
try {
ret = (int) field.getLong(keyEvent);
} catch (IllegalAccessException iae) {
System.err.println("ATTENTION! It is not allowed to read the field \"scancode\" of the KeyEvent instance!");
iae.printStackTrace();
return null;
}
return ret;
}
Apparently, resetting the field's accessibility afterwards is not necessary, as I got an exception when using this method while the setAccessible() line was commented out. (I changed it on the fly back and forth and recompiled in IntelliJ while the runtime was still on. Should be the same in Eclipse.) Would be easily possible, though, using the isAccessible() method first.
I needed the scancode for playing music on the keyboard because changing the keyboard language between QWERTZ and QWERTY swapped the notes accordeonly. It's really sad that we don't have legal access to a scancode-like value. The above solution successfully ignored the current keyboard layout configuration.
By the way, toString return value for "z" and "y" with US keyboard layout:
[KEY_PRESSED,keyCode=90,keyText=Z,keyChar='z',keyLocation=KEY_LOCATION_STANDARD,rawCode=90,primaryLevelUnicode=122,scancode=44,extendedKeyCode=0x5a] on frame0]
[KEY_PRESSED,keyCode=89,keyText=Y,keyChar='y',keyLocation=KEY_LOCATION_STANDARD,rawCode=89,primaryLevelUnicode=121,scancode=21,extendedKeyCode=0x59] on frame0]
With DE keyboard layout:
[KEY_PRESSED,keyCode=89,keyText=Y,keyChar='y',keyLocation=KEY_LOCATION_STANDARD,rawCode=89,primaryLevelUnicode=121,scancode=44,extendedKeyCode=0x59] on frame0]
[KEY_PRESSED,keyCode=90,keyText=Z,keyChar='z',keyLocation=KEY_LOCATION_STANDARD,rawCode=90,primaryLevelUnicode=122,scancode=21,extendedKeyCode=0x5a] on frame0]
Mind the scancode.
As a bonus, off-topic:
[KEY_PRESSED,keyCode=10,keyText=Enter,keyChar=Enter,keyLocation=KEY_LOCATION_STANDARD,rawCode=13,primaryLevelUnicode=13,scancode=28,extendedKeyCode=0xa] on frame0]
[KEY_PRESSED,keyCode=10,keyText=Enter,keyChar=Enter,keyLocation=KEY_LOCATION_NUMPAD,rawCode=13,primaryLevelUnicode=13,scancode=28,extendedKeyCode=0xa] on frame0]
[KEY_PRESSED,keyCode=49,keyText=1,keyChar='1',keyLocation=KEY_LOCATION_STANDARD,rawCode=49,primaryLevelUnicode=49,scancode=2,extendedKeyCode=0x31] on frame0]
[KEY_PRESSED,keyCode=97,keyText=NumPad-1,keyChar='1',keyLocation=KEY_LOCATION_NUMPAD,rawCode=97,primaryLevelUnicode=49,scancode=79,extendedKeyCode=0x61] on frame0]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With