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]; }
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; }
/* 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 }; } } }
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 {
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. }