void PsychGetAdjustedPrecisionTimerSeconds(double *secs) { double rawSecs; PsychGetPrecisionTimerSeconds(&rawSecs); *secs=rawSecs * precisionTimerAdjustmentFactor; }
void PsychGetPrecisionTimerTicks(psych_uint64 *ticks) { double secs; // MK: Simply map current systemtime to microseconds... PsychGetPrecisionTimerSeconds(&secs); *ticks = (psych_uint64) (secs * 1000000.0 + 0.5); return; }
void ReportCallback(void *target,IOReturn result,void *refcon,void *sender,psych_uint32 bufferSize) { int deviceIndex,i,n,m; unsigned char *ptr; ReportStruct *r; CountReports("ReportCallback beginning."); deviceIndex=(int)refcon; if(deviceIndex<0 | deviceIndex>MAXDEVICEINDEXS-1){ printf("ReportCallback received out-of-range deviceIndex %d. Substituting zero.\n",deviceIndex); deviceIndex=0; } // take report from free list. if(freeReportsPtr==NULL){ // Darn. We're full. It might be elegant to discard oldest report, but for now, we'll just ignore the new one. printf("ReportCallback warning. No more free reports. Discarding new report.\n"); return; } r=freeReportsPtr; freeReportsPtr=r->next; r->next=NULL; // install report into the device's list. r->next=deviceReportsPtr[deviceIndex]; deviceReportsPtr[deviceIndex]=r; // fill in the rest of the report struct r->error=result; r->bytes=bufferSize; r->deviceIndex=deviceIndex; ptr=target; for(i=0;i<bufferSize && i<MAXREPORTSIZE;i++)r->report[i]=*(ptr+i); PsychGetPrecisionTimerSeconds(&r->time); if(optionsPrintReportSummary){ // print diagnostic summary of the report int serial; serial=r->report[62]+256*r->report[63]; // 32-bit serial number at end of AInScan report from PMD-1208FS printf("Got input report %4d: %2ld bytes, dev. %d, %4.0f ms. ",serial,(long)r->bytes,deviceIndex,1000*(r->time-AInScanStart)); if(r->bytes>0){ printf(" report "); n=r->bytes; if(n>6)n=6; for(i=0;i<n;i++)printf("%3d ",(int)r->report[i]); m=r->bytes-2; if(m>i){ printf("... "); i=m; } for(;i<r->bytes;i++)printf("%3d ",(int)r->report[i]); } printf("\n"); } CountReports("ReportCallback end."); return; }
// GiveMeReports is called solely by PsychHIDGiveMeReports, but the code resides here // in PsychHIDReceiveReports because it uses the typedefs and static variables that // are defined solely in this file. The linked lists of reports are unknown outside of this file. PsychError GiveMeReports(int deviceIndex,int reportBytes) { mwSize dims[]={1,1}; mxArray **outReports; ReportStruct *r,*rTail; const char *fieldNames[]={"report", "device", "time"}; mxArray *fieldValue; unsigned char *reportBuffer; int i,n; unsigned int j; long error=0; double now; CountReports("GiveMeReports beginning."); outReports=PsychGetOutArgMxPtr(1); r=deviceReportsPtr[deviceIndex]; n=0; while(r!=NULL){ n++; rTail=r; r=r->next; } *outReports=mxCreateStructMatrix(1,n,3,fieldNames); r=deviceReportsPtr[deviceIndex]; PsychGetPrecisionTimerSeconds(&now); for(i=n-1;i>=0;i--){ // assert(r!=NULL); if(r->error)error=r->error; dims[0]=1; //printf("%2d: r->bytes %2d, reportBytes %4d, -%4.1f s\n",i,(int)r->bytes,(int)reportBytes, now-r->time); if(r->bytes> (unsigned int) reportBytes)r->bytes=reportBytes; dims[1]=r->bytes; fieldValue=mxCreateNumericArray(2,(void *)dims,mxUINT8_CLASS,mxREAL); reportBuffer=(void *)mxGetData(fieldValue); for(j=0;j<r->bytes;j++)reportBuffer[j]=r->report[j]; if(fieldValue==NULL)PrintfExit("Couldn't allocate report array."); mxSetField(*outReports,i,"report",fieldValue); fieldValue=mxCreateDoubleMatrix(1,1,mxREAL); *mxGetPr(fieldValue)=(double)r->deviceIndex; mxSetField(*outReports,i,"device",fieldValue); fieldValue=mxCreateDoubleMatrix(1,1,mxREAL); *mxGetPr(fieldValue)=r->time; mxSetField(*outReports,i,"time",fieldValue); r=r->next; } if(deviceReportsPtr[deviceIndex]!=NULL){ // transfer all these now-obsolete reports to the free list rTail->next=freeReportsPtr; freeReportsPtr=deviceReportsPtr[deviceIndex]; deviceReportsPtr[deviceIndex]=NULL; } CountReports("GiveMeReports end."); return error; }
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { double startTime, endTime, currentTime; PsychGetPrecisionTimerSeconds(&startTime); if(nlhs > 0) mexErrMsgTxt("WaitSecsMex uses no output arguments."); if(nrhs > 1) mexErrMsgTxt("WaitSecsMex requires only one input argument."); if(nrhs < 1) mexErrMsgTxt("WaitSecsMex requires one input argument."); if(mxGetM(prhs[0]) * mxGetN(prhs[0]) != 1) mexErrMsgTxt("WaitSecs requires 1x1 double as input argument"); endTime=mxGetPr(prhs[0])[0] + startTime; for(PsychGetPrecisionTimerSeconds(¤tTime);currentTime<endTime;PsychGetPrecisionTimerSeconds(¤tTime)) ; }
void PsychWaitIntervalSeconds(double delaySecs) { double deadline; if (delaySecs <= 0) return; // Get current time: PsychGetPrecisionTimerSeconds(&deadline); // Compute deadline in absolute system time: deadline+=delaySecs; // Wait until deadline reached: PsychWaitUntilSeconds(deadline); return; }
void PsychWaitUntilSeconds(double whenSecs) { static unsigned int missed_count=0; double now=0.0; // Get current time: PsychGetPrecisionTimerSeconds(&now); // If the deadline has already passed, we do nothing and return immediately: if (now > whenSecs) return; // Note that technically there is potential for a race-condition on the // sleepwait_threshold and missed_count variables if multiple threads // inside the same PTB module call PsychWaitUntilSeconds concurrently // and miss their deadlines simultaneously --> concurrent update of that // vars. However, currently no modules do this concurrently in threads, // and even if, the worst case outcome -- sleepwait_threshold incrementing // slower than possible -- is perfectly tolerable. Therefore we safe us // the locking overhead here. // Waiting stage 1: If we have more than sleepwait_threshold seconds left // until the deadline, we call the OS Sleep() function, so the // CPU gets released for difference - sleepwait_threshold s to other processes and threads. // -> Good for general system behaviour and for lowered // power-consumption (longer battery runtime for Laptops) as // the CPU can go idle if nothing else to do... while(whenSecs - now > sleepwait_threshold) { // Sleep until only sleepwait_threshold away from deadline: Sleep((int)((whenSecs - now - sleepwait_threshold) * 1000.0f)); // Recheck: PsychGetPrecisionTimerSeconds(&now); } // Waiting stage 2: We are less than sleepwait_threshold s away from deadline. // Perform busy-waiting until deadline reached: while(now < whenSecs) PsychGetPrecisionTimerSeconds(&now); // Check for deadline-miss of more than 1 ms: if (now - whenSecs > 0.001) { // Deadline missed by over 1 ms. missed_count++; if (missed_count>5) { // Too many consecutive misses. Increase our threshold for sleep-waiting // by 5 ms until it reaches 20 ms. if (sleepwait_threshold < 0.02) sleepwait_threshold+=0.005; printf("PTB-WARNING: Wait-Deadline missed for %i consecutive times (Last miss %0.6f ms). New sleepwait_threshold is %0.6f ms.\n", missed_count, (now - whenSecs)*1000.0f, sleepwait_threshold*1000.0f); // Reset missed count after increase of threshold: missed_count = 0; } } else { // No miss detected. Reset counter... missed_count=0; } // Ready. return; }
/* Do all the report processing: Iterates in a fetch loop * until either no more reports pending for processing, error condition, * or a maximum allowable processing time of optionSecs seconds has been * exceeded. * * Calls hidlib function hid_read() to get reports, one at a time, enqueues * it in our own reports lists for later retrieval by 'GiveMeReports' or * 'GiveMeReport'. * */ PsychError ReceiveReports(int deviceIndex) { double deadline, now; pRecDevice device; int n, m; unsigned int i; ReportStruct *r; long error = 0; CountReports("ReceiveReports beginning."); if(freeReportsPtr==NULL) PrintfExit("No free reports."); 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); PsychHIDVerifyInit(); device = PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex); last_hid_device = (hid_device*) device->interface; PsychGetAdjustedPrecisionTimerSeconds(&deadline); deadline += optionsSecs; // Iterate until deadline reached or no more pending reports to process: while (TRUE) { // Test for timeout: PsychGetAdjustedPrecisionTimerSeconds(&now); if (now >= deadline) break; CountReports("ReportCallback beginning."); // take report from free list. if(freeReportsPtr == NULL){ // Darn. We're full. It might be elegant to discard oldest report, but for now, we'll just ignore the new one. printf("PsychHID: WARNING! ReportCallback warning. No more free reports. Discarding new report.\n"); break; } r = freeReportsPtr; freeReportsPtr = r->next; r->next=NULL; // install report into the device's list. r->next = deviceReportsPtr[deviceIndex]; deviceReportsPtr[deviceIndex] = r; // fill in the rest of the report struct r->deviceIndex = deviceIndex; // Fetch the actual data: Bytes fetched, or zero for no reports available, or // -1 for error condition. r->error = hid_read((hid_device*) device->interface, &(r->report[0]), MAXREPORTSIZE); if (r->error >= 0) { // Success: Reset error, assign size of retrieved report: r->bytes = r->error; r->error = 0; } else { // Error: No data assigned. r->bytes = 0; // Signal error return code -1: error = -1; // Abort fetch loop: break; } // Timestamp processing: PsychGetPrecisionTimerSeconds(&r->time); if (optionsPrintReportSummary) { // print diagnostic summary of the report int serial; serial = r->report[62] + 256 * r->report[63]; // 32-bit serial number at end of AInScan report from PMD-1208FS printf("Got input report %4d: %2ld bytes, dev. %d, %4.0f ms. ", serial, (long) r->bytes, deviceIndex, 1000 * (r->time - AInScanStart)); if(r->bytes>0) { printf(" report "); n = r->bytes; if (n > 6) n=6; for(i=0; i < (unsigned int) n; i++) printf("%3d ", (int) r->report[i]); m = r->bytes - 2; if (m > (int) i) { printf("... "); i = m; } for(; i < r->bytes; i++) printf("%3d ", (int) r->report[i]); } printf("\n"); } CountReports("ReportCallback end."); // If no data has been returned then we're done for now: if (r->bytes == 0) break; } CountReports("ReceiveReports end."); return error; }
/* Do all the report processing for all devices: Iterates in a fetch loop * until error condition, or a maximum allowable processing time of * optionSecs seconds has been exceeded. * * Calls hidlib function hid_read() to get reports, one at a time, enqueues * it in our own reports lists for later retrieval by 'GiveMeReports' or * 'GiveMeReport'. * */ PsychError ReceiveReports(int deviceIndex) { int rateLimit[MAXDEVICEINDEXS] = { 0 }; double deadline, now; pRecDevice device; int n, m; unsigned int i; ReportStruct *r; long error = 0; PsychHIDVerifyInit(); if(deviceIndex < 0 || deviceIndex >= MAXDEVICEINDEXS) PrintfExit("Sorry. Can't cope with deviceNumber %d (more than %d). Please tell [email protected]",deviceIndex, (int) MAXDEVICEINDEXS-1); // Allocate report buffers if needed: PsychHIDAllocateReports(deviceIndex); CountReports("ReceiveReports beginning."); if (freeReportsPtr[deviceIndex] == NULL) PrintfExit("No free reports."); // Enable this device for hid report reception: ready[deviceIndex] = TRUE; PsychGetAdjustedPrecisionTimerSeconds(&now); deadline = now + optionsSecs; // Iterate until deadline reached or no more pending reports to process: while ((error == 0) && (now <= deadline)) { // Iterate over all active devices: for (deviceIndex = 0; deviceIndex < MAXDEVICEINDEXS; deviceIndex++) { // Test for timeout: PsychGetAdjustedPrecisionTimerSeconds(&now); if (now > deadline) break; // Skip this device if it isn't enabled to receive hid reports: if (!ready[deviceIndex]) continue; // Free target report buffers? if(freeReportsPtr[deviceIndex] == NULL) { // Darn. We're full. It might be elegant to discard oldest report, but for now, we'll just ignore the new one. if (!rateLimit[deviceIndex]) printf("PsychHID: WARNING! ReportCallback warning. No more free reports for deviceIndex %i. Discarding new report.\n", deviceIndex); rateLimit[deviceIndex] = 1; continue; } // Handle one report for this device: CountReports("ReportCallback beginning."); device = PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex); last_hid_device = (hid_device*) device->interface; // Get a report struct to fill in: r = freeReportsPtr[deviceIndex]; // Fetch the actual data: Bytes fetched, or zero for no reports available, or // -1 for error condition. r->error = hid_read((hid_device*) device->interface, &(r->report[0]), MaxDeviceReportSize[deviceIndex]); // Skip remainder if no data received: if (r->error == 0) continue; // Ok, we got something, even if it is only an error code. Need // to move the (r)eport from the free list to the received list: freeReportsPtr[deviceIndex] = r->next; r->next=NULL; // install report into the device's list. r->next = deviceReportsPtr[deviceIndex]; deviceReportsPtr[deviceIndex] = r; // fill in the rest of the report struct r->deviceIndex = deviceIndex; // Timestamp processing: PsychGetPrecisionTimerSeconds(&r->time); // Success or error? if (r->error > 0) { // Success: Reset error, assign size of retrieved report: r->bytes = r->error; r->error = 0; } else { // Error: No data assigned. r->bytes = 0; // Signal error return code -1: error = -1; // Abort fetch loop: break; } if (optionsPrintReportSummary) { // print diagnostic summary of the report int serial; serial = r->report[62] + 256 * r->report[63]; // 32-bit serial number at end of AInScan report from PMD-1208FS printf("Got input report %4d: %2ld bytes, dev. %d, %4.0f ms. ", serial, (long) r->bytes, deviceIndex, 1000 * (r->time - AInScanStart)); if(r->bytes>0) { printf(" report "); n = r->bytes; if (n > 6) n=6; for(i=0; i < (unsigned int) n; i++) printf("%3d ", (int) r->report[i]); m = r->bytes - 2; if (m > (int) i) { printf("... "); i = m; } for(; i < r->bytes; i++) printf("%3d ", (int) r->report[i]); } printf("\n"); } CountReports("ReportCallback end."); } } CountReports("ReceiveReports end."); return error; }
void PsychWaitUntilSeconds(double whenSecs) { struct timespec rqtp; double targettime; static unsigned int missed_count=0; double now=0.0; int rc; // Get current time: PsychGetPrecisionTimerSeconds(&now); // If the deadline has already passed, we do nothing and return immediately: if (now >= whenSecs) return; // Waiting stage 1: If we have more than sleepwait_threshold seconds left // until the deadline, we call the OS usleep() function, so the // CPU gets released for (difference - sleepwait_threshold) seconds to other processes and threads. // -> Good for general system behaviour and for lowered power-consumption (longer battery runtime for // Laptops) as the CPU can go idle if nothing else to do... // Set an absolute deadline of whenSecs - sleepwait_threshold. We busy-wait the last few microseconds // to take scheduling jitter/delays gracefully into account: targettime = whenSecs - sleepwait_threshold; // Convert targettime to timespec for the Posix clock functions: rqtp.tv_sec = (unsigned long long) targettime; rqtp.tv_nsec = ((targettime - (double) rqtp.tv_sec) * (double) 1e9); // Use clock_nanosleep() to high-res sleep until targettime, repeat if that gets // prematurely interrupted for whatever reason... while(now < targettime) { // MK: Oldstyle - obsolete: usleep((unsigned long)((whenSecs - now - sleepwait_threshold) * 1000000.0f)); // Starting in 2008, we use high-precision/high-resolution POSIX realtime timers for precise waiting: // Call clock_nanosleep, use the realtime wall clock instead of the monotonic clock -- monotonic would // by theoretically a bit better as NTP time adjustments couldn't mess with our sleep, but that would // cause inconsistencies to other times reported by different useful system services which all measure // against wall clock, and in practice, the effect of NTP adjustments is minimal or negligible, as these // never create backwards running time or large timewarps, only 1 ppm level adjustments per second, ie, // the effect is way below the sleepwait_threshold for any reasonable sleep time -- easily compensated by // our hybrid approach... // We use TIMER_ABSTIME, so we are totally drift-free and restartable in case our sleep gets interrupted by // signals. If clock_nanosleep gets EINTR - Interrupted by a posix signal, we simply loop and restart the // sleep. If it returns a different error condition, we abort sleep iteration -- something would be seriously // wrong... if ((rc = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &rqtp, NULL)) && (rc != EINTR)) break; // Update our 'now' time for reiterating or continuing with busy-sleep... PsychGetPrecisionTimerSeconds(&now); } // Waiting stage 2: We are less than sleepwait_threshold seconds away from deadline. // Perform busy-waiting until deadline reached: while(now < whenSecs) PsychGetPrecisionTimerSeconds(&now); // Check for deadline-miss of more than 0.1 ms: if (now - whenSecs > 0.0001) { // Deadline missed by over 0.1 ms. missed_count++; // As long as the threshold is below a msec, immediately increase by 100 microsecs... if (sleepwait_threshold < 0.001) sleepwait_threshold+=0.0001; // If threshold has reached 1 msec, we require multiple consecutive misses before increasing any further: if (missed_count>5) { // Too many consecutive misses. Increase our threshold for sleep-waiting // by 0.1 ms until it reaches max. 10 ms. if (sleepwait_threshold < 0.01) sleepwait_threshold+=0.0001; printf("PTB-WARNING: Wait-Deadline missed for %i consecutive times (Last miss %lf ms). New sleepwait_threshold is %lf ms.\n", missed_count, (now - whenSecs)*1000.0f, sleepwait_threshold*1000.0f); } } else { // No miss detected. Reset counter... missed_count=0; } // Ready. return; }
PsychError SCREENDontCopyWindow(void) { PsychRectType sourceRect, targetRect, targetRectInverted; PsychWindowRecordType *sourceWin, *targetWin; GLdouble sourceVertex[2], targetVertex[2]; double t1,t2; double sourceRectWidth, sourceRectHeight; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the number of inputs PsychErrorExit(PsychCapNumInputArgs(5)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get parameters for the source window: PsychAllocInWindowRecordArg(1, TRUE, &sourceWin); PsychCopyRect(sourceRect, sourceWin->rect); PsychCopyInRectArg(3, FALSE, sourceRect); //get paramters for the target window: PsychAllocInWindowRecordArg(2, TRUE, &targetWin); PsychCopyRect(targetRect, targetWin->rect); PsychCopyInRectArg(4, FALSE, targetRect); //Check that the windows agree in depth. They don't have to agree in format because we convert to the correct format for the target when we //create the texture. if(sourceWin->depth != targetWin->depth) PsychErrorExitMsg(PsychError_user, "Source and target windows must have the same bit depth"); //We need to unbind the source window texture from any previous target contexts if it is bound. There can be only one binding at a time. //We could augment the Psychtoolbox to support shared contexts and multiple bindings. PsychRetargetWindowToWindow(sourceWin, targetWin); //each of these next three commands makes changes only when neccessary, they can be called generously //when there is a possibility that any are necessary. //PsychGetPrecisionTimerSeconds(&t1); PsychAllocateTexture(sourceWin); PsychBindTexture(sourceWin); PsychUpdateTexture(sourceWin); //PsychGetPrecisionTimerSeconds(&t2); //mexPrintf("texture checking copywindow took %f seconds\n", t2-t1); //PsychGetPrecisionTimerSeconds(&t1); PsychGetPrecisionTimerSeconds(&t1); PsychSetGLContext(targetWin); glBindTexture(GL_TEXTURE_RECTANGLE_EXT, sourceWin->glTexture); sourceRectWidth= PsychGetWidthFromRect(sourceWin->rect); sourceRectHeight= PsychGetHeightFromRect(sourceWin->rect); glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, (GLsizei)sourceRectWidth, (GLsizei)sourceRectHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, sourceWin->textureMemory); PsychInvertRectY(targetRectInverted, targetRect, targetWin->rect); //PsychGetPrecisionTimerSeconds(&t1); targetVertex[0]=0; targetVertex[1]=0; glBegin(GL_QUADS); glTexCoord2dv(PsychExtractQuadVertexFromRect(sourceRect, 0, sourceVertex)); glVertex2dv(targetVertex); glTexCoord2dv(PsychExtractQuadVertexFromRect(sourceRect, 1, sourceVertex)); glVertex2dv(targetVertex); glTexCoord2dv(PsychExtractQuadVertexFromRect(sourceRect, 2, sourceVertex)); glVertex2dv(targetVertex); glTexCoord2dv(PsychExtractQuadVertexFromRect(sourceRect, 3, sourceVertex)); glVertex2dv(targetVertex); glEnd(); glFinish(); //PsychGetPrecisionTimerSeconds(&t2); //mexPrintf("copywindow took %f seconds\n", t2-t1); return(PsychError_none); }
PsychError PSYCHHIDSetReport(void) { long error; pRecDevice device; int deviceIndex; int reportType; // kIOHIDReportTypeInput=0, kIOHIDReportTypeOutput=1, or kIOHIDReportTypeFeature=2 int reportID; unsigned char *reportBuffer; int reportSize; const mxArray *report; mxArray **outErr; int i; PsychPushHelp(useString,synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(4)); outErr=PsychGetOutArgMxPtr(1); assert(outErr!=NULL); PsychCopyInIntegerArg(1,TRUE,&deviceIndex); PsychCopyInIntegerArg(2,TRUE,&reportType); PsychCopyInIntegerArg(3,TRUE,&reportID); report=PsychGetInArgMxPtr(4); reportBuffer=(void *)mxGetPr(report); assert(reportBuffer!=NULL); switch(mxGetClassID(report)){ case mxCHAR_CLASS: case mxINT8_CLASS: case mxUINT8_CLASS: case mxINT16_CLASS: case mxUINT16_CLASS: case mxINT32_CLASS: case mxUINT32_CLASS: break; default: PrintfExit("\"report\" array must be char or integer (8-, 16-, or 32-bit)."); break; } reportSize=mxGetElementSize(report)*mxGetNumberOfElements(report); PsychHIDVerifyInit(); device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex); if(reportSize>0 && reportID!=0) *reportBuffer=0xff&reportID; // copy low byte of reportID to first byte of report. // Apple defines constants for the reportType with values (0,1,2) that are one less that those specified by USB (1,2,3). assert(kIOHIDReportTypeInput==0 && kIOHIDReportTypeOutput==1 && kIOHIDReportTypeFeature==2); if(reportType==0){ printf("SetReport(reportType %d, reportID %d, report ",reportType,reportID); for(i=0;i<reportSize;i++)printf("%d ",(int)reportBuffer[i]); printf(")\n"); error=0; }else{ if(1){ IOHIDDeviceInterface122 **interface; interface=(IOHIDDeviceInterface122 **)(device->interface); if(interface)error=(*interface)->setReport(interface,reportType-1,reportID,reportBuffer,(UInt32)reportSize,50,NULL,NULL,NULL); else PrintfExit("PsychHID SetReport: Bad interface.\n"); }else error=HIDSetReport(device,reportType-1,reportID,reportBuffer,(UInt32)reportSize); } if(reportID==0x11){ PsychGetPrecisionTimerSeconds(&AInScanStart); } //if(error)printf("Warning: PsychHID: HIDSetReport error %ld (0x%lx).",error,error); if(0){ *outErr=mxCreateDoubleMatrix(1,1,mxREAL); if(*outErr==NULL)PrintfExit("Couldn't allocate \"err\"."); *mxGetPr(*outErr)=(double)error; }else{ const char *fieldNames[]={"n", "name", "description"}; char *name="",*description=""; mxArray *fieldValue; PsychHIDErrors(error,&name,&description); *outErr=mxCreateStructMatrix(1,1,3,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); } return(PsychError_none); }
PsychError SCREENTestTextureTwo(void) { #if PSYCH_SYSTEM == PSYCH_OSX PsychWindowRecordType *sourceWinRec, *destWinRec; unsigned int memorySizeBytes; UInt32 *textureMemory; GLuint myTexture; int sourceWinWidth, sourceWinHeight; double t1, t2; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous or missing arguments PsychErrorExit(PsychCapNumInputArgs(2)); PsychErrorExit(PsychRequireNumInputArgs(2)); //Get the window structure for the onscreen and offscreen windows. PsychAllocInWindowRecordArg(1, TRUE, &sourceWinRec); PsychAllocInWindowRecordArg(2, TRUE, &destWinRec); //Now allocate memory for our texture. This is a still a hack so we assume 32 bit pixels which could be overgenerous. sourceWinWidth=(int)PsychGetWidthFromRect(sourceWinRec->rect); sourceWinHeight=(int)PsychGetWidthFromRect(sourceWinRec->rect); memorySizeBytes=sizeof(GLuint) * sourceWinWidth * sourceWinHeight; textureMemory=(GLuint *)malloc(memorySizeBytes); if(!textureMemory) PsychErrorExitMsg(PsychError_internal, "Failed to allocate surface memory\n"); //Now read the contents of the source window in to our memory with glReadPixels. //The pixel format in which we pack the pixels, specified using glPixelStorei() and glReadPixels() //arguments should agree with the format used by glTexImage2D when we turn it into a texture below. //glTextImage2D is constrained by the requirement that it math the video memory pixel format, //therefore there is really now choice here either. PsychSetGLContext(sourceWinRec); glReadBuffer(GL_FRONT); glPixelStorei(GL_PACK_ALIGNMENT, (GLint)sizeof(GLuint)); //fromInvertedY=fromWindowRect[kPsychBottom]-fromSampleRect[kPsychBottom]; //PsychGetPrecisionTimerSeconds(&t1); glReadPixels(0,0, sourceWinWidth, sourceWinHeight, GL_BGRA , GL_UNSIGNED_INT_8_8_8_8_REV, textureMemory); //PsychGetPrecisionTimerSeconds(&t2); //mexPrintf("glReadPixels took %f seconds\n", t2-t1); PsychTestForGLErrors(); CGLSetCurrentContext(NULL); //clear the current context. //Convert the bitmap which we created into into a texture held in "client storage" aka system memory. PsychSetGLContext(destWinRec); glEnable(GL_TEXTURE_RECTANGLE_EXT); glGenTextures(1, &myTexture); //create an index "name" for our texture glBindTexture(GL_TEXTURE_RECTANGLE_EXT, myTexture); //instantiate a texture of type associated with the index and set it to be the target for subsequent gl texture operators. //new //glTextureRangeAPPLE(GL_TEXTURE_RECTANGLE_EXT, 0, NULL); glTextureRangeAPPLE(GL_TEXTURE_RECTANGLE_EXT, 1 * 1600 * 1200 * 4, textureMemory); glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE , GL_STORAGE_SHARED_APPLE); //end new glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, 1); //tell gl how to unpack from our memory when creating a surface, namely don't really unpack it but use it for texture storage. glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //specify interpolation scaling rule for copying from texture. glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //specify interpolation scaling rule from copying from texture. glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); //PsychGetPrecisionTimerSeconds(&t1); glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, sourceWinWidth, sourceWinHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, textureMemory); //PsychGetPrecisionTimerSeconds(&t2); //mexPrintf("glTexImage2D took %f seconds\n", t2-t1); //Copy the texture to the display. What are the s and t indices of the first pixel of the texture ? 0 or 1 ? //set the GL context to be the onscreen window glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //how to combine the texture with the target. We could also alpha blend. PsychGetPrecisionTimerSeconds(&t1); glBegin(GL_QUADS); glTexCoord2d(0.0, 0.0); glVertex2d(0.0, 0.0); glTexCoord2d(sourceWinWidth, 0.0 ); glVertex2d(sourceWinWidth, 0.0); glTexCoord2d(sourceWinWidth, sourceWinHeight); glVertex2d(sourceWinWidth, sourceWinHeight); glTexCoord2d(0.0, sourceWinHeight); glVertex2d(0.0, sourceWinHeight); glEnd(); glFinish(); PsychGetPrecisionTimerSeconds(&t2); mexPrintf("TestTextureTwo took %f seconds\n", t2-t1); glDisable(GL_TEXTURE_RECTANGLE_EXT); //Close up shop. Unlike with normal textures is important to release the context before deallocating the memory which glTexImage2D() was given. //First release the GL context, then the CG context, then free the memory. #endif return(PsychError_none); }
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); }
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); }
PsychError PSYCHHIDKbCheck(void) { pRecDevice deviceRecord; pRecElement currentElement; int i, deviceIndex, numDeviceIndices; long KeysUsagePage=7; long KbDeviceUsagePage= 1, KbDeviceUsage=6; int deviceIndices[PSYCH_HID_MAX_KEYBOARD_DEVICES]; pRecDevice deviceRecords[PSYCH_HID_MAX_KEYBOARD_DEVICES]; boolean isDeviceSpecified, foundUserSpecifiedDevice, isKeyArgPresent, isTimeArgPresent; double *timeValueOutput, *isKeyDownOutput; PsychNativeBooleanType *keyArrayOutput; PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(3)); PsychErrorExit(PsychCapNumInputArgs(1)); //Specifies the number of the keyboard to scan. PsychHIDVerifyInit(); //Choose the device index and its record PsychHIDGetDeviceListByUsage(KbDeviceUsagePage, KbDeviceUsage, &numDeviceIndices, deviceIndices, deviceRecords); isDeviceSpecified=PsychCopyInIntegerArg(1, FALSE, &deviceIndex); if(isDeviceSpecified){ //make sure that the device number provided by the user is really a keyboard. for(i=0;i<numDeviceIndices;i++){ if(foundUserSpecifiedDevice=(deviceIndices[i]==deviceIndex)) break; } if(!foundUserSpecifiedDevice) PsychErrorExitMsg(PsychError_user, "Specified device number is not a keyboard device."); }else{ // set the keyboard device to be the first keyboard device i=0; if(numDeviceIndices==0) PsychErrorExitMsg(PsychError_user, "No keyboard devices detected."); else{ deviceIndex=deviceIndices[i]; } } deviceRecord=deviceRecords[i]; //Allocate and init out return arguments. isKeyArgPresent = PsychAllocOutBooleanMatArg(3, FALSE, 1, 256, 1, &keyArrayOutput); isTimeArgPresent = PsychAllocOutDoubleArg(2, FALSE, &timeValueOutput); PsychGetPrecisionTimerSeconds(timeValueOutput); PsychAllocOutDoubleArg(1, FALSE, &isKeyDownOutput); *isKeyDownOutput=(double)FALSE; //step through the elements of the device. Set flags in the return array for down keys. for(currentElement=HIDGetFirstDeviceElement(deviceRecord, kHIDElementTypeInput); currentElement != NULL; currentElement=HIDGetNextDeviceElement(currentElement, kHIDElementTypeInput)) { if(currentElement->usagePage==KeysUsagePage && currentElement->usage <= 256 && currentElement->usage >=1){ //printf("usage: %x value: %d \n", currentElement->usage, HIDGetElementValue(deviceRecord, currentElement)); keyArrayOutput[currentElement->usage - 1]=(PsychNativeBooleanType)(HIDGetElementValue(deviceRecord, currentElement) || keyArrayOutput[currentElement->usage - 1]); *isKeyDownOutput= keyArrayOutput[currentElement->usage - 1] || *isKeyDownOutput; } } return(PsychError_none); }