PsychError ReceiveReportsStop(int deviceIndex)
{
	CheckRunLoopSource(deviceIndex,"ReceiveReportsStop",__LINE__);
	if(ready[deviceIndex]){
		// Rob Yepez, at Apple, suggested that it might be better to call CFRunLoopRemoveSource than CFRunLoopSourceInvalidate.
		// He's right. There's no problem in re-enabling the callback after using CFRunLoopRemoveSource.
		// The source remains valid and can be added again to the run loop. Don't create it again.
		if(myRunLoopMode==NULL)myRunLoopMode=CFSTR("myMode");; // kCFRunLoopDefaultMode
		if(0 && optionsPrintCrashers)printf("%d: CFRunLoopRemoveSource\n",deviceIndex);
		CFRunLoopRemoveSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode);// kCFRunLoopDefaultMode
		if(0 && optionsPrintCrashers)printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex]
			   ,CFRunLoopSourceIsValid(source[deviceIndex])
			   ,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode));
		ready[deviceIndex]=0;
	}
	CheckRunLoopSource(deviceIndex,"ReceiveReportsStop",__LINE__);
	return 0;
}
/*
1. one must call CFRunLoopSourceInvalidate to kill the callback established by setInterruptReportHandlerCallback 
 before calling HIDReleaseDeviceList.

2. after calling setInterruptReportHandlerCallback to enable the callback and CFRunLoopSourceInvalidate to disable 
it, it is then impossible to re-enable the callback with that source. To later re-enable, simply remove the source, instead of
invalidating it. Once i've called CFRunLoopSourceInvalidate it appears that my only option is to release the interface by calling 
 HIDReleaseDeviceList and start over.
*/
PsychError PsychHIDReceiveReportsCleanup(void) 
{
// On a hunch, we now do both: remove the source and invalidate it. Alas, it makes no difference. I still get the CLEAR MEX crash.
	int deviceIndex;
	
	//printf("Clean up before PsychHID is flushed.\n");
	for(deviceIndex=0;deviceIndex<MAXDEVICEINDEXS;deviceIndex++) if(source[deviceIndex]!=NULL) {
                CheckRunLoopSource(deviceIndex,"PsychHIDReceiveReportsCleanup",__LINE__);
		CFRunLoopRemoveSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode);// kCFRunLoopDefaultMode
		if(0 && optionsPrintCrashers)printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex]
					,CFRunLoopSourceIsValid(source[deviceIndex])
					,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode));
		if(optionsPrintCrashers)printf("%d: CFRunLoopSourceInvalidate\n",deviceIndex);
		CFRunLoopSourceInvalidate(source[deviceIndex]);
		if(optionsPrintCrashers)printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex]
					,CFRunLoopSourceIsValid(source[deviceIndex])
					,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode));
		ready[deviceIndex]=0;
		CheckRunLoopSource(deviceIndex,"PsychHIDReceiveReportsCleanup",__LINE__);
		source[deviceIndex]=NULL;
	}			
	return 0;
}
/* PsychHIDReceiveReportsCleanup(void) -- Called at PsychHID shutdown time:

1. one must call CFRunLoopSourceInvalidate to kill the callback established by setInterruptReportHandlerCallback 
 before calling HIDReleaseDeviceList.

2. after calling setInterruptReportHandlerCallback to enable the callback and CFRunLoopSourceInvalidate to disable 
it, it is then impossible to re-enable the callback with that source. To later re-enable, simply remove the source, instead of
invalidating it. Once i've called CFRunLoopSourceInvalidate it appears that my only option is to release the interface by calling 
 HIDReleaseDeviceList and start over.

*/
PsychError PsychHIDReceiveReportsCleanup(void) 
{
	int deviceIndex;
	
	//printf("Clean up before PsychHID is flushed.\n");
	for(deviceIndex=0;deviceIndex<MAXDEVICEINDEXS;deviceIndex++) if(source[deviceIndex]!=NULL) {
        CheckRunLoopSource(deviceIndex,"PsychHIDReceiveReportsCleanup",__LINE__);
		CFRunLoopRemoveSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode);// kCFRunLoopDefaultMode
        if (optionsPrintCrashers) printf("%d: CFRunLoopSourceInvalidate\n",deviceIndex);
        CFRunLoopSourceInvalidate(source[deviceIndex]);
        if(optionsPrintCrashers) printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex]
                                           ,CFRunLoopSourceIsValid(source[deviceIndex])
                                           ,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode));
        ready[deviceIndex]=0;
        CheckRunLoopSource(deviceIndex,"PsychHIDReceiveReportsCleanup",__LINE__);
        source[deviceIndex]=NULL;
	}
    
    // Release all report linked lists, memory buffers etc.:
    PsychHIDReleaseAllReportMemory();

	return 0;
}
PsychError ReceiveReports(int deviceIndex)
{
	long error=0;
	pRecDevice device;
	IOHIDDeviceInterface122** interface=NULL;
	int reason; // kCFRunLoopRunFinished, kCFRunLoopRunStopped, kCFRunLoopRunTimedOut, kCFRunLoopRunHandledSource

	CountReports("ReceiveReports beginning.");
	if(freeReportsPtr==NULL)PrintfExit("No free reports.");

	PsychHIDVerifyInit();
	device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);
	if(!HIDIsValidDevice(device))PrintfExit("PsychHID: Invalid device.\n");
	interface=device->interface;
	if(interface==NULL)PrintfExit("PsychHID: No interface for device.\n");
	if(deviceIndex<0 || deviceIndex>MAXDEVICEINDEXS-1)PrintfExit("Sorry. Can't cope with deviceNumber %d (more than %d). Please tell [email protected]",deviceIndex,(int)MAXDEVICEINDEXS-1);
	CheckRunLoopSource(deviceIndex,"ReceiveReports",__LINE__);
	if(!ready[deviceIndex]){
		// setInterruptReportHandlerCallback
		static unsigned char buffer[MAXREPORTSIZE];
		psych_uint32 bufferSize=MAXREPORTSIZE;
		psych_bool createSource;

		createSource=(source[deviceIndex]==NULL);
		if(createSource){
			if(optionsPrintCrashers && createSource)printf("%d: createAsyncEventSource\n",deviceIndex);
			error=(*interface)->createAsyncEventSource(interface,&(source[deviceIndex]));
			if(error)PrintfExit("ReceiveReports - createAsyncEventSource error 0x%lx.",error);
			if(0 && optionsPrintCrashers && createSource)
				printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex]
					   ,CFRunLoopSourceIsValid(source[deviceIndex])
					   ,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode));
		}
		if(optionsPrintCrashers && createSource)printf("%d: getAsyncEventSource\n",deviceIndex);
		CheckRunLoopSource(deviceIndex,"ReceiveReports",__LINE__);
		if(optionsPrintCrashers && createSource)printf("%d: CFRunLoopAddSource\n",deviceIndex);
		CFRunLoopAddSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode);
		if(0 && optionsPrintCrashers && createSource)printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex]
			   ,CFRunLoopSourceIsValid(source[deviceIndex])
			   ,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode));
		ready[deviceIndex]=1;
		CheckRunLoopSource(deviceIndex,"ReceiveReports",__LINE__);
		if(optionsPrintCrashers && createSource)printf("%d: setInterruptReportHandlerCallback\n",deviceIndex);
		error=(*interface)->setInterruptReportHandlerCallback(interface,buffer,bufferSize,ReportCallback,buffer,(void *)deviceIndex);
		if(error)PrintfExit("ReceiveReports - setInterruptReportHandlerCallback error 0x%lx.",error);
		if(optionsPrintCrashers && createSource)printf("%d: CFRunLoopRunInMode.\n",deviceIndex);
	}
	//printf("%d: CFRunLoopRunInMode\n",deviceIndex);
	reason=CFRunLoopRunInMode(myRunLoopMode,optionsSecs,false);
	if(reason!=kCFRunLoopRunTimedOut && reason!=kCFRunLoopRunHandledSource){
		char *s;
		switch(reason){
			case kCFRunLoopRunFinished: s="kCFRunLoopRunFinished"; break;
			case kCFRunLoopRunStopped: s="kCFRunLoopRunStopped"; break;
			case kCFRunLoopRunTimedOut: s="kCFRunLoopRunTimedOut"; break;
			case kCFRunLoopRunHandledSource: s="kCFRunLoopRunHandledSource"; break;
			default: s="of unknown reason.";
		}
		printf("RunLoop ended at %.3f s because %s.\n",CFAbsoluteTimeGetCurrent()-AInScanStart,s);			
	}
	CountReports("ReceiveReports end.");
	return error;
}