3

I'll try to explain better what I mean, and I'll also try to keep the question free from language, though if there is a way to do what I want in C# without having to reference anything it'd be nice. Anyway.

I am handling keyboard input, and converting it to string. All fine. I get the status for the Shift and CapsLock keys and EXOR it so I can figure out the casing for the resulting string.

bool shift = KeyDown(SHIFT_KEY)
bool capslock = KeyToggled(CAPSLOCK)
bool stringCasing = shift ^ capslock //if both are true/false, the string will be lowercase. Otherwise uppercase.

foreach Key k in [list of keys passed as parameter]
     char c = (char)k
     if stringCasing
          c = Char.ToUpper(c)
     else
          c = Char.ToLower(c)
end foreach

And no problems for now. If a user types "a" while holding shift or having capslock toggled, it becomes a "A".

However, if a user decides to type "!", which is "1" plus shift I only get a 1, because "1" uppercase is still "1".

I looked a bit on the web before asking this question, but all I got was "Map the keys yourself". Is that really the only answer? And also, what would happen if I mapped the keys and then a user with a different keyboard layout tried to use my application? Thanks in advance.

KappaG3
  • 660
  • 5
  • 14
  • how are you processing keyboard input? Is the KeyCode not available in the event handler? – NG. Jun 30 '13 at 03:18
  • I am building over XNA's input class, which is more or less the same as PInvoking GetKeyState. Basically, what I have access to is a list of all the virtual keys and their relative state. For example, I have the KeyCode for 1, but not for ! (admitted it exists as VK?) – KappaG3 Jun 30 '13 at 03:27
  • 1
    That's tough - this thread may be helpful but its basically what you said would be the case in your question. You kind of have to do it yourself. Not sure how to handle a different keyboard / layout. http://www.gamedev.net/topic/457783-xna-getting-text-from-keyboard/#entry4040153 – NG. Jun 30 '13 at 03:33
  • I see. Thanks for the reply, I guess I'll have to do it manually then. I hope my users won't hate me for standardizing their keyboard language. – KappaG3 Jun 30 '13 at 03:38

1 Answers1

8

This can be accomplished through the Win32 ToAscii function (MSDN reference). I don't know of any .NET framework methods that wrap these functions, so it may be necessary to use P/Invoke.

Along with ToAscii, you may need a reference to VkKeyScan to translate the key into a virtual key code. This key code is used as a parameter to ToAscii. Simple P/Invoke declarations for these methods follow:

    [DllImport("user32.dll")]
    static extern short VkKeyScan(char c);

    [DllImport("user32.dll", SetLastError=true)]
    static extern int ToAscii(
        uint uVirtKey,
        uint uScanCode,
        byte[] lpKeyState,
        out uint lpChar,
        uint flags
        );

Note the the third parameter to ToAscii is a 256-element array that references the state of each key; a value of 0x80 (high bit set) indicates the key is set. Setting element 0x10 (the virtual key code for Shift) emulates Shift being pressed.

We can then define a helper method that takes as a parameter a character representing a key, and outputs that key with Shift depressed:

public static char GetModifiedKey(char c)
{
    short vkKeyScanResult = VkKeyScan(c);

    // a result of -1 indicates no key translates to input character
    if (vkKeyScanResult == -1)
        throw new ArgumentException("No key mapping for " + c);

    // vkKeyScanResult & 0xff is the base key, without any modifiers
    uint code = (uint)vkKeyScanResult & 0xff;

    // set shift key pressed
    byte[] b = new byte[256];
    b[0x10] = 0x80;

    uint r;
    // return value of 1 expected (1 character copied to r)
    if (1 != ToAscii(code, code, b, out r, 0))
        throw new ApplicationException("Could not translate modified state");

    return (char)r;
}

Calling this method will return the character associated with Shift+base key for the input character (where the base key is the physical key pressed to enter the character, e.g., 1 is the base key for !). For instance, for a US keyboard, GetModifiedKey('7') and GetModifiedKey('&') will both return '&'. The return value will use the loaded keyboard layout; for example, on a German keyboard (where Shift+7 is /), the method will return / instead.

drf
  • 8,461
  • 32
  • 50
  • Ah, thanks a lot! That is exactly what I needed, and you explained it well. Edit: Could I get an explaination on the -1 casted to unsigned int? Of course it doesn't compile, but maybe there's something going underneath that I'm not aware of. – KappaG3 Jul 01 '13 at 15:33
  • Apologies for the error -- I refactored the first part of the code to clarify the error checking. By way of explanation, `VkKeyScan` will return -1 (0xffff) if there is no key that translates to the provided character (e.g., 'ü' on a US keyboard). – drf Jul 02 '13 at 03:29
  • Yeah, I thought so. Thanks again for the answer! – KappaG3 Jul 02 '13 at 09:17
  • Hey man, writing four years later. I still love your reply hahah! Now the ecosystem has shifted to mono, and here I am again wondering if there's a multiplatform alternative. Ahh, do I hate XNA and all it spawned. – KappaG3 Nov 05 '17 at 02:16