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(&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);
}
示例#6
0
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);
    }
}