PsychError PSYCHHIDKbQueueStart(void)
{
    int deviceIndex;

    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()) {
        PsychGiveHelp();
        return(PsychError_none);
    };

    PsychErrorExit(PsychCapNumOutputArgs(0));
    PsychErrorExit(PsychCapNumInputArgs(1));

    deviceIndex = -1;
    PsychCopyInIntegerArg(1, kPsychArgOptional, &deviceIndex);

    PsychHIDOSKbQueueStart(deviceIndex);

    return(PsychError_none);
}
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(&timestamp);

    // 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);
}
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;
}