// adds all elements to queue, performing any device queue set up required
// queue is started and ready to return events on exit from this function
int  HIDQueueDevice( IOHIDDeviceRef inIOHIDDeviceRef )
{
	IOReturn result = kIOReturnSuccess;
	
	// error checking
	if ( !inIOHIDDeviceRef ) {
		HIDReportError( "Device does not exist, cannot queue device." );
		return kIOReturnBadArgument;
	}
	
	if ( !inIOHIDDeviceRef ) { // must have interface
		HIDReportError( "Device does not have hid device ref, cannot queue device." );
		return kIOReturnError;
	}
	
	IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef );
	if ( !tIOHIDQueueRef ) {         // if no queue create queue
		result = HIDCreateQueue( inIOHIDDeviceRef );
		if ( kIOReturnSuccess == result ) {
			tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef );
		}
	}
	
	if ( ( kIOReturnSuccess != result ) || ( !tIOHIDQueueRef ) ) {
		HIDReportErrorNum( "Could not queue device due to problem creating queue.", result );
		
		if ( kIOReturnSuccess != result ) {
			return result;
		} else {
			return kIOReturnError;
		}
	}
	
	// stop queue
	IOHIDQueueStop( tIOHIDQueueRef );
	
	// queue element
	IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement( inIOHIDDeviceRef, kHIDElementTypeIO );
	while ( tIOHIDElementRef ) {
		if ( !IOHIDQueueContainsElement( tIOHIDQueueRef, tIOHIDElementRef ) ) {
			IOHIDQueueAddElement( tIOHIDQueueRef, tIOHIDElementRef );
		}
		tIOHIDElementRef = HIDGetNextDeviceElement( tIOHIDElementRef, kHIDElementTypeIO );
	}
	
	// restart queue
	IOHIDQueueStart( tIOHIDQueueRef );
	
	return result;
} /* HIDQueueDevice */
// ---------------------------------
// removes element for queue, if last element in queue will release queue and closes device interface
int  HIDDequeueElement( IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef )
{
	IOReturn result = kIOReturnSuccess;
	
	if ( inIOHIDDeviceRef ) {
		assert( IOHIDDeviceGetTypeID() == CFGetTypeID( inIOHIDDeviceRef ) );
		if ( inIOHIDElementRef ) {
			assert( IOHIDElementGetTypeID() == CFGetTypeID( inIOHIDElementRef ) );
			IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef );
			if ( tIOHIDQueueRef ) {
				// stop queue
				IOHIDQueueStop( tIOHIDQueueRef );
				
				// de-queue element
				if ( IOHIDQueueContainsElement( tIOHIDQueueRef, inIOHIDElementRef ) ) {
					IOHIDQueueRemoveElement( tIOHIDQueueRef, inIOHIDElementRef );
				}
				
				// release device queue and close interface if queue empty
				if ( HIDIsDeviceQueueEmpty( inIOHIDDeviceRef ) ) {
					result = HIDDisposeReleaseQueue( inIOHIDDeviceRef );
					
					if ( kIOReturnSuccess != result ) {
						HIDReportErrorNum( "Failed to dispose and release queue.", result );
					}
				} else { // not empty so restart queue
					IOHIDQueueStart( tIOHIDQueueRef );
				}
			} else {
				HIDReportError( "No queue for device passed to HIDDequeueElement." );
				if ( kIOReturnSuccess == result ) {
					result = kIOReturnError;
				}
			}
		} else {
			HIDReportError( "NULL element passed to HIDDequeueElement." );
			result = kIOReturnBadArgument;
		}
	} else {
		HIDReportError( "NULL device passed to HIDDequeueElement." );
		result = kIOReturnBadArgument;
	}
	return result;
} /* HIDDequeueElement */
// queues specific element, performing any device queue set up required
// queue is started and ready to return events on exit from this function
int  HIDQueueElement( IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef )
{
	IOReturn result = kIOReturnSuccess;
	
	if ( inIOHIDDeviceRef ) {
		assert( IOHIDDeviceGetTypeID() == CFGetTypeID( inIOHIDDeviceRef ) );
		if ( inIOHIDElementRef ) {
			assert( IOHIDElementGetTypeID() == CFGetTypeID( inIOHIDElementRef ) );
			IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef );
			if ( !tIOHIDQueueRef ) {         // if no queue create queue
				result = HIDCreateQueue( inIOHIDDeviceRef );
				if ( kIOReturnSuccess == result ) {
					tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef );
				}
			}
			if ( tIOHIDQueueRef ) {
				
				// stop queue
				IOHIDQueueStop( tIOHIDQueueRef );
				
				// queue element
				if ( !IOHIDQueueContainsElement( tIOHIDQueueRef, inIOHIDElementRef ) ) {
					IOHIDQueueAddElement( tIOHIDQueueRef, inIOHIDElementRef );
				}
				
				// restart queue
				IOHIDQueueStart( tIOHIDQueueRef );
			} else {
				HIDReportError( "No queue for device passed to HIDQueueElement." );
				if ( kIOReturnSuccess == result ) {
					result = kIOReturnError;
				}
			}
		} else {
			HIDReportError( "NULL element passed to HIDQueueElement." );
			result = kIOReturnBadArgument;
		}
	} else {
		HIDReportError( "NULL device passed to HIDQueueElement." );
		result = kIOReturnBadArgument;
	}
	return result;
} /* HIDQueueElement */
// ---------------------------------
// completely removes all elements from queue and releases queue and closes device interface
// does not release device interfaces, application must call ReleaseHIDDeviceList on exit
int  HIDDequeueDevice( IOHIDDeviceRef inIOHIDDeviceRef )
{
	
	IOReturn result = kIOReturnSuccess;
	
	// error checking
	if ( !inIOHIDDeviceRef ) {
		HIDReportError( "Device does not exist, cannot queue device." );
		return kIOReturnBadArgument;
	}
	
	if ( !inIOHIDDeviceRef ) { // must have interface
		HIDReportError( "Device does not have hid device ref, cannot queue device." );
		return kIOReturnError;
	}
	
	IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef );
	
	if ( tIOHIDQueueRef ) {
		// iterate through elements and if queued, remove
		IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement( inIOHIDDeviceRef, kHIDElementTypeIO );
		while ( tIOHIDElementRef ) {
			// de-queue element
			if ( IOHIDQueueContainsElement( tIOHIDQueueRef, tIOHIDElementRef ) ) {
				IOHIDQueueRemoveElement( tIOHIDQueueRef, tIOHIDElementRef );
			}
			tIOHIDElementRef = HIDGetNextDeviceElement( tIOHIDElementRef, kHIDElementTypeIO );
		}
		// ensure queue is disposed and released
		result = HIDDisposeReleaseQueue( inIOHIDDeviceRef );
		
		if ( kIOReturnSuccess != result ) {
			HIDReportErrorNum( "Failed to dispose and release queue.", result );
		}
	} else {
		HIDReportError( "No queue for device passed to HIDDequeueElement." );
		if ( kIOReturnSuccess == result ) {
			result = kIOReturnError;
		}
	}
	return result;
} /* HIDDequeueDevice */
// Put element into the dictionary and into the queue:
PsychError PsychHIDOSKbElementAdd(IOHIDElementRef element, IOHIDQueueRef queue, int deviceIndex)
{
    // If at least one keyboard style device is detected, mark this queue as keyboard queue:
    if (IOHIDElementGetUsagePage(element) == kHIDPage_KeyboardOrKeypad) queueIsAKeyboard[deviceIndex] = TRUE;
    
    // Avoid redundant assignment to same keycode:
    if (IOHIDQueueContainsElement(queue, element)) {
        if (getenv("PSYCHHID_TELLME")) printf("--> Key %i Already assigned --> Skipping.\n", IOHIDElementGetUsage(element) - 1);
        return(PsychError_none);
    }
    
    if (getenv("PSYCHHID_TELLME")) {
        printf("--> Accepting key %i as new KbQueue element%s.\n", IOHIDElementGetUsage(element) - 1, (queueIsAKeyboard) ? " for a keyboard" : "");
    }
    
    // Put the element cookie into the queue:
    IOHIDQueueAddElement(queue, element);
    
    return(PsychError_none);
}
// ---------------------------------
// returns true if queue is empty false otherwise
// error if no device, empty if no queue
static unsigned char HIDIsDeviceQueueEmpty( IOHIDDeviceRef inIOHIDDeviceRef )
{
	if ( inIOHIDDeviceRef ) { // need device and queue
		assert( IOHIDDeviceGetTypeID() == CFGetTypeID( inIOHIDDeviceRef ) );
		IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef );
		if ( tIOHIDQueueRef ) {
			IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement( inIOHIDDeviceRef, kHIDElementTypeIO );
			while ( tIOHIDElementRef ) {
				if ( IOHIDQueueContainsElement( tIOHIDQueueRef, tIOHIDElementRef ) ) {
					return false;
				}
				tIOHIDElementRef = HIDGetNextDeviceElement( tIOHIDElementRef, kHIDElementTypeIO );
			}
		} else {
			HIDReportError( "NULL device passed to HIDIsDeviceQueueEmpty." );
		}
	} else {
		HIDReportError( "NULL device passed to HIDIsDeviceQueueEmpty." );
	}
	return true;
} /* HIDIsDeviceQueueEmpty */