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];
}
Exemple #2
0
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;
}
Exemple #3
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);
}
void UBKeyboardPalette::checkLayout()
{
    TISInputSourceRef selectedLocale = TISCopyCurrentKeyboardInputSource();

    CFStringRef sr = (CFStringRef) TISGetInputSourceProperty(selectedLocale,
                                                          kTISPropertyInputSourceID);

    if (sr!=NULL)
    {
        char clId[1024];
        CFStringGetCString(sr, clId, 1024, 0);

        for(int i=0; i<nLocalesCount;i++)
        {
            if (locales[i]->id == clId)
            {
                if (nCurrentLocale!=i)
                {
                    setLocale(i);
                }
                break;
            }
        }
    }
}
// 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 };
		}
	}
}
Exemple #7
0
quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
{
    UTF16Char ch;
    // Constants found in NSEvent.h from AppKit.framework
    switch (key)
    {
    case Qt::Key_Return:
        return kVK_Return;
    case Qt::Key_Enter:
        return kVK_ANSI_KeypadEnter;
    case Qt::Key_Tab:
        return kVK_Tab;
    case Qt::Key_Space:
        return kVK_Space;
    case Qt::Key_Backspace:
        return kVK_Delete;
    case Qt::Key_Control:
        return kVK_Command;
    case Qt::Key_Shift:
        return kVK_Shift;
    case Qt::Key_CapsLock:
        return kVK_CapsLock;
    case Qt::Key_Option:
        return kVK_Option;
    case Qt::Key_Meta:
        return kVK_Control;
    case Qt::Key_F17:
        return kVK_F17;
    case Qt::Key_VolumeUp:
        return kVK_VolumeUp;
    case Qt::Key_VolumeDown:
        return kVK_VolumeDown;
    case Qt::Key_F18:
        return kVK_F18;
    case Qt::Key_F19:
        return kVK_F19;
    case Qt::Key_F20:
        return kVK_F20;
    case Qt::Key_F5:
        return kVK_F5;
    case Qt::Key_F6:
        return kVK_F6;
    case Qt::Key_F7:
        return kVK_F7;
    case Qt::Key_F3:
        return kVK_F3;
    case Qt::Key_F8:
        return kVK_F8;
    case Qt::Key_F9:
        return kVK_F9;
    case Qt::Key_F11:
        return kVK_F11;
    case Qt::Key_F13:
        return kVK_F13;
    case Qt::Key_F16:
        return kVK_F16;
    case Qt::Key_F14:
        return kVK_F14;
    case Qt::Key_F10:
        return kVK_F10;
    case Qt::Key_F12:
        return kVK_F12;
    case Qt::Key_F15:
        return kVK_F15;
    case Qt::Key_Help:
        return kVK_Help;
    case Qt::Key_Home:
        return kVK_Home;
    case Qt::Key_PageUp:
        return kVK_PageUp;
    case Qt::Key_Delete:
        return kVK_ForwardDelete;
    case Qt::Key_F4:
        return kVK_F4;
    case Qt::Key_End:
        return kVK_End;
    case Qt::Key_F2:
        return kVK_F2;
    case Qt::Key_PageDown:
        return kVK_PageDown;
    case Qt::Key_F1:
        return kVK_F1;
    case Qt::Key_Left:
        return kVK_LeftArrow;
    case Qt::Key_Right:
        return kVK_RightArrow;
    case Qt::Key_Down:
        return kVK_DownArrow;
    case Qt::Key_Up:
        return kVK_UpArrow;
    default:
        ;
    }

    if (key == Qt::Key_Escape)	ch = 27;
    else if (key == Qt::Key_Return) ch = 13;
    else if (key == Qt::Key_Enter) ch = 3;
    else if (key == Qt::Key_Tab) ch = 9;
    else ch = key;

    CFDataRef currentLayoutData;
    TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();

    if (currentKeyboard == NULL)
        return 0;

    currentLayoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
    CFRelease(currentKeyboard);
    if (currentLayoutData == NULL)
        return 0;

    UCKeyboardLayout* header = (UCKeyboardLayout*)CFDataGetBytePtr(currentLayoutData);
    UCKeyboardTypeHeader* table = header->keyboardTypeList;

    uint8_t *data = (uint8_t*)header;
    // God, would a little documentation for this shit kill you...
    for (quint32 i=0; i < header->keyboardTypeCount; i++)
    {
        UCKeyStateRecordsIndex* stateRec = 0;
        if (table[i].keyStateRecordsIndexOffset != 0)
        {
            stateRec = reinterpret_cast<UCKeyStateRecordsIndex*>(data + table[i].keyStateRecordsIndexOffset);
            if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0;
        }

        UCKeyToCharTableIndex* charTable = reinterpret_cast<UCKeyToCharTableIndex*>(data + table[i].keyToCharTableIndexOffset);
        if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue;

        for (quint32 j=0; j < charTable->keyToCharTableCount; j++)
        {
            UCKeyOutput* keyToChar = reinterpret_cast<UCKeyOutput*>(data + charTable->keyToCharTableOffsets[j]);
            for (quint32 k=0; k < charTable->keyToCharTableSize; k++)
            {
                if (keyToChar[k] & kUCKeyOutputTestForIndexMask)
                {
                    long idx = keyToChar[k] & kUCKeyOutputGetIndexMask;
                    if (stateRec && idx < stateRec->keyStateRecordCount)
                    {
                        UCKeyStateRecord* rec = reinterpret_cast<UCKeyStateRecord*>(data + stateRec->keyStateRecordOffsets[idx]);
                        if (rec->stateZeroCharData == ch) return k;
                    }
                }
                else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE)
                {
                    if (keyToChar[k] == ch) return k;
                }
            } // for k
        } // for j
    } // for i
    return 0;
}
static bool translateKeyEventInternal(EventHandlerCallRef er, EventRef keyEvent, int *qtKey,
                                      QChar *outChar, Qt::KeyboardModifiers *outModifiers, bool *outHandled)
{
#if !defined(QT_MAC_USE_COCOA) || defined(Q_OS_MAC64)
    Q_UNUSED(er);
    Q_UNUSED(outHandled);
#endif
    const UInt32 ekind = GetEventKind(keyEvent);
    {
        UInt32 mac_modifiers = 0;
        GetEventParameter(keyEvent, kEventParamKeyModifiers, typeUInt32, 0,
                          sizeof(mac_modifiers), 0, &mac_modifiers);
#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
        qDebug("************ Mapping modifiers and key ***********");
#endif
        *outModifiers = qt_mac_get_modifiers(mac_modifiers);
#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
        qDebug("------------ Mapping modifiers and key -----------");
#endif
    }

    //get keycode
    UInt32 keyCode = 0;
    GetEventParameter(keyEvent, kEventParamKeyCode, typeUInt32, 0, sizeof(keyCode), 0, &keyCode);

    //get mac mapping
    static UInt32 tmp_unused_state = 0L;
    const UCKeyboardLayout *uchrData = 0;
#if defined(Q_OS_MAC32)
    KeyboardLayoutRef keyLayoutRef = 0;
    KLGetCurrentKeyboardLayout(&keyLayoutRef);
    OSStatus err;
    if (keyLayoutRef != 0) {
        err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData,
                                  (reinterpret_cast<const void **>(&uchrData)));
        if (err != noErr) {
            qWarning("Qt::internal::unable to get keyboardlayout %ld %s:%d",
                     long(err), __FILE__, __LINE__);
        }
    }
#else
    QCFType<TISInputSourceRef> inputSource = TISCopyCurrentKeyboardInputSource();
    Q_ASSERT(inputSource != 0);
    CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(inputSource,
                                                                 kTISPropertyUnicodeKeyLayoutData));
    uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0;
#endif
    *qtKey = Qt::Key_unknown;
    if (uchrData) {
        // The easy stuff; use the unicode stuff!
        UniChar string[4];
        UniCharCount actualLength;
        UInt32 currentModifiers = GetCurrentEventKeyModifiers();
        UInt32 currentModifiersWOAltOrControl = currentModifiers & ~(controlKey | optionKey);
        int keyAction;
        switch (ekind) {
        default:
        case kEventRawKeyDown:
            keyAction = kUCKeyActionDown;
            break;
        case kEventRawKeyUp:
            keyAction = kUCKeyActionUp;
            break;
        case kEventRawKeyRepeat:
            keyAction = kUCKeyActionAutoKey;
            break;
        }
        OSStatus err = UCKeyTranslate(uchrData, keyCode, keyAction,
                                  ((currentModifiersWOAltOrControl >> 8) & 0xff), LMGetKbdType(),
                                  kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength,
                                  string);
        if (err == noErr) {
            *outChar = QChar(string[0]);
            *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode);
            if (currentModifiersWOAltOrControl != currentModifiers) {
                // Now get the real char.
                err = UCKeyTranslate(uchrData, keyCode, keyAction,
                                     ((currentModifiers >> 8) & 0xff), LMGetKbdType(),
                                      kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength,
                                      string);
                if (err == noErr)
                    *outChar = QChar(string[0]);
            }
        } else {
Exemple #9
0
KeyID
OSXKeyState::KeyResource::getKeyID(UInt8 c)
{
	if (c == 0) {
		return kKeyNone;
	}
	else if (c >= 32 && c < 127) {
		// ASCII
		return static_cast<KeyID>(c);
	}
	else {
		// handle special keys
		switch (c) {
		case 0x01:
			return kKeyHome;

		case 0x02:
			return kKeyKP_Enter;

		case 0x03:
			return kKeyKP_Enter;

		case 0x04:
			return kKeyEnd;

		case 0x05:
			return kKeyHelp;

		case 0x08:
			return kKeyBackSpace;

		case 0x09:
			return kKeyTab;

		case 0x0b:
			return kKeyPageUp;

		case 0x0c:
			return kKeyPageDown;

		case 0x0d:
			return kKeyReturn;

		case 0x10:
			// OS X maps all the function keys (F1, etc) to this one key.
			// we can't determine the right key here so we have to do it
			// some other way.
			return kKeyNone;

		case 0x1b:
			return kKeyEscape;

		case 0x1c:
			return kKeyLeft;

		case 0x1d:
			return kKeyRight;

		case 0x1e:
			return kKeyUp;

		case 0x1f:
			return kKeyDown;

		case 0x7f:
			return kKeyDelete;

		case 0x06:
		case 0x07:
		case 0x0a:
		case 0x0e:
		case 0x0f:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1a:
			// discard other control characters
			return kKeyNone;

		default:
			// not special or unknown
			break;
		}

		// create string with character
		char str[2];
		str[0] = static_cast<char>(c);
		str[1] = 0;

#if defined(MAC_OS_X_VERSION_10_5)
		// get current keyboard script
		TISInputSourceRef isref = TISCopyCurrentKeyboardInputSource();
		CFArrayRef langs = (CFArrayRef) TISGetInputSourceProperty(isref, kTISPropertyInputSourceLanguages);
		CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)CFArrayGetValueAtIndex(langs, 0));
#else
		CFStringEncoding encoding = GetScriptManagerVariable(smKeyScript);
#endif
		// convert to unicode
		CFStringRef cfString =
			CFStringCreateWithCStringNoCopy(
				kCFAllocatorDefault, str, encoding, kCFAllocatorNull);

		// sometimes CFStringCreate...() returns NULL (e.g. Apple Korean
		// encoding with char value 214).  if it did then make no key,
		// otherwise CFStringCreateMutableCopy() will crash.
		if (cfString == NULL) {
			return kKeyNone; 
		}

		// convert to precomposed
		CFMutableStringRef mcfString =
			CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString);
		CFRelease(cfString);
		CFStringNormalize(mcfString, kCFStringNormalizationFormC);

		// check result
		int unicodeLength = CFStringGetLength(mcfString);
		if (unicodeLength == 0) {
			CFRelease(mcfString);
			return kKeyNone;
		}
		if (unicodeLength > 1) {
			// FIXME -- more than one character, we should handle this
			CFRelease(mcfString);
			return kKeyNone;
		}

		// get unicode character
		UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0);
		CFRelease(mcfString);

		// convert to KeyID
		return static_cast<KeyID>(uc);
	}
}
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.
}