Convert Virtual Key Code to unicode string
Asked Answered
D

1

9

I have some code I've been using to get the current keyboard layout and convert a virtual key code into a string. This works great in most situations, but I'm having trouble with some specific cases. The one that brought this to light is the accent key next to the backspace key on german QWERTZ keyboards. http://en.wikipedia.org/wiki/File:KB_Germany.svg

That key generates the VK code I'd expect kVK_ANSI_Equal but when using a QWERTZ keyboard layout I get no description back. Its ending up as a dead key because its supposed to be composed with another key. Is there any way to catch these cases and do the proper conversion?

My current code is below.

TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);

if(keyboardLayout)
{
    UInt32 deadKeyState = 0;
    UniCharCount maxStringLength = 255;
    UniCharCount actualStringLength = 0;
    UniChar unicodeString[maxStringLength];

    OSStatus status = UCKeyTranslate(keyboardLayout,
                                     keyCode, kUCKeyActionDown, 0,
                                     LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit,
                                     &deadKeyState,
                                     maxStringLength,
                                     &actualStringLength, unicodeString);

    if(actualStringLength > 0 && status == noErr)
        return [[NSString stringWithCharacters:unicodeString length:(NSInteger)actualStringLength] uppercaseString];
}
Disgorge answered 24/11, 2011 at 23:59 Comment(1)
Should you not set kUCKeyTranslateNoDeadKeysMask instead of kUCKeyTranslateNoDeadKeysBit, since the latter is defined as 0 whereas the former is a mask with that bit actually enabled?Calorie
O
14

That key is a dead key, as you can see if you try it yourself or look at the Keyboard Viewer with the German layout active.

On the Mac, the way to enter a dead key's actual character, without composing it with another character, is to press a space after it. So try that: Turn off kUCKeyTranslateNoDeadKeysBit, and if UCKeyTranslate sets the dead-key state, translate a space after it.

EDIT (added by asker)

Just for future people, here is the fixed code with the right solution.

TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);

if(keyboardLayout)
{
    UInt32 deadKeyState = 0;
    UniCharCount maxStringLength = 255;
    UniCharCount actualStringLength = 0;
    UniChar unicodeString[maxStringLength];

    OSStatus status = UCKeyTranslate(keyboardLayout,
                                     keyCode, kUCKeyActionDown, 0,
                                     LMGetKbdType(), 0,
                                     &deadKeyState,
                                     maxStringLength,
                                     &actualStringLength, unicodeString);

    if (actualStringLength == 0 && deadKeyState)
    {
        status = UCKeyTranslate(keyboardLayout,
                                         kVK_Space, kUCKeyActionDown, 0,
                                         LMGetKbdType(), 0,
                                         &deadKeyState,
                                         maxStringLength,
                                         &actualStringLength, unicodeString);   
    }
    if(actualStringLength > 0 && status == noErr)
        return [[NSString stringWithCharacters:unicodeString length:(NSUInteger)actualStringLength] uppercaseString];
}
Off answered 25/11, 2011 at 0:44 Comment(5)
This code is for key binding translations. So that key is usable outside of normal typing in this context. I'm grabbing it at the HID level, but I need a way to show the user what key is bound. How do I tell that for a QWERTZ layout, that virtual key is that keycap?Disgorge
Here is the fixed code, thank you very much Peter, github.com/OpenEmu/OpenEmu/commit/…Disgorge
@JoshuaWeinberg: I corrected the code you added to my answer; you cast the length to the wrong type.Off
Note: in the original post, kUCKeyTranslateNoDeadKeysBit was not turned on in the first place, since it was used incorrectly. There's no difference between passing kUCKeyTranslateNoDeadKeysBit or 0 since the former is defined to 0. The actual mask with the bit set is kUCKeyTranslateNoDeadKeysMask.Calorie
Totally awesome. Thanks peter!Hash

© 2022 - 2024 — McMap. All rights reserved.