static UniChar OSKeyCodeToUnicode( UInt16 osKeyCode, bool shift = false ) { // Translate the key code. UniChar uniChar = 0; if( sKeyLayoutKind == kKLKCHRKind ) { // KCHR mapping. void* KCHRData; KLGetKeyboardLayoutProperty( sKeyLayout, kKLKCHRData, ( const void** ) & KCHRData ); UInt16 key = ( osKeyCode & 0x7f ); if( shift ) key |= NSShiftKeyMask; UInt32 keyTranslateState = 0; UInt32 charCode = KeyTranslate( KCHRData, key, &keyTranslateState ); charCode &= 0xff; if( keyTranslateState == 0 && charCode ) uniChar = charCode; } else { // UCHR mapping. UCKeyboardLayout* uchrData; KLGetKeyboardLayoutProperty( sKeyLayout, kKLuchrData, ( const void** ) &uchrData ); UInt32 deadKeyState; UniCharCount actualStringLength; UniChar unicodeString[ 4 ]; UCKeyTranslate( uchrData, osKeyCode, kUCKeyActionDown, ( shift ? 0x02 : 0 ), // Oh yeah... Apple docs are fun... LMGetKbdType(), 0, &deadKeyState, sizeof( unicodeString ) / sizeof( unicodeString[ 0 ] ), &actualStringLength, unicodeString ); if( actualStringLength ) uniChar = unicodeString[ 0 ]; // Well, Unicode is something else, but... } return uniChar; }
QString TranslateKey(quint8 vk) { TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); const CFDataRef layoutData = reinterpret_cast<const CFDataRef>(TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData) ); const UCKeyboardLayout *keyboardLayout = reinterpret_cast<const UCKeyboardLayout*>(CFDataGetBytePtr(layoutData)); UInt32 keysDown = 0; UniChar chars[10]; UniCharCount realLength = 0; OSStatus oss = UCKeyTranslate(keyboardLayout, vk, kUCKeyActionDown, 0, LMGetKbdType(), 0,//kUCKeyTranslateNoDeadKeysBit, &keysDown, sizeof(chars) / sizeof(chars[0]), &realLength, chars); Q_ASSERT(oss == 0); CFStringRef ptr_str = CFStringCreateWithCharacters(kCFAllocatorDefault, chars, (int)realLength); QString ss = QCFStringToQString(ptr_str); CFRelease(ptr_str); CFRelease(currentKeyboard); return ss; }
KeySym HelperMacX::keycodeToKeysym(uint keycode){ TISInputSourceRef inputSourceRef = TISCopyCurrentKeyboardInputSource(); if (NULL == inputSourceRef) { qWarning("HelperMacX::keycodeToKeysym: inputSourceRef is NULL"); return NoSymbol; } CFDataRef unicodeKeyLayoutDataRef = (CFDataRef)TISGetInputSourceProperty(inputSourceRef, kTISPropertyUnicodeKeyLayoutData); if (NULL == unicodeKeyLayoutDataRef) { CFRelease(inputSourceRef); qWarning("HelperMacX::keycodeToKeysym: unicodeKeyLayoutDataRef is NULL"); return NoSymbol; } UCKeyboardLayout *unicodeKeyLayoutDataPtr = (UCKeyboardLayout*)CFDataGetBytePtr(unicodeKeyLayoutDataRef); UInt32 deadKeyState = 0; UniChar unicodeString[8]; UniCharCount len = 0; OSStatus status = UCKeyTranslate(unicodeKeyLayoutDataPtr, keycode, kUCKeyActionDown, 0, LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask, &deadKeyState, sizeof(unicodeString), &len, unicodeString); CFRelease(inputSourceRef); if (noErr != status) { qWarning("HelperMacX::keycodeToKeysym: UCKeyTranslate error: %d", (int)status); return NoSymbol; } return 1 != len ? NoSymbol : unicodeString[0]; }
/* Returns string representation of key, if it is printable. * Ownership follows the Create Rule; that is, it is the caller's * responsibility to release the returned object. */ CFStringRef createStringForKey(CGKeyCode keyCode) { TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData); UInt32 keysDown = 0; UniChar chars[4]; UniCharCount realLength; UCKeyTranslate(keyboardLayout, keyCode, kUCKeyActionDisplay, 0, LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &keysDown, sizeof(chars) / sizeof(chars[0]), &realLength, chars); CFRelease(currentKeyboard); return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1); }
internal CFStringRef KeycodeToString(CGKeyCode Keycode) { TISInputSourceRef Keyboard = TISCopyCurrentASCIICapableKeyboardLayoutInputSource(); CFDataRef Uchr = (CFDataRef)TISGetInputSourceProperty(Keyboard, 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 CFStringCreateWithCharacters(NULL, UnicodeString, ActualStringLength); } return NULL; }
// Modified from NESControllerInterface of Macifom project, // used under MIT license from http://macifom.googlecode.com/svn-history/r89/Macifom/trunk/NESControllerInterface.m // Used under MIT license from http://inquisitivecocoa.com/2009/04/05/key-code-translator/ static wchar_t KeyCodeToChar(CGKeyCode keyCode, unsigned int modifierFlags) { TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); const UCKeyboardLayout *keyboardLayout = uchr ? (const UCKeyboardLayout*)CFDataGetBytePtr(uchr) : NULL; if( keyboardLayout ) { UInt32 deadKeyState = 0; UniCharCount maxStringLength = 255; UniCharCount actualStringLength = 0; UniChar unicodeString[maxStringLength]; OSStatus status = UCKeyTranslate(keyboardLayout, keyCode, kUCKeyActionDown, modifierFlags, LMGetKbdType(), 0, &deadKeyState, maxStringLength, &actualStringLength, unicodeString); if( status != noErr ) { fprintf(stderr, "There was an %s error translating from the '%d' key code to a human readable string: %s\n", GetMacOSStatusErrorString(status), (int)status, GetMacOSStatusCommentString(status)); } else if( actualStringLength == 0 ) { fprintf(stderr, "Couldn't find a translation for the '%d' key code\n", keyCode); } else { return unicodeString[0]; } } else { fprintf(stderr, "Couldn't find a translation for the '%d' key code\n", keyCode); } return 0; }
void HelperMacX::initUnicodeToKeycodeWithModsMap(){ unicodeToKeycodeWithModsMap.clear(); TISInputSourceRef inputSourceRef = TISCopyCurrentKeyboardInputSource(); if (NULL == inputSourceRef) { qWarning("HelperMacX::initUnicodeToKeycodeWithModsMap: inputSourceRef is NULL"); return; } CFDataRef unicodeKeyLayoutDataRef = (CFDataRef)TISGetInputSourceProperty(inputSourceRef, kTISPropertyUnicodeKeyLayoutData); if (NULL == unicodeKeyLayoutDataRef) { qWarning("HelperMacX::initUnicodeToKeycodeWithModsMap: unicodeKeyLayoutDataRef is NULL"); return; } UCKeyboardLayout *unicodeKeyLayoutDataPtr = (UCKeyboardLayout*)CFDataGetBytePtr(unicodeKeyLayoutDataRef); UInt32 deadKeyState; UniChar unicodeString[8]; UniCharCount len; for (int m = 0; m < 16; m++) { uint mods = orderedModifiers[m]; for (uint keycode = 0; keycode < 0x80; keycode++) { deadKeyState = 0; len = 0; OSStatus status = UCKeyTranslate(unicodeKeyLayoutDataPtr, keycode, kUCKeyActionDown, mods, LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask, &deadKeyState, sizeof(unicodeString), &len, unicodeString); if (noErr != status) { qWarning("HelperMacX::initUnicodeToKeycodeWithModsMap: UCKeyTranslate error: %d keycode 0x%02X modifiers 0x%02X", (int)status, keycode, mods); continue; } // store if only one char and not already in store if ((1 != len) || (0 < unicodeToKeycodeWithModsMap.count(unicodeString[0]))) continue; unicodeToKeycodeWithModsMap[unicodeString[0]] = (KeycodeWithMods){ keycode, mods }; } } }
OP_STATUS UKeyTranslate::GetUnicharFromVirtualKey(UInt32 virtualKeyCode, uni_char &outUniChar, UInt8 modifierKeyState) { #ifdef SIXTY_FOUR_BIT UInt32 deadKeyState = 0; OP_STATUS result = OpStatus::ERR; TISInputSourceRef kbInputSourceRef = TISCopyCurrentKeyboardLayoutInputSource(); CFDataRef uchrDataRef = (CFDataRef)TISGetInputSourceProperty(kbInputSourceRef, kTISPropertyUnicodeKeyLayoutData); Boolean existsUchr = ((uchrDataRef != NULL) && (CFDataGetBytePtr(uchrDataRef) != NULL) && (CFDataGetLength(uchrDataRef) != 0)); if (existsUchr) { UniCharCount actualLength = 0; UniChar outChar[2] = {0,0}; OSStatus status = UCKeyTranslate((const UCKeyboardLayout *)CFDataGetBytePtr(uchrDataRef), virtualKeyCode, kUCKeyActionDown, modifierKeyState, LMGetKbdType(), kNilOptions, &deadKeyState, 2, &actualLength, outChar); if (status == noErr && actualLength && outChar[0] != kFunctionKeyCharCode) { outUniChar = outChar[0]; result = OpStatus::OK; } } CFRelease(kbInputSourceRef); return result; #else // !SIXTY_FOUR_BIT static KeyboardLayoutRef lastKbdLayout = 0; static const UCKeyboardLayout* ucharData = NULL; static const void* charData = NULL; KeyboardLayoutRef currentKbdLayout = 0; if (noErr != KLGetCurrentKeyboardLayout(¤tKbdLayout) || !currentKbdLayout) { return OpStatus::ERR; } short keyCode; OSStatus error = noErr; UInt32 deadKeyState = 0; OP_STATUS result = OpStatus::ERR; keyCode = virtualKeyCode; if (!ucharData || (currentKbdLayout != lastKbdLayout)) { // Don't fetch this unless we have to: Because of the KeyScript issue handled below this may in some cases return 0 // KeyScript is an EXPENSIVE call, so by caching ucharData as long as possible we minimise the number of times // we need to call it. error = KLGetKeyboardLayoutProperty(currentKbdLayout, kKLuchrData, (const void**)&ucharData); } if (!ucharData) { static Boolean try_again = true; if (try_again && (smRoman == GetScriptManagerVariable(smKeyScript))) { // This is required for roman scripts in order to get something from KLGetCurrentKeyboardLayout KeyScript(smRoman | smKeyForceKeyScriptMask); error = KLGetKeyboardLayoutProperty(currentKbdLayout, kKLuchrData, (const void**)&ucharData); if (error || !ucharData) try_again = false; } } if ((error == noErr) && (ucharData != 0)) { UniCharCount actualLength = 0; UniChar outChar[2] = {0,0}; charData = NULL; error = UCKeyTranslate(ucharData, (unsigned short)keyCode, kUCKeyActionDown, modifierKeyState, LMGetKbdType(), 0, &deadKeyState, 2, &actualLength, outChar); if (error == noErr && actualLength && outChar[0] != kFunctionKeyCharCode) { outUniChar = outChar[0]; result = OpStatus::OK; #ifdef DEBUG_UNI_KEYSTROKES // if (outUniChar & 0xff00) // fprintf(stderr, "UKC:%x\n", outChar[0]); #endif } #ifdef DEBUG_UNI_KEYSTROKES else { fprintf(stderr, "UKCe:%li-%x-%lu-%x\n", error, outChar[0], virtualKeyCode, modifierKeyState); } if (actualLength != 1) fprintf(stderr, "UKCl:%lu-%x-%x-%lu-%x\n", actualLength, outChar[0], outChar[1], virtualKeyCode, modifierKeyState); #endif } else { #ifdef DEBUG_UNI_KEYSTROKES fprintf(stderr, "KLP:%li\n", error); #endif error = noErr; if (!charData || (currentKbdLayout != lastKbdLayout)) { error = KLGetKeyboardLayoutProperty(currentKbdLayout, kKLKCHRData, &charData); } if ((error == noErr) && (charData != 0)) { unsigned long charcs = KeyTranslate(charData, (keyCode & 0xFF) | (modifierKeyState << 8), &deadKeyState); if (charcs & 0xFF) { char src = charcs & 0x00FF; gTextConverter->ConvertBufferFromMac(&src, 1, &outUniChar, 1); result = OpStatus::OK; } #ifdef DEBUG_UNI_KEYSTROKES else { fprintf(stderr, "KTe\n", outUniChar); } if (charcs & 0xFF0000) { fprintf(stderr, "KTe:%x-%lu-%x\n", charcs, virtualKeyCode, modifierKeyState); } #endif } else { #ifdef DEBUG_UNI_KEYSTROKES fprintf(stderr, "KLPe:%li\n", error); #endif } } lastKbdLayout = currentKbdLayout; return result; #endif // SIXTY_FOUR_BIT }
static void PsychHIDKbQueueCallbackFunction(void *target, IOReturn result, void *sender) { // This routine is executed each time the queue transitions from empty to non-empty // The CFRunLoop of the thread in KbQueueWorkerThreadMain() is the one that executes here: IOHIDQueueRef queue = (IOHIDQueueRef) sender; IOHIDValueRef valueRef = NULL; int deviceIndex = (int) target; double timestamp; int eventValue; long keysUsage = -1; PsychHIDEventRecord evt; result=kIOReturnError; if (!queue) return; // Nothing we can do because we can't access queue, (shouldn't happen) while (1) { // This function only gets called when queue transitions from empty to non-empty // Therefore, we must process all available events in this while loop before // it will be possible for this function to be notified again. if (valueRef) { CFRelease(valueRef); valueRef = NULL; } // Dequeue next event from queue in a polling non-blocking fashion: valueRef = IOHIDQueueCopyNextValueWithTimeout(queue, 0.0); // Done? Exit, if so: if (!valueRef) break; // Get event value, e.g., the key state of a key or button 1 = pressed, 0 = released: eventValue = IOHIDValueGetIntegerValue(valueRef); // Get usage value, ie., the identity of the key: IOHIDElementRef element = IOHIDValueGetElement(valueRef); keysUsage = IOHIDElementGetUsage(element); // Get double GetSecs timestamp, computed from returned uint64 nanoseconds timestamp: timestamp = convertTime(IOHIDValueGetTimeStamp(valueRef)); // Don't bother with keysUsage of 0 (meaningless) or 1 (ErrorRollOver) for keyboards: if ((queueIsAKeyboard[deviceIndex]) && (keysUsage <= 1)) continue; // Clear ringbuffer event: memset(&evt, 0 , sizeof(evt)); // Cooked key code defaults to "unhandled", and stays that way for anything but keyboards: evt.cookedEventCode = -1; // For real keyboards we can compute cooked key codes: Requires OSX 10.5 or later. if (queueIsAKeyboard[deviceIndex]) { // Keyboard(ish) device. We can handle this under some conditions. // Init to a default of handled, but unmappable/ignored keycode: evt.cookedEventCode = 0; // Keypress event code available in mapping table? if (keysUsage < kHID2VKCSize) { // Yes: We try to map this to a character code: // Step 1: Map HID usage value to virtual keycode via LUT: uint16_t vcKey = kHID2VKC[keysUsage]; // Keep track of SHIFT keys as modifier keys: Bits 0 == Command, 1 == Shift, 2 == CapsLock, 3 == Alt/Option, 4 == CTRL if ((vcKey == kVKC_Shift || vcKey == kVKC_rShift) && (eventValue != 0)) modifierKeyState[deviceIndex] |= (1 << 1); if ((vcKey == kVKC_Shift || vcKey == kVKC_rShift) && (eventValue == 0)) modifierKeyState[deviceIndex] &= ~(1 << 1); // Keep track of ALT keys as modifier keys: if ((vcKey == kVKC_Option || vcKey == kVKC_rOption) && (eventValue != 0)) modifierKeyState[deviceIndex] |= (1 << 3); if ((vcKey == kVKC_Option || vcKey == kVKC_rOption) && (eventValue == 0)) modifierKeyState[deviceIndex] &= ~(1 << 3); // Keep track of CTRL keys as modifier keys: if ((vcKey == kVKC_Control || vcKey == kVKC_rControl) && (eventValue != 0)) modifierKeyState[deviceIndex] |= (1 << 4); if ((vcKey == kVKC_Control || vcKey == kVKC_rControl) && (eventValue == 0)) modifierKeyState[deviceIndex] &= ~(1 << 4); // Was this a CTRL + C interrupt request? if ((eventValue != 0) && (vcKey == 0x08) && (modifierKeyState[deviceIndex] & (1 << 4))) { // Yes: Tell the console input helper about it, so it can send interrupt // signals to the runtime and reenable keyboard input if appropriate: // Note: Not sure if the mutex exclusion is needed here, but better safe than sorry. PsychLockMutex(&KbQueueMutex); ConsoleInputHelper(-1); PsychUnlockMutex(&KbQueueMutex); } // Key press? if (eventValue != 0) { // Step 2: Translate virtual key code into unicode char: // Ok, this is the usual horrifying complexity of Apple's system. We use code // snippets found on StackOverflow, modified to suit our needs, e.g., we track // modifier keys manually, at least left and right ALT and SHIFT keys. We don't // care about other modifiers. TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); CFDataRef uchr = (CFDataRef) ((currentKeyboard) ? TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData) : NULL); const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*) ((uchr) ? CFDataGetBytePtr(uchr) : NULL); if (keyboardLayout) { UInt32 deadKeyState = 0; UniCharCount maxStringLength = 255; UniCharCount actualStringLength = 0; UniChar unicodeString[maxStringLength]; OSStatus status = UCKeyTranslate(keyboardLayout, vcKey, kUCKeyActionDown, modifierKeyState[deviceIndex], 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)) { // Assign final cooked / mapped keycode: evt.cookedEventCode = (int) unicodeString[0]; // Send same keystroke character to console input helper. // In kbqueue-based ListenChar(1) mode, the helper will // inject/forward the character into the runtime: // Note: ConsoleInputHelper() should be safe to call without // mutex protection for >= 0 event codes. ConsoleInputHelper(evt.cookedEventCode); } } } } } PsychLockMutex(&KbQueueMutex); // Update records of first and latest key presses and releases if (eventValue != 0) { if (psychHIDKbQueueFirstPress[deviceIndex]) { // First key press timestamp: if (psychHIDKbQueueFirstPress[deviceIndex][keysUsage-1] == 0) { psychHIDKbQueueFirstPress[deviceIndex][keysUsage-1] = timestamp; } } if (psychHIDKbQueueLastPress[deviceIndex]) { // Last key press timestamp: psychHIDKbQueueLastPress[deviceIndex][keysUsage-1] = timestamp; } evt.status |= (1 << 0); } else { if (psychHIDKbQueueFirstRelease[deviceIndex]) { // First key release timestamp: if (psychHIDKbQueueFirstRelease[deviceIndex][keysUsage-1] == 0) psychHIDKbQueueFirstRelease[deviceIndex][keysUsage-1] = timestamp; } if (psychHIDKbQueueLastRelease[deviceIndex]) { // Last key release timestamp: psychHIDKbQueueLastRelease[deviceIndex][keysUsage-1] = timestamp; } evt.status &= ~(1 << 0); } // Update event buffer: evt.timestamp = timestamp; evt.rawEventCode = keysUsage; PsychHIDAddEventToEventBuffer(deviceIndex, &evt); // Tell waiting userspace (under KbQueueMutxex protection for better scheduling) something interesting has changed: PsychSignalCondition(&KbQueueCondition); PsychUnlockMutex(&KbQueueMutex); // Next while loop iteration to dequeue potentially more events: } // Done for this queue transition. Return to runloop. }
void updateScancodes() { #ifdef QT_MAC_USE_COCOA TISInputSourceRef layout = TISCopyCurrentKeyboardLayoutInputSource(); if (!layout) { qWarning() << "Error retrieving current layout"; return; } if (layout == lastLayout) { CFRelease(layout); } else { // keyboard layout changed #ifndef NDEBUG const void *name = TISGetInputSourceProperty(layout, kTISPropertyLocalizedName); qDebug() << "Layout changed to: " << CFStringGetCStringPtr((CFStringRef)name, 0); #endif lastLayout = layout; scancodes.clear(); CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(layout, kTISPropertyUnicodeKeyLayoutData)); const UCKeyboardLayout *ucData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0; if (!ucData) { qWarning() << "Error retrieving current layout character data"; return; } for (int i = 0; i < 128; ++i) { UInt32 tmpState = 0; UniChar str[4]; UniCharCount actualLength = 0; OSStatus err = UCKeyTranslate(ucData, i, kUCKeyActionDown, 0, LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask, &tmpState, 4, &actualLength, str); if (err != noErr) { qWarning() << "Error translating unicode key" << err; } else { if (str[0] && str[0] != kFunctionKeyCharCode) { scancodes.insert(str[0], i); } } } } #else KeyboardLayoutRef layout; if (KLGetCurrentKeyboardLayout(&layout) != noErr) { qWarning() << "Error retrieving current layout"; } if (layout != lastLayout) { #ifndef NDEBUG void *name; KLGetKeyboardLayoutProperty(layout, kKLName, const_cast<const void **>(&name)); qDebug() << "Layout changed to: " << CFStringGetCStringPtr((CFStringRef) name, 0); #endif lastLayout = layout; scancodes.clear(); void *kchr; if (KLGetKeyboardLayoutProperty(layout, kKLKCHRData, const_cast<const void **>(&kchr)) != noErr) { qWarning() << "Couldn't load active keyboard layout"; } else { for (int i = 0; i < 128; i++) { UInt32 tmpState = 0; UInt32 chr = KeyTranslate(kchr, i, &tmpState); if (chr && chr != kFunctionKeyCharCode) { scancodes.insert(chr, i); } } } } #endif }