void CheckRunLoopSource(int deviceIndex,char *caller,int line){ CFRunLoopSourceRef currentSource; pRecDevice device; IOHIDDeviceInterface122** interface; device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex); if(!HIDIsValidDevice(device))PrintfExit("PsychHID: Invalid device.\n"); interface=device->interface; if(interface==NULL)PrintfExit("PsychHID: No interface for device.\n"); currentSource=(*interface)->getAsyncEventSource(interface); if(source[deviceIndex] != currentSource)printf("%s (%d): source[%d] %4.4lx != current source %4.4lx.\n" ,caller,line,(int)deviceIndex,(unsigned long)source[deviceIndex],(unsigned long)currentSource); if(ready[deviceIndex] && (source[deviceIndex]!=NULL) && !CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode)) printf("%d: %s(%d): \"ready\" but source not in CFRunLoop.\n",(int)deviceIndex,caller,line); if(!ready[deviceIndex] && (source[deviceIndex]!=NULL) && CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode)) printf("%d: %s(%d): \"!ready\" yet source is in CFRunLoop.\n",(int)deviceIndex,caller,line); }
PsychError PSYCHHIDGiveMeReports(void) { long error=0; int deviceIndex; int reportBytes=1024; mxArray **outErr; pRecDevice device; PsychPushHelp(useString,synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(2)); PsychErrorExit(PsychCapNumInputArgs(2)); PsychCopyInIntegerArg(1,TRUE,&deviceIndex); PsychCopyInIntegerArg(2,false,&reportBytes); PsychHIDVerifyInit(); device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex); if(!HIDIsValidDevice(device))PrintfExit("PsychHID:GiveMeReports: Invalid device.\n"); // reports error=GiveMeReports(deviceIndex,reportBytes); // PsychHIDReceiveReports.c // err outErr=PsychGetOutArgMxPtr(2); // outErr==NULL if optional argument is absent. if(outErr!=NULL){ const char *fieldNames[]={"n", "name", "description"}; mxArray *fieldValue; char *name="",*description=""; PsychHIDErrors(error,&name,&description); // Get error name and description, if available. *outErr=mxCreateStructMatrix(1,1,3,fieldNames); fieldValue=mxCreateString(name); mxSetField(*outErr,0,"name",fieldValue); fieldValue=mxCreateString(description); mxSetField(*outErr,0,"description",fieldValue); fieldValue=mxCreateDoubleMatrix(1,1,mxREAL); *mxGetPr(fieldValue)=(double)error; mxSetField(*outErr,0,"n",fieldValue); } return(PsychError_none); }
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; }
/************************************************************************* * * HIDAddDeviceToXML( inDevice ) * * Purpose: Adds a devices info to the HID_device_usage_strings.plist( XML ) file * * Inputs: inDevice - the device * Returns: Boolean - if successful */ static Boolean HIDAddDeviceToXML(IOHIDDeviceRef inIOHIDDeviceRef) { Boolean result = FALSE; if ( HIDIsValidDevice(inIOHIDDeviceRef) ) { CFStringRef vendorCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef); CFStringRef productCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef); if ( vendorCFStringRef && productCFStringRef ) { #if 0 // don't update the cookie xml file gCookieCFPropertyListRef = hu_XMLLoad( CFSTR( "HID_cookie_strings"), CFSTR("plist") ); if ( gCookieCFPropertyListRef ) { CFMutableDictionaryRef tCFMutableDictionaryRef = CFDictionaryCreateMutableCopy( kCFAllocatorDefault, 0, gCookieCFPropertyListRef); if ( tCFMutableDictionaryRef ) { if ( hu_AddVendorProductToCFDict(tCFMutableDictionaryRef, vendorID, vendorCFStringRef, productID, productCFStringRef) ) { hu_XMLSave( tCFMutableDictionaryRef, CFSTR( "HID_cookie_strings"), CFSTR("plist") ); result = TRUE; } CFRelease(tCFMutableDictionaryRef); } } #endif if ( gUsageCFPropertyListRef ) { CFRelease(gUsageCFPropertyListRef); } gUsageCFPropertyListRef = hu_XMLLoad( CFSTR( "HID_device_usage_strings"), CFSTR("plist") ); if ( gUsageCFPropertyListRef ) { CFMutableDictionaryRef tCFMutableDictionaryRef = CFDictionaryCreateMutableCopy( kCFAllocatorDefault, 0, gUsageCFPropertyListRef); if ( tCFMutableDictionaryRef ) { long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); if ( hu_AddVendorProductToCFDict(tCFMutableDictionaryRef, vendorID, vendorCFStringRef, productID, productCFStringRef) ) { hu_XMLSave( tCFMutableDictionaryRef, CFSTR( "HID_device_usage_strings"), CFSTR("plist") ); result = TRUE; } CFRelease(tCFMutableDictionaryRef); } } } } return (result); } // HIDAddDeviceToXML
PsychError PSYCHHIDGetReport(void) { long error=0; pRecDevice device; int deviceIndex; int reportType; // 1=input, 2=output, 3=feature unsigned char *reportBuffer; UInt32 reportBytes=0; int reportBufferSize=0; int reportID=0; long dims[]={1,1}; mxArray **outReport,**outErr; char *name="",*description="",string[256]; IOHIDDeviceInterface122** interface=NULL; boolean reportAvailable; double reportTime; PsychPushHelp(useString,synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(2)); PsychErrorExit(PsychCapNumInputArgs(4)); PsychCopyInIntegerArg(1,TRUE,&deviceIndex); PsychCopyInIntegerArg(2,TRUE,&reportType); PsychCopyInIntegerArg(3,TRUE,&reportID); PsychCopyInIntegerArg(4,TRUE,&reportBufferSize); outReport=PsychGetOutArgMxPtr(1); outErr=PsychGetOutArgMxPtr(2); // outErr==NULL if optional argument is absent. dims[0]=1; dims[1]=reportBufferSize; *outReport=mxCreateNumericArray(2,(void *)dims,mxUINT8_CLASS,mxREAL); if(*outReport==NULL)PrintfExit("Couldn't allocate report array."); reportBuffer=(void *)mxGetData(*outReport); if(reportBuffer==NULL)PrintfExit("Couldn't allocate report buffer."); reportBytes=reportBufferSize; PsychHIDVerifyInit(); device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex); if(!HIDIsValidDevice(device))PrintfExit("PsychHID:GetReport: Invalid device.\n"); interface=device->interface; if(interface==NULL)PrintfExit("PsychHID:GetReport: No interface for device.\n"); if(reportType==0){ printf("GetReport(reportType %d, reportID %d, reportBytes %d)\n",reportType,reportID,(int)reportBytes); }else{ // Apple defines constants for the reportType with values (0,1,2) that are one less that those specified by USB (1,2,3). if(0){ // HIDGetReport error=HIDGetReport(device,reportType-1,reportID,reportBuffer,&reportBytes); } if(0){ // getReport error=(*interface)->getReport(interface,reportType-1,reportID,reportBuffer,&reportBytes,-1,nil,nil,nil); } if(0){ // handleReport } if(1){ // using setInterruptReportHandlerCallback and CFRunLoopRunInMode error=ReceiveReports(deviceIndex); error=GiveMeReport(deviceIndex,&reportAvailable,reportBuffer,&reportBytes,&reportTime); }else{ // using getReport if(error==0)reportAvailable=1; PsychGetPrecisionTimerSeconds(&reportTime); } } if(outReport==NULL)PrintfExit("Output argument is required."); // I think MATLAB always provides this. if(error==0 && reportBytes>reportBufferSize){ error=2; name="Warning"; description=string; sprintf(description,"GetReport overflow. Expected %ld but received %ld bytes.",(long)reportBufferSize,(long)reportBytes); } if(error==0 && reportBytes<reportBufferSize){ // Reduce the declared size of the array to that of the received report. if(reportBytes>0)error=3; name="Warning"; description=string; sprintf(description,"GetReport expected %ld but received %ld bytes.",(long)reportBufferSize,(long)reportBytes); mxSetN(*outReport,reportBytes); } if(outErr!=NULL){ const char *fieldNames[]={"n", "name", "description", "reportLength", "reportTime"}; mxArray *fieldValue; PsychHIDErrors(error,&name,&description); // Get error name and description, if available. *outErr=mxCreateStructMatrix(1,1,5,fieldNames); fieldValue=mxCreateString(name); if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\"."); mxSetField(*outErr,0,"name",fieldValue); fieldValue=mxCreateString(description); if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\"."); mxSetField(*outErr,0,"description",fieldValue); fieldValue=mxCreateDoubleMatrix(1,1,mxREAL); if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\"."); *mxGetPr(fieldValue)=(double)error; mxSetField(*outErr,0,"n",fieldValue); fieldValue=mxCreateDoubleMatrix(1,1,mxREAL); if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\"."); if(reportAvailable)*mxGetPr(fieldValue)=(double)reportBytes; else *mxGetPr(fieldValue)=-1.0; mxSetField(*outErr,0,"reportLength",fieldValue); fieldValue=mxCreateDoubleMatrix(1,1,mxREAL); if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\"."); *mxGetPr(fieldValue)=reportTime; mxSetField(*outErr,0,"reportTime",fieldValue); } return(PsychError_none); }