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); }
void PsychHIDShutdownHIDStandardInterfaces(void) { int i; // Release all keyboard queues: for (i = 0; i < PSYCH_HID_MAX_DEVICES; i++) { if (psychHIDKbQueueFirstPress[i]) { PsychHIDOSKbQueueRelease(i); } } // Close all devices registered in x_dev array: for (i = 0; i < PSYCH_HID_MAX_DEVICES; i++) { if (x_dev[i]) x_dev[i]->Release(); x_dev[i] = NULL; } // Release keyboard queue mutex: PsychDestroyMutex(&KbQueueMutex); PsychDestroyCondition(&KbQueueCondition); KbQueueThreadTerminate = FALSE; if (!CloseHandle(hEvent)) { printf("PsychHID-WARNING: Closing keyboard event handle failed!\n"); } ndevices = 0; // Close our dedicated x-display connection and we are done: if (dinput) dinput->Release(); dinput = NULL; return; }
void PsychHIDShutdownHIDStandardInterfaces(void) { // Release the one single supported keyboard queue. // The 0 is just a meaningless dummy. PsychHIDOSKbQueueRelease(0); return; }
void PsychHIDShutdownHIDStandardInterfaces(void) { int i; // Release all keyboard queues: for (i = 0; i < PSYCH_HID_MAX_DEVICES; i++) { if (psychHIDKbQueueFirstPress[i]) { PsychHIDOSKbQueueRelease(i); } } // Release keyboard queue mutex: PsychDestroyMutex(&KbQueueMutex); PsychDestroyCondition(&KbQueueCondition); return; }
void PsychHIDOSKbTriggerWait(int deviceIndex, int numScankeys, int* scanKeys) { int keyMask[256]; int i; double t, tc; if (deviceIndex < 0) { deviceIndex = PsychHIDGetDefaultKbQueueDevice(); // Ok, deviceIndex now contains our default keyboard to use - The first suitable keyboard. } if ((deviceIndex < 0) || (deviceIndex >= ndevices)) { // Out of range index: PsychErrorExitMsg(PsychError_user, "Invalid 'deviceIndex' specified. No such device!"); } if(psychHIDKbQueueFirstPress[deviceIndex]) PsychErrorExitMsg(PsychError_user, "A queue for this device is already running, you must call KbQueueRelease() before invoking KbTriggerWait."); // Create a keyboard queue for this deviceIndex: memset(&keyMask[0], 0, sizeof(keyMask)); for (i = 0; i < numScankeys; i++) { if (scanKeys[i] < 1 || scanKeys[i] > 256) PsychErrorExitMsg(PsychError_user, "Invalid entry for triggerKey specified. Not in valid range 1 - 256!"); keyMask[scanKeys[i] - 1] = 1; } // Create keyboard queue with proper mask: PsychHIDOSKbQueueCreate(deviceIndex, 256, &keyMask[0]); PsychHIDOSKbQueueStart(deviceIndex); PsychLockMutex(&KbQueueMutex); // Scan for trigger key: while (1) { // Wait until something changes in a keyboard queue: PsychWaitCondition(&KbQueueCondition, &KbQueueMutex); // Check if our queue had one of the dedicated trigger keys pressed: for (i = 0; i < numScankeys; i++) { // Break out of scan loop if key pressed: if (psychHIDKbQueueFirstPress[deviceIndex][scanKeys[i] - 1] != 0) break; } // Triggerkey pressed? if ((i < numScankeys) && (psychHIDKbQueueFirstPress[deviceIndex][scanKeys[i] - 1] != 0)) break; // No change for our trigger keys. Repeat scan loop. } // If we reach this point, we know some triggerkey has been pressed. As we aborted // the scan on detection of the first pressed key, we can't be certain we caught the // key with the earliest key press, maybe one of the untested keys was pressed even // earlier. Therefore do another pass over all keys to find the pressed one with the // earliest (minimum) pressed time: t = DBL_MAX; for (i = 0; i < numScankeys; i++) { tc = psychHIDKbQueueFirstPress[deviceIndex][scanKeys[i] - 1]; if ((tc != 0) && (tc <= t)) t = tc; } // Done. Release the lock: PsychUnlockMutex(&KbQueueMutex); // Stop and release the queue: PsychHIDOSKbQueueStop(deviceIndex); PsychHIDOSKbQueueRelease(deviceIndex); // Return timestamp: PsychCopyOutDoubleArg(1, kPsychArgOptional, t); return; }
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); }