PsychError PsychHIDOSKbQueueCreate(int deviceIndex, int numScankeys, int* scanKeys) { dinfo* dev = NULL; // Valid number of keys? if (scanKeys && (numScankeys != 256)) { PsychErrorExitMsg(PsychError_user, "Second argument to KbQueueCreate must be a vector with 256 elements."); } if (deviceIndex < 0) { deviceIndex = PsychHIDGetDefaultKbQueueDevice(); // Ok, deviceIndex now contains our default keyboard to use - The first suitable keyboard. } else if (deviceIndex >= ndevices) { // Out of range index: PsychErrorExitMsg(PsychError_user, "Invalid 'deviceIndex' specified. No such device!"); } // Do we finally have a valid keyboard? dev = &info[deviceIndex]; // Keyboard queue for this deviceIndex already created? if (psychHIDKbQueueFirstPress[deviceIndex]) { // Yep. Release it, so we can start from scratch: PsychHIDOSKbQueueRelease(deviceIndex); } // Allocate and zero-init memory for tracking key presses and key releases: psychHIDKbQueueFirstPress[deviceIndex] = (double*) calloc(256, sizeof(double)); psychHIDKbQueueFirstRelease[deviceIndex] = (double*) calloc(256, sizeof(double)); psychHIDKbQueueLastPress[deviceIndex] = (double*) calloc(256, sizeof(double)); psychHIDKbQueueLastRelease[deviceIndex] = (double*) calloc(256, sizeof(double)); psychHIDKbQueueScanKeys[deviceIndex] = (int*) calloc(256, sizeof(int)); // Assign scanKeys vector, if any: if (scanKeys) { // Copy it: memcpy(psychHIDKbQueueScanKeys[deviceIndex], scanKeys, 256 * sizeof(int)); } else { // None provided. Enable all keys by default: memset(psychHIDKbQueueScanKeys[deviceIndex], 1, 256 * sizeof(int)); } // Create event buffer: if (!PsychHIDCreateEventBuffer(deviceIndex)) { PsychHIDOSKbQueueRelease(deviceIndex); PsychErrorExitMsg(PsychError_system, "Failed to create keyboard queue due to out of memory condition."); } // Ready to use this keybord queue. return(PsychError_none); }
PsychError PsychHIDOSKbQueueCreate(int deviceIndex, int numScankeys, int* scanKeys) { pRecDevice deviceRecord; // Valid number of keys? if (scanKeys && (numScankeys != 256)) { PsychErrorExitMsg(PsychError_user, "Second argument to KbQueueCreate must be a vector with 256 elements."); } // Do we finally have a valid keyboard or other suitable input device? // PsychHIDOSGetKbQueueDevice() will error out if no suitable device // for deviceIndex can be found. Otherwise it will return the HID // device record and remapped deviceIndex for use with our KbQueues: deviceIndex = PsychHIDOSGetKbQueueDevice(deviceIndex, &deviceRecord); // Keyboard queue for this deviceIndex already created? if (psychHIDKbQueueFirstPress[deviceIndex]) { // Yep. Release it, so we can start from scratch: PsychHIDOSKbQueueRelease(deviceIndex); } // Allocate and zero-init memory for tracking key presses and key releases: psychHIDKbQueueFirstPress[deviceIndex] = calloc(256, sizeof(double)); psychHIDKbQueueFirstRelease[deviceIndex] = calloc(256, sizeof(double)); psychHIDKbQueueLastPress[deviceIndex] = calloc(256, sizeof(double)); psychHIDKbQueueLastRelease[deviceIndex] = calloc(256, sizeof(double)); psychHIDKbQueueScanKeys[deviceIndex] = calloc(256, sizeof(int)); // Assign scanKeys vector, if any: if (scanKeys) { // Copy it: memcpy(psychHIDKbQueueScanKeys[deviceIndex], scanKeys, 256 * sizeof(int)); } else { // None provided. Enable all keys by default: memset(psychHIDKbQueueScanKeys[deviceIndex], 1, 256 * sizeof(int)); } // Create HIDQueue for device: queue[deviceIndex] = IOHIDQueueCreate(kCFAllocatorDefault, deviceRecord, 30, 0); if (NULL == queue[deviceIndex]) PsychErrorExitMsg(PsychError_system, "Failed to create event queue for detecting key press."); // Mark as a non-keyboard device, to start with: queueIsAKeyboard[deviceIndex] = FALSE; // Parse HID device to add all detected and selected keys: { // Add deviceRecord's elements to our queue, filtering unwanted keys via 'scanList'. // This code is almost identical to the enumeration code in PsychHIDKbCheck, to make sure we get // matching performance and behaviour and hopefully that it works on the latest problematic Apple // hardware, e.g., late 2013 MacBookAir and OSX 10.9: { uint32_t usage, usagePage; pRecElement currentElement, lastElement = NULL; // Step through the elements of the device and add matching ones: for (currentElement = HIDGetFirstDeviceElement(deviceRecord, kHIDElementTypeInput | kHIDElementTypeCollection); (currentElement != NULL) && (currentElement != lastElement); currentElement = HIDGetNextDeviceElement(currentElement, kHIDElementTypeInput | kHIDElementTypeCollection)) { // Keep track of last queried element: lastElement = currentElement; usage = IOHIDElementGetUsage(currentElement); usagePage = IOHIDElementGetUsagePage(currentElement); if (getenv("PSYCHHID_TELLME")) { printf("PTB-DEBUG: [KbQueueCreate]: ce %p page %d usage: %d isArray: %d\n", currentElement, usagePage, usage, IOHIDElementIsArray(currentElement)); } if (IOHIDElementGetType(currentElement) == kIOHIDElementTypeCollection) { CFArrayRef children = IOHIDElementGetChildren(currentElement); if (!children) continue; CFIndex idx, cnt = CFArrayGetCount(children); for (idx = 0; idx < cnt; idx++) { IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(children, idx); if (tIOHIDElementRef && ((IOHIDElementGetType(tIOHIDElementRef) == kIOHIDElementTypeInput_Button) || (IOHIDElementGetType(tIOHIDElementRef) == kIOHIDElementTypeInput_ScanCodes))) { usage = IOHIDElementGetUsage(tIOHIDElementRef); if ((usage <= 256) && (usage >= 1) && ( (scanKeys == NULL) || (scanKeys[usage - 1] > 0) )) { // Add it for use in keyboard queue: PsychHIDOSKbElementAdd(tIOHIDElementRef, queue[deviceIndex], deviceIndex); } } } // Done with this currentElement, which was a collection of buttons/keys. // Iterate to next currentElement: continue; } // Classic path for non-collection elements: if(((usagePage == kHIDPage_KeyboardOrKeypad) || (usagePage == kHIDPage_Button)) && (usage <= 256) && (usage >= 1) && ( (scanKeys == NULL) || (scanKeys[usage - 1] > 0) ) ) { // Add it for use in keyboard queue: PsychHIDOSKbElementAdd(currentElement, queue[deviceIndex], deviceIndex); } } } } // Register "queue empty -> non-empty transition" callback: TODO Replace queue by reference to our keyboard queue struct: IOHIDQueueRegisterValueAvailableCallback(queue[deviceIndex], (IOHIDCallback) PsychHIDKbQueueCallbackFunction, (void*) (long) deviceIndex); // Create event buffer: PsychHIDCreateEventBuffer(deviceIndex); // Start the processing thread for this queue: PsychLockMutex(&KbQueueMutex); if (PsychCreateThread(&KbQueueThread[deviceIndex], NULL, KbQueueWorkerThreadMain, (void*) (long) deviceIndex)) { // We are so screwed: // Cleanup the mess: psychHIDKbQueueActive[deviceIndex] = FALSE; PsychUnlockMutex(&KbQueueMutex); // Whine a little bit: printf("PsychHID-ERROR: Start of keyboard queue processing for deviceIndex %i failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_system, "Creation of keyboard queue background processing thread failed!"); } PsychUnlockMutex(&KbQueueMutex); // Ready to use this keybord queue. return(PsychError_none); }