I am working on a two-way private chat that will work in a full screen game.
This is required to let the user to type into a semi-transparent textbox at the top of the screen even when it doesn't have focus.
Using the following code, I can detect ALL physical keys, but have a tough time with virtual keys.
SHIFT
is detected.
2
is detected.
However Shift + 2
are detected both as separate keys (Even though [SHIFT+2]
gives @
on my keyboard). IE: The program outputs both SHIFT, and 2, but not what they produce: @
.
The problem is, how will I convert to a character depending on the keyboard? For example:
"
(quotes).@
.How can I convert to a specific character depending on the keyboard?
Here is the code so far:
static interface User32 extends Library {
public static User32 INSTANCE = (User32) Native.loadLibrary("User32", User32.class);
short GetAsyncKeyState(int key);
short GetKeyState(int key);
IntByReference GetKeyboardLayout(int dwLayout);
int MapVirtualKeyExW (int uCode, int nMapType, IntByReference dwhkl);
boolean GetKeyboardState(byte[] lpKeyState);
int ToUnicodeEx(int wVirtKey, int wScanCode, byte[] lpKeyState, char[] pwszBuff, int cchBuff, int wFlags, IntByReference dwhkl);
}
public static void main(String[] args) {
long currTime = System.currentTimeMillis();
while (System.currentTimeMillis() < currTime + 20000)
{
for (int key = 1; key < 256; key++)
{
if (isKeyPressed(key))
getKeyType(key);
}
}
}
private static boolean isKeyPressed(int key)
{
return User32.INSTANCE.GetAsyncKeyState(key) == -32767;
}
private static void getKeyType(int key)
{
boolean isDownShift = (User32.INSTANCE.GetKeyState(VK_SHIFT) & 0x80) == 0x80;
boolean isDownCapsLock = (User32.INSTANCE.GetKeyState(VK_CAPS)) != 0;
byte[] keystate = new byte[256];
User32.INSTANCE.GetKeyboardState(keystate);
IntByReference keyblayoutID = User32.INSTANCE.GetKeyboardLayout(0);
int ScanCode = User32.INSTANCE.MapVirtualKeyExW(key, MAPVK_VK_TO_VSC, keyblayoutID);
char[] buff = new char[10];
int bufflen = buff.length;
int ret = User32.INSTANCE.ToUnicodeEx(key, ScanCode, keystate, buff, bufflen, 0, keyblayoutID);
switch (ret)
{
case -1:
System.out.println("Error");
break;
case 0: // no translation
break;
default:
System.out.println("output=" + String.valueOf(buff).substring(0, ret));
}
}
It works fine and outputs the keys pressed, but doesn't work with Shift + combinations. I realize that I could do a "Switch" and change Shift+3 to "£", but this will not work with different keyboards.
I got it. After many, many, many hours of searching, I managed to create a method that converts the combination to what it should be on the current keyboard layout. It doesn't deal with dead-keys (such as accents), but it catches all the [SHIFT+Combinations]
that I need it to catch.
To use it, call it as follows:
getCharacter(int vkCode, boolean shiftKeyPressed);
So, watch this magic. If I want to get what SHIFT+3
will give me on my keyboard (£), I use:
getCharacter(KeyEvent.VK_3, true);
Here is the code:
public static char getCharacter(int vkCode, boolean shiftKeyPressed)
{
byte[] keyStates = new byte[256]; //Create a keyboard map of 256 keys
if (shiftKeyPressed)
{
keyStates[16]=-127; //Emulate the shift key being held down
keyStates[160]=-128; //This needs to be set as well
}
IntByReference keyblayoutID = User32.INSTANCE.GetKeyboardLayout(0); //Load local keyboard layout
int ScanCode = User32.INSTANCE.MapVirtualKeyExW(vkCode, MAPVK_VK_TO_VSC, keyblayoutID); //Get the scancode
char[] buff = new char[1];
int ret = User32.INSTANCE.ToUnicodeEx(vkCode, ScanCode, keyStates, buff, 1, 0, _currentInputLocaleIdentifier);
switch (ret)
{
case -1: //Error
return (char) -1;
case 0: //No Translation
return (char) 0;
default: //Returning key...
return buff[0];
}
}
Here are the declarations:
final static int MAPVK_VK_TO_VSC = 0;
static IntByReference _currentInputLocaleIdentifier;
static interface User32 extends Library {
public static User32 INSTANCE = (User32) Native.loadLibrary("User32", User32.class);
IntByReference GetKeyboardLayout(int dwLayout);
int MapVirtualKeyExW (int uCode, int nMapType, IntByReference dwhkl);
boolean GetKeyboardState(byte[] lpKeyState);
int ToUnicodeEx(int wVirtKey, int wScanCode, byte[] lpKeyState, char[] pwszBuff, int cchBuff, int wFlags, IntByReference dwhkl);
}
A big thank you to BrendanMcK, who helped me to get to this solution.