void PsychHIDInitializeHIDStandardInterfaces(void)
{
    long KbDeviceUsagePages[NUMDEVICEUSAGES] = {kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop};
    long KbDeviceUsages[NUMDEVICEUSAGES] = {kHIDUsage_GD_Keyboard, kHIDUsage_GD_Keypad, kHIDUsage_GD_Mouse, kHIDUsage_GD_Pointer, kHIDUsage_GD_Joystick, kHIDUsage_GD_GamePad, kHIDUsage_GD_MultiAxisController};
    int	numDeviceUsages = NUMDEVICEUSAGES;
	int rc, i;

    for (i = 0; i < PSYCH_HID_MAX_DEVICES; i++) {
        queue[i] = NULL;
        psychHIDKbQueueCFRunLoopRef[i] = NULL;
        modifierKeyState[i] = 0;
        KbQueueThread[i] = NULL;
        queueIsAKeyboard[i] = FALSE;
    }

	// Init keyboard queue arrays:
	memset(&psychHIDKbQueueFirstPress[0], 0, sizeof(psychHIDKbQueueFirstPress));
	memset(&psychHIDKbQueueFirstRelease[0], 0, sizeof(psychHIDKbQueueFirstRelease));
	memset(&psychHIDKbQueueLastPress[0], 0, sizeof(psychHIDKbQueueLastPress));
	memset(&psychHIDKbQueueLastRelease[0], 0, sizeof(psychHIDKbQueueLastRelease));
	memset(&psychHIDKbQueueActive[0], 0, sizeof(psychHIDKbQueueActive));
	memset(&psychHIDKbQueueScanKeys[0], 0, sizeof(psychHIDKbQueueScanKeys));

    // Enumerate all possible candidates for default keyboard device:
    PsychHIDGetDeviceListByUsages(numDeviceUsages, KbDeviceUsagePages, KbDeviceUsages, &ndevices, deviceIndices, deviceRecords);

    // Nothing?
    if (ndevices == 0) printf("PTB-WARNING: No keyboard, keypad, mouse, touchpad, joystick etc. input devices detected! KbQueues won't work.\n");

	// Create keyboard queue mutex for later use:
	PsychInitMutex(&KbQueueMutex);
	PsychInitCondition(&KbQueueCondition, NULL);

	return;
}
PsychError PSYCHHIDKbTriggerWait(void) 
{
    pRecDevice	deviceRecord;
    int			i, deviceIndex, numDeviceIndices;
    long		KeysUsagePage=0x07;									// This is the keyboard usage page
	long		KeysUsage;											// This will contain the key code of the trigger key
    long		KbDeviceUsagePages[NUMDEVICEUSAGES]= {0x01,0x01}, KbDeviceUsages[NUMDEVICEUSAGES]={0x06,0x07}; // Generic Desktop page (0x01), keyboard (0x06), keypad (0x07)
    int 		numDeviceUsages=NUMDEVICEUSAGES;
    int			deviceIndices[PSYCH_HID_MAX_KEYBOARD_DEVICES]; 
    pRecDevice	deviceRecords[PSYCH_HID_MAX_KEYBOARD_DEVICES];
    psych_bool		isDeviceSpecified, foundUserSpecifiedDevice;
    double		*timeValueOutput;
	
	IOHIDQueueInterface **queue;
	HRESULT result;
	IOHIDDeviceInterface122** interface=NULL;	// This requires Mac OS X 10.3 or higher
	
	IOReturn success;
	
	IOHIDElementCookie triggerCookie;

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

    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(2));  	//Specify trigger key code and the deviceNumber of the keyboard or keypad to scan.  
    
    PsychHIDVerifyInit();
	
	if(hidDataRef!=NULL) PsychErrorExitMsg(PsychError_user, "A queue is already running, you must call KbQueueRelease() before invoking KbTriggerWait.");
	
	//Identify the trigger
	{
		int KeysUsageInteger;
		if(!PsychCopyInIntegerArg(1, TRUE, &KeysUsageInteger)){
			PsychErrorExitMsg(PsychError_user, "Keycode is required.");
		}
		KeysUsage=KeysUsageInteger;
	}
	
    //Choose the device index and its record
    PsychHIDGetDeviceListByUsages(numDeviceUsages, KbDeviceUsagePages, KbDeviceUsages, &numDeviceIndices, deviceIndices, deviceRecords);  
    isDeviceSpecified=PsychCopyInIntegerArg(2, FALSE, &deviceIndex);
    if(isDeviceSpecified){  //make sure that the device number provided by the user is really a keyboard or keypad.
        for(i=0;i<numDeviceIndices;i++){
            if(foundUserSpecifiedDevice=(deviceIndices[i]==deviceIndex))
                break;
        }
        if(!foundUserSpecifiedDevice)
            PsychErrorExitMsg(PsychError_user, "Specified device number is not a keyboard or keypad device.");
    }else{ // set the keyboard or keypad device to be the first keyboard device or, if no keyboard, the first keypad
        i=0;
        if(numDeviceIndices==0)
            PsychErrorExitMsg(PsychError_user, "No keyboard or keypad devices detected.");
        else{
            deviceIndex=deviceIndices[i];
        }
    }
    deviceRecord=deviceRecords[i]; 
    
    //Allocate and init out return arguments.
    PsychAllocOutDoubleArg(1, FALSE, &timeValueOutput);
	if(!timeValueOutput)
		PsychErrorExitMsg(PsychError_system, "Failed to allocate memory for output.");
		
	interface=deviceRecord->interface;
	if(!interface)
		PsychErrorExitMsg(PsychError_system, "Could not get interface to device.");
	
	// The following bracketed clause will get a cookie corresponding to the
	// trigger. If multiple keys were of interest, the code could be modified
	// trivially to iterate over an array of KeysUsage to generate an array of 
	// corresponding cookies
	{
		CFArrayRef elements;
		psych_bool usedDictionary=FALSE;
		{
			CFDictionaryRef dict=NULL;
		
			// The next few lines define a dictionary describing the key of interest
			// If they are omitted, the code will still work, but all elements will match
			// initially rather than just the one key of interest, so the code will be
			// slower since it will iterate through hundreds of keys
			CFStringRef keys[2] = {CFSTR("UsagePage"), CFSTR("Usage")};
			CFNumberRef values[2];
			values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &KeysUsagePage);
			values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &KeysUsage);
			if(values[0]!=NULL && values[1]!=NULL){
				// Even if they are NULL, it will be ok since dict can be NULL at the expense of some loss of efficiency
				dict = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, 2, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
			}
	
			// copyMatchinfElements requires IOHIDDeviceInterface122, thus Mac OS X 10.3 or higher
			// elements would have to be obtained directly from IORegistry for 10.2 or earlier
			// If dict==NULL, all elements will match, leading to some inefficiency
			success = (*interface)->copyMatchingElements(interface, dict, &elements);
		
			if(dict){
				usedDictionary=TRUE;
				CFRelease(dict);
			}

			if(values[0]) CFRelease(values[0]);
			if(values[1]) CFRelease(values[1]);
			
			if(!elements){
				PsychErrorExitMsg(PsychError_user, "Specified key code not found on device.");
			}
		}
		{
			// elements will only contain one element in this implementation, but has the
			// advantage of generalizing to future derived implementations that listen
			// for multiple keys
			CFIndex i;
			for (i=0; i<CFArrayGetCount(elements); i++)
		
			{
				long number;
				CFDictionaryRef element= CFArrayGetValueAtIndex(elements, i);
				CFTypeRef object;
				
				if(!element) continue;
				
				if(!usedDictionary){
				
					// Verify tht we are dealing with a keypad or keyboard
					object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementUsageKey));
					if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue;
					if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,&number)) continue;
					if(number!=KeysUsage) continue;
				
					// See if element corresponds to the desired key
					object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementUsagePageKey));
					if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue;
					if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number)) continue;
					if(number!=KeysUsagePage) continue;
				}
				
				// Get the cookie for this element
				object= (CFDictionaryGetValue(element, CFSTR(kIOHIDElementCookieKey)));
				if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue;
				if(!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number)) continue;
				triggerCookie = (IOHIDElementCookie) number;
				
				break;
			}
			if(CFArrayGetCount(elements)==i){
				CFRelease(elements);
				PsychErrorExitMsg(PsychError_user, "Specified key code not found on device.");
			}
			CFRelease(elements);
		}
	}

	// Allocate for the queue
	queue=(*interface)->allocQueue(interface);
	if(!queue){
		PsychErrorExitMsg(PsychError_system, "Failed to allocate event queue for detecting key press.");
	}
	
	// Create the queue
	result = (*queue)->create(queue, 0, 8);		// 8 events can be stored before the earliest event is lost
	if (kIOReturnSuccess != result){
		(*queue)->Release(queue);
		(*queue)=NULL;
		PsychErrorExitMsg(PsychError_system, "Failed to create event queue for detecting key press.");
	}
	
	// Add the trigger to the queue
	// If multiple keys were of interest, their cookies could be added in turn
	result = (*queue)->addElement(queue, triggerCookie, 0);
	if (kIOReturnSuccess != result){
		result = (*queue)->dispose(queue);
		(*queue)->Release(queue);
		(*queue)=NULL;
		PsychErrorExitMsg(PsychError_system, "Failed to add trigger key to event queues.");
	}
	
	// Start the queue
	result = (*queue)->start(queue);
	if (kIOReturnSuccess != result){
		result = (*queue)->dispose(queue);
		(*queue)->Release(queue);
		(*queue)=NULL;
		PsychErrorExitMsg(PsychError_system, "Failed to start event queues.");
	}
	 
	// Watch for the trigger
	{
		IOHIDEventStruct event;
		while(1){
			
			AbsoluteTime zeroTime = {0,0};
			result = (*queue)->getNextEvent(queue, &event, zeroTime, 0);
			if(kIOReturnSuccess==result) break;
			PsychWaitIntervalSeconds((double)0.004);  //surrender some time to other processes	
		
			// If it were of interest to trigger selectively on key press or key release,
			// this could be evaluated by checking event.value (zero versus non-zero)
			// but this would put more code inside the loop
			
			// If multiple keys are registered via addElement (not the case here), the
			// cookie for the key responsible for the event can be obtained from 
			// event.elementCookie
		}
		
		// If event.longValue is not NULL, the documentation indicates that it is up
		// to the caller to deallocate it. The circumstances under which a non-NULL
		// value would be generated are not specified. My guess is that some devices 
		// might return a 64-bit value (e.g., a tracking device coordinate).
		// Keys, having only two states, shouldn't need this, but check and free to
		// be safe		
		if ((event.longValueSize != 0) && (event.longValue != NULL)) free(event.longValue);
		
		// Set the time, using the same strategy as PsychTimeGlue's PsychGetPrecisionTimerSeconds
		// For code maintainability, it would be better if this conversion were performed
		// by a function in PsychTimeGlue
		{
			Nanoseconds timeNanoseconds=AbsoluteToNanoseconds(event.timestamp);
			UInt64 timeUInt64=UnsignedWideToUInt64(timeNanoseconds);
			double timeDouble=(double)timeUInt64;
			*timeValueOutput=timeDouble / 1000000000;
		}
	}
	
	// Clean up
	result = (*queue)->stop(queue);
	// Code from Apple sometimes removes elements from queue before disposing and sometimes does not
	// I can't see any reason to do so for a queue that's one line of code away from destruction
	// and therefore haven't
	result = (*queue)->dispose(queue);
	(*queue)->Release(queue);
	(*queue)=NULL;				// Just in case queue is redefined as static in the future
	
    // PsychGetPrecisionTimerSeconds(timeValueOutput);		// Less precise strategy than using event.timestamp
        
    return(PsychError_none);	
}
PsychError PsychHIDOSKbCheck(int deviceIndex, double* scanList)
{
    pRecDevice			deviceRecord;
    pRecElement			currentElement, lastElement = NULL;
    int					i, debuglevel = 0;
    static int			numDeviceIndices = -1;
    int					numDeviceUsages=NUMDEVICEUSAGES;
	long				KbDeviceUsagePages[NUMDEVICEUSAGES]= {kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop};
	long				KbDeviceUsages[NUMDEVICEUSAGES]={kHIDUsage_GD_Keyboard, kHIDUsage_GD_Keypad, kHIDUsage_GD_Mouse, kHIDUsage_GD_Pointer, kHIDUsage_GD_Joystick, kHIDUsage_GD_GamePad, kHIDUsage_GD_MultiAxisController};
    static int			deviceIndices[PSYCH_HID_MAX_KEYBOARD_DEVICES]; 
    static pRecDevice	deviceRecords[PSYCH_HID_MAX_KEYBOARD_DEVICES];
    psych_bool			isDeviceSpecified, foundUserSpecifiedDevice;
    double				*timeValueOutput, *isKeyDownOutput, *keyArrayOutput;
    int					m, n, p, nout;
    double				dummyKeyDown;
    double				dummykeyArrayOutput[256];
    uint32_t            usage, usagePage;
    int                 value;

    // We query keyboard and keypad devices only on first invocation, then cache and recycle the data:
    if (numDeviceIndices == -1) {
        PsychHIDVerifyInit();
        PsychHIDGetDeviceListByUsages(numDeviceUsages, KbDeviceUsagePages, KbDeviceUsages, &numDeviceIndices, deviceIndices, deviceRecords);
    }
	
    // Choose the device index and its record
    isDeviceSpecified = (deviceIndex != INT_MAX);
    if(isDeviceSpecified){  //make sure that the device number provided by the user is really a keyboard or keypad.
        if (deviceIndex < 0) {
            debuglevel = 1;
            deviceIndex = -deviceIndex;
        }

        for(i=0;i<numDeviceIndices;i++){
            if ((foundUserSpecifiedDevice=(deviceIndices[i]==deviceIndex)))
                break;
        }
        if(!foundUserSpecifiedDevice)
            PsychErrorExitMsg(PsychError_user, "Specified device number is not a keyboard or keypad device.");
    } else { // set the keyboard or keypad device to be the first keyboard device or, if no keyboard, the first keypad
        i=0;
        if(numDeviceIndices==0)
            PsychErrorExitMsg(PsychError_user, "No keyboard or keypad devices detected.");
        else{
            deviceIndex=deviceIndices[i];
        }
    }
    deviceRecord=deviceRecords[i]; 

    // Allocate and init out return arguments.

    // Either alloc out the arguments, or redirect to
    // internal dummy variables. This to avoid mxMalloc() call overhead
    // inside the PsychAllocOutXXX() routines:
    nout = PsychGetNumNamedOutputArgs();

    // keyDown flag:
    if (nout >= 1) {
       PsychAllocOutDoubleArg(1, FALSE, &isKeyDownOutput);
    } else {
       isKeyDownOutput = &dummyKeyDown;
    }
    *isKeyDownOutput= (double) FALSE;

    // key state vector:
    if (nout >= 3) {
        PsychAllocOutDoubleMatArg(3, FALSE, 1, 256, 1, &keyArrayOutput);
    }
    else {
        keyArrayOutput = &dummykeyArrayOutput[0];
    }
    memset((void*) keyArrayOutput, 0, sizeof(double) * 256);

    // Query timestamp:
    if (nout >= 2) {
        PsychAllocOutDoubleArg(2, FALSE, &timeValueOutput);

        // Get query timestamp:
        PsychGetPrecisionTimerSeconds(timeValueOutput);
    }

    // Make sure our keyboard query mechanism is not blocked for security reasons, e.g.,
    // secure password entry field active in another process, i.e., EnableSecureEventInput() active.
    if (PsychHIDWarnInputDisabled("PsychHID('KbCheck')")) return(PsychError_none);

    //step through the elements of the device.  Set flags in the return array for down keys.
    for(currentElement=HIDGetFirstDeviceElement(deviceRecord, kHIDElementTypeInput | kHIDElementTypeCollection); 
        (currentElement != NULL) && (currentElement != lastElement);
        currentElement=HIDGetNextDeviceElement(currentElement, kHIDElementTypeInput | kHIDElementTypeCollection))
    {
        // Keep track of last queried element:
        lastElement = currentElement;
        
        #ifndef __LP64__
        usage = currentElement->usage;
        usagePage = currentElement->usagePage;
        #else
        usage = IOHIDElementGetUsage(currentElement);
        usagePage = IOHIDElementGetUsagePage(currentElement);
        // printf("PTB-DEBUG: [KbCheck]: 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) && ( (scanList == NULL) || (scanList[usage - 1] > 0) )) {
                        value = (int) IOHIDElement_GetValue(tIOHIDElementRef, kIOHIDValueScaleTypePhysical);
                        if (debuglevel > 0) printf("PTB-DEBUG: [KbCheck]: usage: %x value: %d \n", usage, value);
                        keyArrayOutput[usage - 1] = (value || (int) keyArrayOutput[usage - 1]);
                        *isKeyDownOutput = keyArrayOutput[usage - 1] || *isKeyDownOutput;
                    }
                }
            }
            
            // Done with this currentElement, which was a collection of buttons/keys.
            // Iterate to next currentElement:
            continue;
        }
        #endif

        // Classic path, or 64-Bit path for non-collection elements:
        if(((usagePage == kHIDPage_KeyboardOrKeypad) || (usagePage == kHIDPage_Button)) && (usage <= 256) && (usage >= 1) &&
			( (scanList == NULL) || (scanList[usage - 1] > 0) ) ) {
            #ifndef __LP64__
            value = (int) HIDGetElementValue(deviceRecord, currentElement);
            #else
            value = (int) IOHIDElement_GetValue(currentElement, kIOHIDValueScaleTypePhysical);
            #endif

            if (debuglevel > 0) printf("PTB-DEBUG: [KbCheck]: usage: %x value: %d \n", usage, value);
            keyArrayOutput[usage - 1]=(value || (int) keyArrayOutput[usage - 1]);
            *isKeyDownOutput= keyArrayOutput[usage - 1] || *isKeyDownOutput; 
        }
    }

    return(PsychError_none);	
}