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; }
void PsychHIDOSKbQueueCheck(int deviceIndex) { double *hasKeyBeenDownOutput, *firstPressTimeOutput, *firstReleaseTimeOutput, *lastPressTimeOutput, *lastReleaseTimeOutput; psych_bool isFirstPressSpecified, isFirstReleaseSpecified, isLastPressSpecified, isLastReleaseSpecified; int i; 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!"); } // Does Keyboard queue for this deviceIndex already exist? if (NULL == psychHIDKbQueueFirstPress[deviceIndex]) { // No. Bad bad... printf("PsychHID-ERROR: Tried to check non-existent keyboard queue for deviceIndex %i! Call KbQueueCreate first!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Invalid 'deviceIndex' specified. No queue for that device yet!"); } // Allocate output PsychAllocOutDoubleArg(1, kPsychArgOptional, &hasKeyBeenDownOutput); isFirstPressSpecified = PsychAllocOutDoubleMatArg(2, kPsychArgOptional, 1, 256, 1, &firstPressTimeOutput); isFirstReleaseSpecified = PsychAllocOutDoubleMatArg(3, kPsychArgOptional, 1, 256, 1, &firstReleaseTimeOutput); isLastPressSpecified = PsychAllocOutDoubleMatArg(4, kPsychArgOptional, 1, 256, 1, &lastPressTimeOutput); isLastReleaseSpecified = PsychAllocOutDoubleMatArg(5, kPsychArgOptional, 1, 256, 1, &lastReleaseTimeOutput); // Initialize output if(isFirstPressSpecified) memset((void*) firstPressTimeOutput, 0, sizeof(double) * 256); if(isFirstReleaseSpecified) memset((void*) firstReleaseTimeOutput, 0, sizeof(double) * 256); if(isLastPressSpecified) memset((void*) lastPressTimeOutput, 0, sizeof(double) * 256); if(isLastReleaseSpecified) memset((void*) lastReleaseTimeOutput, 0, sizeof(double) * 256); *hasKeyBeenDownOutput=0; // Compute and assign output: PsychLockMutex(&KbQueueMutex); for (i = 0; i < 256; i++) { double lastRelease = psychHIDKbQueueLastRelease[deviceIndex][i]; double lastPress = psychHIDKbQueueLastPress[deviceIndex][i]; double firstRelease = psychHIDKbQueueFirstRelease[deviceIndex][i]; double firstPress = psychHIDKbQueueFirstPress[deviceIndex][i]; if (firstPress) { *hasKeyBeenDownOutput=1; if(isFirstPressSpecified) firstPressTimeOutput[i] = firstPress; psychHIDKbQueueFirstPress[deviceIndex][i] = 0; } if (firstRelease) { if(isFirstReleaseSpecified) firstReleaseTimeOutput[i] = firstRelease; psychHIDKbQueueFirstRelease[deviceIndex][i] = 0; } if (lastPress) { if(isLastPressSpecified) lastPressTimeOutput[i] = lastPress; psychHIDKbQueueLastPress[deviceIndex][i] = 0; } if (lastRelease) { if(isLastReleaseSpecified) lastReleaseTimeOutput[i] = lastRelease; psychHIDKbQueueLastRelease[deviceIndex][i] = 0; } } PsychUnlockMutex(&KbQueueMutex); return; }
void PsychHIDOSKbQueueStart(int deviceIndex) { LPDIRECTINPUTDEVICE8 kb; DIPROPDWORD dipdw; psych_bool queueActive; int i; 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!"); } // Does Keyboard queue for this deviceIndex already exist? if (NULL == psychHIDKbQueueFirstPress[deviceIndex]) { // No. Bad bad... printf("PsychHID-ERROR: Tried to start processing on non-existent keyboard queue for deviceIndex %i! Call KbQueueCreate first!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Invalid keyboard 'deviceIndex' specified. No queue for that device yet!"); } // Keyboard queue already stopped? Then we ain't nothing to do: if (psychHIDKbQueueActive[deviceIndex]) return; // Queue is inactive. Start it: // Will this be the first active queue, ie., aren't there any queues running so far? queueActive = FALSE; for (i = 0; i < PSYCH_HID_MAX_DEVICES; i++) { queueActive |= psychHIDKbQueueActive[i]; } PsychLockMutex(&KbQueueMutex); // Clear out current state for this queue: memset(psychHIDKbQueueFirstPress[deviceIndex] , 0, (256 * sizeof(double))); memset(psychHIDKbQueueFirstRelease[deviceIndex] , 0, (256 * sizeof(double))); memset(psychHIDKbQueueLastPress[deviceIndex] , 0, (256 * sizeof(double))); memset(psychHIDKbQueueLastRelease[deviceIndex] , 0, (256 * sizeof(double))); // Setup event mask, so events from our associated xinput device // get enqueued in our event queue: kb = GetXDevice(deviceIndex); // Device specific data format setup: switch (info[deviceIndex].dwDevType & 0xff) { case DI8DEVTYPE_KEYBOARD: if (DI_OK != kb->SetDataFormat(&c_dfDIKeyboard)) { PsychUnlockMutex(&KbQueueMutex); printf("PsychHID-ERROR: Tried to start processing on keyboard queue for deviceIndex %i, but setting dataformat failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Starting keyboard queue failed!"); } break; case DI8DEVTYPE_MOUSE: case DI8DEVTYPE_SCREENPOINTER: if (DI_OK != kb->SetDataFormat(&c_dfDIMouse2)) { PsychUnlockMutex(&KbQueueMutex); printf("PsychHID-ERROR: Tried to start processing on keyboard queue for deviceIndex %i, but setting dataformat failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Starting keyboard queue failed!"); } break; case DI8DEVTYPE_JOYSTICK: if (DI_OK != kb->SetDataFormat(&c_dfDIJoystick2)) { PsychUnlockMutex(&KbQueueMutex); printf("PsychHID-ERROR: Tried to start processing on keyboard queue for deviceIndex %i, but setting dataformat failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Starting keyboard queue failed!"); } break; } // Set device event buffer size to 256 elements: dipdw.diph.dwSize = sizeof(DIPROPDWORD); dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); dipdw.diph.dwObj = 0; dipdw.diph.dwHow = DIPH_DEVICE; dipdw.dwData = 256; if (DI_OK != kb->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)) { PsychUnlockMutex(&KbQueueMutex); printf("PsychHID-ERROR: Tried to start processing on keyboard queue for deviceIndex %i, but setting buffersize on device failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Starting keyboard queue failed!"); } // Enable state-change event notifications: if (DI_OK != kb->SetEventNotification(hEvent)) { PsychUnlockMutex(&KbQueueMutex); printf("PsychHID-ERROR: Tried to start processing on keyboard queue for deviceIndex %i, but setting device state notifications failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Starting keyboard queue failed!"); } if (DI_OK != kb->Acquire()) { PsychUnlockMutex(&KbQueueMutex); printf("PsychHID-ERROR: Tried to start processing on keyboard queue for deviceIndex %i, but acquiring device failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Starting keyboard queue failed!"); } // Mark this queue as logically started: psychHIDKbQueueActive[deviceIndex] = TRUE; // Queue started. PsychUnlockMutex(&KbQueueMutex); // If other queues are already active then we're done: if (queueActive) return; // No other active queues. We are the first one. // Start the common processing thread for all queues: PsychLockMutex(&KbQueueMutex); KbQueueThreadTerminate = FALSE; if (PsychCreateThread(&KbQueueThread, NULL, KbQueueWorkerThreadMain, NULL)) { // We are soo screwed: // Cleanup the mess: psychHIDKbQueueActive[deviceIndex] = FALSE; PsychUnlockMutex(&KbQueueMutex); // Whine a little bit: printf("PsychHID-ERROR: Start of keyboard queue processing failed!\n"); PsychErrorExitMsg(PsychError_system, "Creation of keyboard queue background processing thread failed!"); } // Up and running, we're done! PsychUnlockMutex(&KbQueueMutex); return; }
void PsychHIDOSKbQueueStop(int deviceIndex) { LPDIRECTINPUTDEVICE8 kb; psych_bool queueActive; int i; 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!"); } // Keyboard queue for this deviceIndex already exists? if (NULL == psychHIDKbQueueFirstPress[deviceIndex]) { // No. Nothing to do then. return; } // Keyboard queue already stopped? if (!psychHIDKbQueueActive[deviceIndex]) return; // Get device: kb = GetXDevice(deviceIndex); // Queue is active. Stop it: PsychLockMutex(&KbQueueMutex); // Release the device: if (DI_OK != kb->Unacquire()) { PsychUnlockMutex(&KbQueueMutex); printf("PsychHID-ERROR: Tried to stop processing on keyboard queue for deviceIndex %i, but releasing device failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Stopping keyboard queue failed!"); } // Disable state-change event notifications: if (DI_OK != kb->SetEventNotification(NULL)) { PsychUnlockMutex(&KbQueueMutex); printf("PsychHID-ERROR: Tried to stop processing on keyboard queue for deviceIndex %i, but disabling device state notifications failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Stopping keyboard queue failed!"); } // Mark queue logically stopped: psychHIDKbQueueActive[deviceIndex] = FALSE; PsychUnlockMutex(&KbQueueMutex); // Was this the last active queue? queueActive = FALSE; for (i = 0; i < PSYCH_HID_MAX_DEVICES; i++) { queueActive |= psychHIDKbQueueActive[i]; } // If more queues are active then we're done: if (queueActive) return; // No more active queues. Shutdown the common processing thread: PsychLockMutex(&KbQueueMutex); KbQueueThreadTerminate = TRUE; // Done. PsychUnlockMutex(&KbQueueMutex); // Shutdown the thread, wait for its termination: PsychDeleteThread(&KbQueueThread); KbQueueThreadTerminate = FALSE; // printf("DEBUG: THREAD JOINED.\n"); fflush(NULL); return; }
PsychError PsychHIDOSKbCheck(int deviceIndex, double* scanList) { psych_uint8 keys[1024]; LPDIRECTINPUTDEVICE8 kb; unsigned int i, j; double* buttonStates; int keysdown; double timestamp; DWORD cbSize; if (deviceIndex == INT_MAX) { 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!"); } // Get DirectInput keyboard device: kb = GetXDevice(deviceIndex); // Keyboard queue for this deviceIndex already exists? if (NULL == psychHIDKbQueueFirstPress[deviceIndex]) { // No. Create one which accepts all keys: PsychHIDOSKbQueueCreate(deviceIndex, 0, NULL); } // Keyboard queue for this device active? If not, we need // to start it: if (!psychHIDKbQueueActive[deviceIndex]) { // Keyboard idle: Need to start it: PsychHIDOSKbQueueStart(deviceIndex); // Startup to first key delivery takes time. Wait for // 50 msecs to be on the safe side: PsychYieldIntervalSeconds(0.050); } // Size of state structure is device dependent: switch (info[deviceIndex].dwDevType & 0xff) { case DI8DEVTYPE_KEYBOARD: cbSize = 256; break; case DI8DEVTYPE_MOUSE: case DI8DEVTYPE_SCREENPOINTER: cbSize = sizeof(DIMOUSESTATE2); break; case DI8DEVTYPE_JOYSTICK: cbSize = sizeof(DIJOYSTATE2); break; default: // Unkown device. Fail. cbSize = 0; } // Query current state snapshot of keyboard: memset(keys, 0, sizeof(keys)); if (DI_OK != kb->GetDeviceState(cbSize, (LPVOID) &keys[0])) { printf("PsychHID-ERROR: KbCheck for deviceIndex %i failed, because query of device failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "KbCheck failed!"); } // Request current time of query: PsychGetAdjustedPrecisionTimerSeconds(×tamp); // Reset overall key state to "none pressed": keysdown = 0; // Copy out timestamp: PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp); // Copy keyboard state: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates); for (i = 0; i < 256; i++) buttonStates[i] = 0; // Keyboard? if (cbSize == 256) { // Copy button state to output vector, apply scanlist mask, compute // resulting overall keysdown state. We ignore keyboard scancode zero and // start with 1 instead. We also ignore code 255. These are borderline codes // which may do weird things... for (i = 1; i < 255; i++) { // Compute target key slot for this scancode i: j = PsychHIDOSMapKey(i); // This key down? buttonStates[j] += (keys[i] > 0) ? 1 : 0; // Apply scanList mask, if any provided: if (scanList && (scanList[j] <= 0)) buttonStates[j] = 0; keysdown += (unsigned int) buttonStates[j]; } } // Joystick? if (cbSize == sizeof(DIJOYSTATE2)) { // Copy button state to output vector, apply scanlist mask, compute // resulting overall keysdown state. There are 128 buttons at an offset: for (i = (8 * sizeof(LONG) + 4 * sizeof(DWORD)); i < (8 * sizeof(LONG) + 4 * sizeof(DWORD)) + 128; i++) { // Compute target key slot for this scancode i: j = i - (8 * sizeof(LONG) + 4 * sizeof(DWORD)); // This key down? buttonStates[j] += (keys[i] > 0) ? 1 : 0; // Apply scanList mask, if any provided: if (scanList && (scanList[j] <= 0)) buttonStates[j] = 0; keysdown += (unsigned int) buttonStates[j]; } } // Mouse? if (cbSize == sizeof(DIMOUSESTATE2)) { // Copy button state to output vector, apply scanlist mask, compute // resulting overall keysdown state. There are 8 buttons at an offset: for (i = (3 * sizeof(LONG)); i < (3 * sizeof(LONG)) + 8; i++) { // Compute target key slot for this scancode i: j = i - (3 * sizeof(LONG)); // This key down? buttonStates[j] += (keys[i] > 0) ? 1 : 0; // Apply scanList mask, if any provided: if (scanList && (scanList[j] <= 0)) buttonStates[j] = 0; keysdown += (unsigned int) buttonStates[j]; } } // Copy out overall keystate: PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown > 0) ? 1 : 0); return(PsychError_none); }
int PsychHIDReturnEventFromEventBuffer(int deviceIndex, int outArgIndex, double maxWaitTimeSecs) { unsigned int navail, j; PsychHIDEventRecord evt; PsychGenericScriptType *retevent; double* foo = NULL; PsychGenericScriptType *outMat; double *v; const char *FieldNames[] = { "Type", "Time", "Pressed", "Keycode", "CookedKey", "ButtonStates", "Motion", "X", "Y", "NormX", "NormY", "Valuators" }; if (deviceIndex < 0) deviceIndex = PsychHIDGetDefaultKbQueueDevice(); if (!hidEventBuffer[deviceIndex]) return(0); PsychLockMutex(&hidEventBufferMutex[deviceIndex]); navail = hidEventBufferWritePos[deviceIndex] - hidEventBufferReadPos[deviceIndex]; // If nothing available and we're asked to wait for something, then wait: if ((navail == 0) && (maxWaitTimeSecs > 0)) { // Wait for something: PsychTimedWaitCondition(&hidEventBufferCondition[deviceIndex], &hidEventBufferMutex[deviceIndex], maxWaitTimeSecs); // Recompute number of available events: navail = hidEventBufferWritePos[deviceIndex] - hidEventBufferReadPos[deviceIndex]; } // Check if anything available, copy it if so: if (navail) { memcpy(&evt, &(hidEventBuffer[deviceIndex][hidEventBufferReadPos[deviceIndex] % hidEventBufferCapacity[deviceIndex]]), sizeof(PsychHIDEventRecord)); hidEventBufferReadPos[deviceIndex]++; } PsychUnlockMutex(&hidEventBufferMutex[deviceIndex]); if (navail) { // Return event struct: switch (evt.type) { case 0: // Press/Release case 1: // Motion/Valuator change PsychAllocOutStructArray(outArgIndex, kPsychArgOptional, 1, 12, FieldNames, &retevent); break; case 2: // Touch begin case 3: // Touch update/move case 4: // Touch end case 5: // Touch sequence compromised marker. If this one shows up - with magic touch point // id 0xffffffff btw., then the user script knows the sequence was cut short / aborted // by some higher priority consumer, e.g., some global gesture recognizer. PsychAllocOutStructArray(outArgIndex, kPsychArgOptional, 1, 12, FieldNames, &retevent); break; default: PsychErrorExitMsg(PsychError_internal, "Unhandled keyboard queue event type!"); } PsychSetStructArrayDoubleElement("Type", 0, (double) evt.type, retevent); PsychSetStructArrayDoubleElement("Time", 0, evt.timestamp, retevent); PsychSetStructArrayDoubleElement("Pressed", 0, (double) (evt.status & (1 << 0)) ? 1 : 0, retevent); PsychSetStructArrayDoubleElement("Keycode", 0, (double) evt.rawEventCode, retevent); PsychSetStructArrayDoubleElement("CookedKey", 0, (double) evt.cookedEventCode, retevent); PsychSetStructArrayDoubleElement("ButtonStates", 0, (double) evt.buttonStates, retevent); PsychSetStructArrayDoubleElement("Motion", 0, (double) (evt.status & (1 << 1)) ? 1 : 0, retevent); PsychSetStructArrayDoubleElement("X", 0, (double) evt.X, retevent); PsychSetStructArrayDoubleElement("Y", 0, (double) evt.Y, retevent); PsychSetStructArrayDoubleElement("NormX", 0, (double) evt.normX, retevent); PsychSetStructArrayDoubleElement("NormY", 0, (double) evt.normY, retevent); // Copy out all valuators (including redundant (X,Y) again: PsychAllocateNativeDoubleMat(1, evt.numValuators, 1, &v, &outMat); for (j = 0; j < evt.numValuators; j++) *(v++) = (double) evt.valuators[j]; PsychSetStructArrayNativeElement("Valuators", 0, outMat, retevent); return(navail - 1); } else { // Return empty matrix: PsychCopyOutDoubleMatArg(outArgIndex, kPsychArgOptional, 0, 0, 0, foo); return(0); } }