psych_bool PsychCopyOutColorArg(int position, psych_bool required, PsychColorType *color) { double *colorArgMat; if(color->mode == kPsychRGBAColor){ if(!PsychAllocOutDoubleMatArg(position, required, 1, 4, 0, &colorArgMat)) return(FALSE); colorArgMat[0] = (double)color->value.rgba.r; colorArgMat[1] = (double)color->value.rgba.g; colorArgMat[2] = (double)color->value.rgba.b; colorArgMat[3] = (double)color->value.rgba.a; return(TRUE); }else if(color->mode == kPsychRGBColor){ if(!PsychAllocOutDoubleMatArg(position, required, 1, 3, 0, &colorArgMat)) return(FALSE); colorArgMat[0] = (double)color->value.rgb.r; colorArgMat[1] = (double)color->value.rgb.g; colorArgMat[2] = (double)color->value.rgb.b; return(TRUE); }else if(color->mode == kPsychIndexColor){ if(!PsychAllocOutDoubleMatArg(position, required, 1, 1, 0, &colorArgMat)) return(FALSE); colorArgMat[0] = (double)color->value.index.i; return(TRUE); } PsychErrorExitMsg(PsychError_internal, "Unrecognized color mode"); return(FALSE); //make the compiler happy }
psych_bool PsychAllocOutRectArg(int position, psych_bool required, const double **rect) { double *rectArg; if(!PsychAllocOutDoubleMatArg(position, required, 1, 4, 0, &rectArg)) return(FALSE); //optional argument was omitted *rect=rectArg; return(TRUE); }
/* Accept a pointer to a Psychtoolbox rect specifier and return it from the module call in the specified argument position. The return value indicates whether the return argument was present. We could add a PsychAllocRectArg statement also. */ psych_bool PsychCopyOutRectArg(int position, psych_bool required, PsychRectType rect) { double *rectArgMat; if(!PsychAllocOutDoubleMatArg(position, required, 1, 4, 0, &rectArgMat)) return(FALSE); //optional argument was omitted memcpy(rectArgMat,rect,sizeof(PsychRectType)); return(TRUE); }
psych_bool PsychCopyOutDepthArg(int position, psych_bool required, PsychDepthType *depths) { double *depthsArray; int i; if(!PsychAllocOutDoubleMatArg(position, required, 1, PsychGetNumDepthsFromStruct(depths), 0, &depthsArray)) return(FALSE); //optional argument was omitted for(i=0;i<PsychGetNumDepthsFromStruct(depths);i++) depthsArray[i]=(double)PsychGetValueFromDepthStruct(i,depths); return(TRUE); }
PsychError SCREENWindows(void) { PsychWindowRecordType **windowRecordArray; int i,numWindows; double *windowPointers; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(0)); PsychCreateVolatileWindowRecordPointerList(&numWindows, &windowRecordArray); PsychAllocOutDoubleMatArg(1, FALSE, 1, numWindows, 0, &windowPointers); for(i=0;i<numWindows;i++) windowPointers[i]=windowRecordArray[i]->windowIndex; PsychDestroyVolatileWindowRecordPointerList(windowRecordArray); return(PsychError_none); }
PsychError PsychHIDOSKbCheck(int deviceIndex, double* scanList) { psych_uint8 keys[1024]; LPDIRECTINPUTDEVICE8 kb; unsigned int i, j; double* buttonStates; int keysdown; double timestamp; DWORD cbSize; if (deviceIndex == INT_MAX) { deviceIndex = PsychHIDGetDefaultKbQueueDevice(); // Ok, deviceIndex now contains our default keyboard to use - The first suitable keyboard. } if ((deviceIndex < 0) || (deviceIndex >= ndevices)) { // Out of range index: PsychErrorExitMsg(PsychError_user, "Invalid 'deviceIndex' specified. No such device!"); } // Get DirectInput keyboard device: kb = GetXDevice(deviceIndex); // Keyboard queue for this deviceIndex already exists? if (NULL == psychHIDKbQueueFirstPress[deviceIndex]) { // No. Create one which accepts all keys: PsychHIDOSKbQueueCreate(deviceIndex, 0, NULL); } // Keyboard queue for this device active? If not, we need // to start it: if (!psychHIDKbQueueActive[deviceIndex]) { // Keyboard idle: Need to start it: PsychHIDOSKbQueueStart(deviceIndex); // Startup to first key delivery takes time. Wait for // 50 msecs to be on the safe side: PsychYieldIntervalSeconds(0.050); } // Size of state structure is device dependent: switch (info[deviceIndex].dwDevType & 0xff) { case DI8DEVTYPE_KEYBOARD: cbSize = 256; break; case DI8DEVTYPE_MOUSE: case DI8DEVTYPE_SCREENPOINTER: cbSize = sizeof(DIMOUSESTATE2); break; case DI8DEVTYPE_JOYSTICK: cbSize = sizeof(DIJOYSTATE2); break; default: // Unkown device. Fail. cbSize = 0; } // Query current state snapshot of keyboard: memset(keys, 0, sizeof(keys)); if (DI_OK != kb->GetDeviceState(cbSize, (LPVOID) &keys[0])) { printf("PsychHID-ERROR: KbCheck for deviceIndex %i failed, because query of device failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "KbCheck failed!"); } // Request current time of query: PsychGetAdjustedPrecisionTimerSeconds(×tamp); // Reset overall key state to "none pressed": keysdown = 0; // Copy out timestamp: PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp); // Copy keyboard state: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates); for (i = 0; i < 256; i++) buttonStates[i] = 0; // Keyboard? if (cbSize == 256) { // Copy button state to output vector, apply scanlist mask, compute // resulting overall keysdown state. We ignore keyboard scancode zero and // start with 1 instead. We also ignore code 255. These are borderline codes // which may do weird things... for (i = 1; i < 255; i++) { // Compute target key slot for this scancode i: j = PsychHIDOSMapKey(i); // This key down? buttonStates[j] += (keys[i] > 0) ? 1 : 0; // Apply scanList mask, if any provided: if (scanList && (scanList[j] <= 0)) buttonStates[j] = 0; keysdown += (unsigned int) buttonStates[j]; } } // Joystick? if (cbSize == sizeof(DIJOYSTATE2)) { // Copy button state to output vector, apply scanlist mask, compute // resulting overall keysdown state. There are 128 buttons at an offset: for (i = (8 * sizeof(LONG) + 4 * sizeof(DWORD)); i < (8 * sizeof(LONG) + 4 * sizeof(DWORD)) + 128; i++) { // Compute target key slot for this scancode i: j = i - (8 * sizeof(LONG) + 4 * sizeof(DWORD)); // This key down? buttonStates[j] += (keys[i] > 0) ? 1 : 0; // Apply scanList mask, if any provided: if (scanList && (scanList[j] <= 0)) buttonStates[j] = 0; keysdown += (unsigned int) buttonStates[j]; } } // Mouse? if (cbSize == sizeof(DIMOUSESTATE2)) { // Copy button state to output vector, apply scanlist mask, compute // resulting overall keysdown state. There are 8 buttons at an offset: for (i = (3 * sizeof(LONG)); i < (3 * sizeof(LONG)) + 8; i++) { // Compute target key slot for this scancode i: j = i - (3 * sizeof(LONG)); // This key down? buttonStates[j] += (keys[i] > 0) ? 1 : 0; // Apply scanList mask, if any provided: if (scanList && (scanList[j] <= 0)) buttonStates[j] = 0; keysdown += (unsigned int) buttonStates[j]; } } // Copy out overall keystate: PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown > 0) ? 1 : 0); return(PsychError_none); }
void PsychHIDOSKbQueueCheck(int deviceIndex) { double *hasKeyBeenDownOutput, *firstPressTimeOutput, *firstReleaseTimeOutput, *lastPressTimeOutput, *lastReleaseTimeOutput; psych_bool isFirstPressSpecified, isFirstReleaseSpecified, isLastPressSpecified, isLastReleaseSpecified; int i; if (deviceIndex < 0) { deviceIndex = PsychHIDGetDefaultKbQueueDevice(); // Ok, deviceIndex now contains our default keyboard to use - The first suitable keyboard. } if ((deviceIndex < 0) || (deviceIndex >= ndevices)) { // Out of range index: PsychErrorExitMsg(PsychError_user, "Invalid 'deviceIndex' specified. No such device!"); } // Does Keyboard queue for this deviceIndex already exist? if (NULL == psychHIDKbQueueFirstPress[deviceIndex]) { // No. Bad bad... printf("PsychHID-ERROR: Tried to check non-existent keyboard queue for deviceIndex %i! Call KbQueueCreate first!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Invalid 'deviceIndex' specified. No queue for that device yet!"); } // Allocate output PsychAllocOutDoubleArg(1, kPsychArgOptional, &hasKeyBeenDownOutput); isFirstPressSpecified = PsychAllocOutDoubleMatArg(2, kPsychArgOptional, 1, 256, 1, &firstPressTimeOutput); isFirstReleaseSpecified = PsychAllocOutDoubleMatArg(3, kPsychArgOptional, 1, 256, 1, &firstReleaseTimeOutput); isLastPressSpecified = PsychAllocOutDoubleMatArg(4, kPsychArgOptional, 1, 256, 1, &lastPressTimeOutput); isLastReleaseSpecified = PsychAllocOutDoubleMatArg(5, kPsychArgOptional, 1, 256, 1, &lastReleaseTimeOutput); // Initialize output if(isFirstPressSpecified) memset((void*) firstPressTimeOutput, 0, sizeof(double) * 256); if(isFirstReleaseSpecified) memset((void*) firstReleaseTimeOutput, 0, sizeof(double) * 256); if(isLastPressSpecified) memset((void*) lastPressTimeOutput, 0, sizeof(double) * 256); if(isLastReleaseSpecified) memset((void*) lastReleaseTimeOutput, 0, sizeof(double) * 256); *hasKeyBeenDownOutput=0; // Compute and assign output: PsychLockMutex(&KbQueueMutex); for (i = 0; i < 256; i++) { double lastRelease = psychHIDKbQueueLastRelease[deviceIndex][i]; double lastPress = psychHIDKbQueueLastPress[deviceIndex][i]; double firstRelease = psychHIDKbQueueFirstRelease[deviceIndex][i]; double firstPress = psychHIDKbQueueFirstPress[deviceIndex][i]; if (firstPress) { *hasKeyBeenDownOutput=1; if(isFirstPressSpecified) firstPressTimeOutput[i] = firstPress; psychHIDKbQueueFirstPress[deviceIndex][i] = 0; } if (firstRelease) { if(isFirstReleaseSpecified) firstReleaseTimeOutput[i] = firstRelease; psychHIDKbQueueFirstRelease[deviceIndex][i] = 0; } if (lastPress) { if(isLastPressSpecified) lastPressTimeOutput[i] = lastPress; psychHIDKbQueueLastPress[deviceIndex][i] = 0; } if (lastRelease) { if(isLastReleaseSpecified) lastReleaseTimeOutput[i] = lastRelease; psychHIDKbQueueLastRelease[deviceIndex][i] = 0; } } PsychUnlockMutex(&KbQueueMutex); return; }
void PsychHIDOSKbQueueCheck(int deviceIndex) { double *hasKeyBeenDownOutput, *firstPressTimeOutput, *firstReleaseTimeOutput, *lastPressTimeOutput, *lastReleaseTimeOutput; psych_bool isFirstPressSpecified, isFirstReleaseSpecified, isLastPressSpecified, isLastReleaseSpecified; int i; // Get true keyboardqueue index assigned to deviceIndex from original user provided deviceIndex: deviceIndex = PsychHIDOSGetKbQueueDevice(deviceIndex, NULL); // Does Keyboard queue for this deviceIndex already exist? if (NULL == psychHIDKbQueueFirstPress[deviceIndex]) { // No. Bad bad... printf("PsychHID-ERROR: Tried to check non-existent keyboard queue for deviceIndex %i! Call KbQueueCreate first!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Invalid keyboard 'deviceIndex' specified. No queue for that device yet!"); } // Allocate output PsychAllocOutDoubleArg(1, FALSE, &hasKeyBeenDownOutput); isFirstPressSpecified = PsychAllocOutDoubleMatArg(2, FALSE, 1, 256, 1, &firstPressTimeOutput); isFirstReleaseSpecified = PsychAllocOutDoubleMatArg(3, FALSE, 1, 256, 1, &firstReleaseTimeOutput); isLastPressSpecified = PsychAllocOutDoubleMatArg(4, FALSE, 1, 256, 1, &lastPressTimeOutput); isLastReleaseSpecified = PsychAllocOutDoubleMatArg(5, FALSE, 1, 256, 1, &lastReleaseTimeOutput); // Initialize output if(isFirstPressSpecified) memset((void*) firstPressTimeOutput, 0, sizeof(double) * 256); if(isFirstReleaseSpecified) memset((void*) firstReleaseTimeOutput, 0, sizeof(double) * 256); if(isLastPressSpecified) memset((void*) lastPressTimeOutput, 0, sizeof(double) * 256); if(isLastReleaseSpecified) memset((void*) lastReleaseTimeOutput, 0, sizeof(double) * 256); *hasKeyBeenDownOutput=0; // Compute and assign output: PsychLockMutex(&KbQueueMutex); for (i = 0; i < 256; i++) { double lastRelease = psychHIDKbQueueLastRelease[deviceIndex][i]; double lastPress = psychHIDKbQueueLastPress[deviceIndex][i]; double firstRelease = psychHIDKbQueueFirstRelease[deviceIndex][i]; double firstPress = psychHIDKbQueueFirstPress[deviceIndex][i]; if (firstPress) { *hasKeyBeenDownOutput=1; if(isFirstPressSpecified) firstPressTimeOutput[i] = firstPress; psychHIDKbQueueFirstPress[deviceIndex][i] = 0; } if (firstRelease) { if(isFirstReleaseSpecified) firstReleaseTimeOutput[i] = firstRelease; psychHIDKbQueueFirstRelease[deviceIndex][i] = 0; } if (lastPress) { if(isLastPressSpecified) lastPressTimeOutput[i] = lastPress; psychHIDKbQueueLastPress[deviceIndex][i] = 0; } if (lastRelease) { if(isLastReleaseSpecified) lastReleaseTimeOutput[i] = lastRelease; psychHIDKbQueueLastRelease[deviceIndex][i] = 0; } } PsychUnlockMutex(&KbQueueMutex); return; }
PsychError IOPORTRead(void) { static char useString[] = "[data, when, errmsg] = IOPort('Read', handle [, blocking=0] [, amount]);"; static char synopsisString[] = "Read data from device, specified by 'handle'.\n" "Returned 'data' will be a row vector of read data bytes. 'when' will be a receive " "timestamp of when the data read was complete. 'errmsg' will be a human readable " "char string with an error message if any error occured, otherwise an empty string. " "The optional flag 'blocking' if set to 0 will ask the read function to not block, " "but return immediately: The read function will return whatever amount of data is " "currently available in the internal input queue, but at most 'amount' bytes if 'amount' " "is specified. If no data is available, it will return an empty matrix. This is the default.\n" "If 'blocking' is set to 1, you must specify the 'amount' of bytes to receive and the " "function will wait until that exact amount of data is available, then return it." "Even in blocking mode, the function will return if no data becomes available within " "the time period specified by the configuration setting 'ReadTimeout' (see help for " "'OpenSerialPort' for description). In some situations, the read function may return " "less data than requested, e.g., in specific error cases, where a read could only " "get partially satisfied."; static char seeAlsoString[] = "'Write', 'OpenSerialPort', 'ConfigureSerialPort'"; char errmsg[1024]; int handle, blocking, nread, amount, i; psych_uint8* readbuffer; double* outbuffer; double timestamp; errmsg[0] = 0; // Setup online help: PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none); }; PsychErrorExit(PsychCapNumInputArgs(3)); // The maximum number of inputs PsychErrorExit(PsychRequireNumInputArgs(1)); // The required number of inputs PsychErrorExit(PsychCapNumOutputArgs(3)); // The maximum number of outputs // Get required port handle: PsychCopyInIntegerArg(1, kPsychArgRequired, &handle); // Get optional blocking flag: Defaults to 0 -- non-blocking. blocking = 0; PsychCopyInIntegerArg(2, kPsychArgOptional, &blocking); // Get optional maximum or exact amount to read: amount = INT_MAX; if (!PsychCopyInIntegerArg(3, kPsychArgOptional, &amount)) { // Not spec'd: if (blocking > 0) PsychErrorExitMsg(PsychError_user, "When issuing a 'Read' in blocking mode, you must specify the exact 'amount' to read, but 'amount' was omitted!"); } if (amount < 0) PsychErrorExitMsg(PsychError_user, "Invalid (negative) 'amount' of data to read!"); // Read data: nread = PsychReadIOPort(handle, (void**) &readbuffer, amount, blocking, errmsg, ×tamp); // Allocate outbuffer of proper size: PsychAllocOutDoubleMatArg(1, kPsychArgOptional, 1, ((nread >=0) ? nread : 0), 1, &outbuffer); // Copy to outbuffer: We copy our uint8 values to a double matrix. This is arguably a bit of a waste of // memory and bandwidth, but it simplifies interfacing with Octave, old Matlab versions and possible // future runtime environments a lot: if (nread > 0) for (i=0; i<nread; i++) outbuffer[i] = readbuffer[i]; if (nread < 0 && verbosity > 0) printf("IOPort: Error: %s\n", errmsg); // Return timestamp and errmsg, if any: PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp); PsychCopyOutCharArg(3, kPsychArgOptional, errmsg); return(PsychError_none); }
PsychError SCREENLoadCLUT(void) { int i, screenNumber, numEntries, inM, inN, inP, start, bits; float *outRedTable, *outGreenTable, *outBlueTable, *inRedTable, *inGreenTable, *inBlueTable; double *inTable, *outTable, maxval; psych_bool isclutprovided; start = 0; bits = 8; //all subfunctions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(4)); // Read in the screen number: PsychCopyInScreenNumberArg(1, TRUE, &screenNumber); // Read in optional start index: PsychCopyInIntegerArg(3, FALSE, &start); if (start<0 || start>255) { PsychErrorExitMsg(PsychError_user, "Argument startEntry must be between zero and 255."); } // Read in optional bits argument: PsychCopyInIntegerArg(4, FALSE, &bits); if (bits<1 || bits>16) { PsychErrorExitMsg(PsychError_user, "Argument 'bits' must be between 1 and 16."); } // Compute allowable maxval: maxval=(double) ((1 << bits) - 1); // First read the existing gamma table so we can return it. PsychReadNormalizedGammaTable(screenNumber, &numEntries, &outRedTable, &outGreenTable, &outBlueTable); // Load and sanity check the input matrix, and convert from float to doubles: isclutprovided = PsychAllocInDoubleMatArg(2, FALSE, &inM, &inN, &inP, &inTable); if (isclutprovided) { if((inM > 256 - start) || (inM < 1) || (inN != 3) || (inP != 1)) PsychErrorExitMsg(PsychError_user, "The provided CLUT table must have a size between 1 and (256 - startEntry) rows and 3 columns."); inRedTable=PsychMallocTemp(sizeof(float) * 256); inGreenTable=PsychMallocTemp(sizeof(float) * 256); inBlueTable=PsychMallocTemp(sizeof(float) * 256); // Copy the table into the new inTable array: for(i=0; i<numEntries; i++) { inRedTable[i] = outRedTable[i]; inGreenTable[i] = outGreenTable[i]; inBlueTable[i] = outBlueTable[i]; } } // Allocate output array: PsychAllocOutDoubleMatArg(1, FALSE, numEntries, 3, 0, &outTable); // Copy read table into output array, scale it by maxval to map range 0.0-1.0 to 0-maxval: for(i=0;i<numEntries;i++){ outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 0, 0)]=(double) outRedTable[i] * maxval; outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 1, 0)]=(double) outGreenTable[i] * maxval; outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 2, 0)]=(double) outBlueTable[i] * maxval; } if (isclutprovided) { // Now we can overwrite entries 'start' to start+inM of inTable with the user provided table values. We // need to scale the users values down from 0-maxval to 0.0-1.0: for(i=start; (i<256) && (i-start < inM); i++){ inRedTable[i] = (float) (inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i-start, 0, 0)] / maxval); inGreenTable[i] = (float) (inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i-start, 1, 0)] / maxval); inBlueTable[i] = (float) (inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i-start, 2, 0)] / maxval); // Range check: if(inRedTable[i]>1 || inRedTable[i]< 0 || inGreenTable[i] > 1 || inGreenTable[i] < 0 || inBlueTable[i] >1 || inBlueTable[i] < 0) { printf("PTB-ERROR: At least one of the CLUT values in row %i is outside the valid range of 0 to %i!\n", i-start+1, ((1 << bits) - 1)); PsychErrorExitMsg(PsychError_user, "Tried to set a CLUT with invalid entries."); } } // Now set the new gamma table PsychLoadNormalizedGammaTable(screenNumber, numEntries, inRedTable, inGreenTable, inBlueTable); } return(PsychError_none); }
PsychError SCREENBlendFunction(void) { PsychWindowRecordType *windowRecord; GLenum oldSource, oldDestination, newSource, newDestination; char *oldSoureStr, *oldDestinationStr, *newSourceStr, *newDestinationStr; int oldSourceStrSize, oldDestinationStrSize, isSourceStringValid, isDestinationStringValid; psych_bool isSourceSupplied, isDestinationSupplied, isSourceChoiceValid, isDestinationChoiceValid; double *oldColorMask, *newColorMask; int m, n, p; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumInputArgs(4)); //The maximum number of inputs PsychErrorExit(PsychRequireNumInputArgs(1)); //The required number of inputs PsychErrorExit(PsychCapNumOutputArgs(3)); //The maximum number of outputs //Get the window record or exit with an error if the windex was bogus. PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); //Retreive the old source and destination factors and return them from the Screen call as strings PsychGetAlphaBlendingFactorsFromWindow(windowRecord, &oldSource, &oldDestination); oldSourceStrSize=PsychGetAlphaBlendingFactorStringFromConstant(oldSource, NULL); oldDestinationStrSize=PsychGetAlphaBlendingFactorStringFromConstant(oldDestination, NULL); oldSoureStr=(char *)malloc(sizeof(char) * oldSourceStrSize); oldDestinationStr=(char *)malloc(sizeof(char) * oldDestinationStrSize); PsychGetAlphaBlendingFactorStringFromConstant(oldSource, oldSoureStr); PsychGetAlphaBlendingFactorStringFromConstant(oldDestination, oldDestinationStr); PsychCopyOutCharArg(1, kPsychArgOptional, oldSoureStr); PsychCopyOutCharArg(2, kPsychArgOptional, oldDestinationStr); free((void *)oldSoureStr); free((void *)oldDestinationStr); //Get the new settings if they are present and set them. newSource=oldSource; newDestination=oldDestination; isSourceSupplied= PsychAllocInCharArg(2, kPsychArgOptional, &newSourceStr); isDestinationSupplied= PsychAllocInCharArg(3, kPsychArgOptional, &newDestinationStr); if(isSourceSupplied){ isSourceStringValid=PsychGetAlphaBlendingFactorConstantFromString(newSourceStr, &newSource); if(!isSourceStringValid) PsychErrorExitMsg(PsychError_user, "Supplied string argument 'sourceFactorNew' is invalid"); isSourceChoiceValid=PsychValidateBlendingConstantForSource(newSource); if(!isSourceChoiceValid) PsychErrorExitMsg(PsychError_user, "The blending factor supplied for the source is only valid only for the destination"); } if(isDestinationSupplied){ isDestinationStringValid=PsychGetAlphaBlendingFactorConstantFromString(newDestinationStr, &newDestination); if(!isDestinationStringValid) PsychErrorExitMsg(PsychError_user, "Supplied string argument 'destinationFactorNew' is invalid"); isDestinationChoiceValid=PsychValidateBlendingConstantForDestination(newDestination); if(!isDestinationChoiceValid) PsychErrorExitMsg(PsychError_user, "The blending factor supplied for the destination is only valid only for the source"); } PsychStoreAlphaBlendingFactorsForWindow(windowRecord, newSource, newDestination); // Check if alpha blending is possible for this windowRecord: if ((newSource != GL_ONE || newDestination != GL_ZERO) && !((windowRecord->bpc < 16) || (windowRecord->bpc == 16 && (windowRecord->gfxcaps & kPsychGfxCapFPBlend16)) || (windowRecord->bpc == 32 && (windowRecord->gfxcaps & kPsychGfxCapFPBlend32)))) { // Nope. Alpha blending requested but not possible for this windowRecord with this gfx-hardware. if (PsychPrefStateGet_Verbosity() > 1) { printf("PTB-WARNING: Screen('Blendfunction') called to enable alpha-blending on a window (handle=%i) which doesn't support\n", windowRecord->windowIndex); printf("PTB-WARNING: alpha-blending at its current color resolution of %i bits per color component on your hardware.\n", windowRecord->bpc); printf("PTB-WARNING: Won't enable blending. Either lower the color resolution of the window (see help PsychImaging) or\n"); printf("PTB-WARNING: upgrade your graphics hardware.\n\n"); } } // Create return array with encoded old colormask: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, 1, 4, 1, &oldColorMask); oldColorMask[0] = (windowRecord->colorMask[0]) ? 1 : 0; oldColorMask[1] = (windowRecord->colorMask[1]) ? 1 : 0; oldColorMask[2] = (windowRecord->colorMask[2]) ? 1 : 0; oldColorMask[3] = (windowRecord->colorMask[3]) ? 1 : 0; // Any new colormask provided? if (PsychAllocInDoubleMatArg(4, kPsychArgOptional, &m, &n, &p, &newColorMask)) { // Yes. Assign it: if (p!=1 || m*n != 4) PsychErrorExitMsg(PsychError_user, "The colorMaskNew argument must be a 4 element row- or column vector!"); windowRecord->colorMask[0] = (newColorMask[0] > 0) ? GL_TRUE : GL_FALSE; windowRecord->colorMask[1] = (newColorMask[1] > 0) ? GL_TRUE : GL_FALSE; windowRecord->colorMask[2] = (newColorMask[2] > 0) ? GL_TRUE : GL_FALSE; windowRecord->colorMask[3] = (newColorMask[3] > 0) ? GL_TRUE : GL_FALSE; } return(PsychError_none); }
// This also works as 'AddFrameToMovie', as almost all code is shared with 'GetImage'. // Only difference is where the fetched pixeldata is sent: To the movie encoder or to // a matlab/octave matrix. PsychError SCREENGetImage(void) { PsychRectType windowRect, sampleRect; int nrchannels, invertedY, stride; size_t ix, iy, sampleRectWidth, sampleRectHeight, redReturnIndex, greenReturnIndex, blueReturnIndex, alphaReturnIndex, planeSize; int viewid = 0; psych_uint8 *returnArrayBase, *redPlane; float *dredPlane; double *returnArrayBaseDouble; PsychWindowRecordType *windowRecord; GLboolean isDoubleBuffer, isStereo; char* buffername = NULL; psych_bool floatprecision = FALSE; GLenum whichBuffer = 0; int frameduration = 1; int moviehandle = 0; unsigned int twidth, theight, numChannels, bitdepth; unsigned char* framepixels; psych_bool isOES; // Called as 2nd personality "AddFrameToMovie" ? psych_bool isAddMovieFrame = PsychMatch(PsychGetFunctionName(), "AddFrameToMovie"); // All sub functions should have these two lines if (isAddMovieFrame) { PsychPushHelp(useString2, synopsisString2, seeAlsoString); } else { PsychPushHelp(useString, synopsisString, seeAlsoString); } if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the numbers of inputs and outputs PsychErrorExit(PsychCapNumInputArgs(5)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(1)); //The maximum number of outputs // Get windowRecord for this window: PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); // Embedded subset has very limited support for readback formats : isOES = PsychIsGLES(windowRecord); // Make sure we don't execute on an onscreen window with pending async flip, as this would interfere // by touching the system backbuffer -> Impaired timing of the flip thread and undefined readback // of image data due to racing with the ops of the flipperthread on the same drawable. // // Note: It would be possible to allow drawBuffer readback if the drawBuffer is not multi-sampled // or if we can safely multisample-resolve without touching the backbuffer, but checking for all // special cases adds ugly complexity and is not really worth the effort, so we don't allow this. // // If this passes then PsychSetDrawingTarget() below will trigger additional validations to check // if execution of 'GetImage' is allowed under the current conditions for offscreen windows and // textures: if (PsychIsOnscreenWindow(windowRecord) && (windowRecord->flipInfo->asyncstate > 0)) { PsychErrorExitMsg(PsychError_user, "Calling this function on an onscreen window with a pending asynchronous flip is not allowed!"); } // Set window as drawingtarget: Even important if this binding is changed later on! // We need to make sure all needed transitions are done - esp. in non-imaging mode, // so backbuffer is in a useable state: PsychSetDrawingTarget(windowRecord); // Disable shaders: PsychSetShader(windowRecord, 0); // Soft-Reset drawingtarget. This is important to make sure no FBO's are bound, // otherwise the following glGets for GL_DOUBLEBUFFER and GL_STEREO will retrieve // wrong results, leading to totally wrong read buffer assignments down the road!! PsychSetDrawingTarget((PsychWindowRecordType*) 0x1); // Queries only available on desktop OpenGL: if (!isOES) { glGetBooleanv(GL_DOUBLEBUFFER, &isDoubleBuffer); glGetBooleanv(GL_STEREO, &isStereo); } else { // Make something reasonable up: isStereo = FALSE; isDoubleBuffer = TRUE; } // Force "quad-buffered" stereo mode if our own homegrown implementation is active: if (windowRecord->stereomode == kPsychFrameSequentialStereo) isStereo = TRUE; // Assign read buffer: if(PsychIsOnscreenWindow(windowRecord)) { // Onscreen window: We read from the front- or front-left buffer by default. // This works on single-buffered and double buffered contexts in a consistent fashion: // Copy in optional override buffer name: PsychAllocInCharArg(3, FALSE, &buffername); // Override buffer name provided? if (buffername) { // Which one is it? // "frontBuffer" is always a valid choice: if (PsychMatch(buffername, "frontBuffer")) whichBuffer = GL_FRONT; // Allow selection of left- or right front stereo buffer in stereo mode: if (PsychMatch(buffername, "frontLeftBuffer") && isStereo) whichBuffer = GL_FRONT_LEFT; if (PsychMatch(buffername, "frontRightBuffer") && isStereo) whichBuffer = GL_FRONT_RIGHT; // Allow selection of backbuffer in double-buffered mode: if (PsychMatch(buffername, "backBuffer") && isDoubleBuffer) whichBuffer = GL_BACK; // Allow selection of left- or right back stereo buffer in stereo mode: if (PsychMatch(buffername, "backLeftBuffer") && isStereo && isDoubleBuffer) whichBuffer = GL_BACK_LEFT; if (PsychMatch(buffername, "backRightBuffer") && isStereo && isDoubleBuffer) whichBuffer = GL_BACK_RIGHT; // Allow AUX buffer access for debug purposes: if (PsychMatch(buffername, "aux0Buffer")) whichBuffer = GL_AUX0; if (PsychMatch(buffername, "aux1Buffer")) whichBuffer = GL_AUX1; if (PsychMatch(buffername, "aux2Buffer")) whichBuffer = GL_AUX2; if (PsychMatch(buffername, "aux3Buffer")) whichBuffer = GL_AUX3; // If 'drawBuffer' is requested, but imaging pipeline inactive, ie., there is no real 'drawBuffer', then we // map this to the backbuffer, as on a non-imaging configuration, the backbuffer is pretty much exactly the // equivalent of the 'drawBuffer': if (PsychMatch(buffername, "drawBuffer") && !(windowRecord->imagingMode & kPsychNeedFastBackingStore)) whichBuffer = GL_BACK; } else { // Default is frontbuffer: whichBuffer = GL_FRONT; } } else { // Offscreen window or texture: They only have one buffer, which is the // backbuffer in double-buffered mode and the frontbuffer in single buffered mode: whichBuffer=(isDoubleBuffer) ? GL_BACK : GL_FRONT; } // Enable this windowRecords framebuffer as current drawingtarget. This should // also allow us to "GetImage" from Offscreen windows: if ((windowRecord->imagingMode & kPsychNeedFastBackingStore) || (windowRecord->imagingMode & kPsychNeedFastOffscreenWindows)) { // Special case: Imaging pipeline active - We need to activate system framebuffer // so we really read the content of the framebuffer and not of some FBO: if (PsychIsOnscreenWindow(windowRecord)) { // It's an onscreen window: // Homegrown frame-sequential stereo active? Need to remap some stuff: if (windowRecord->stereomode == kPsychFrameSequentialStereo) { // Back/Front buffers map to backleft/frontleft buffers: if (whichBuffer == GL_BACK) whichBuffer = GL_BACK_LEFT; if (whichBuffer == GL_FRONT) whichBuffer = GL_FRONT_LEFT; // Special case: Want to read from stereo front buffer? if ((whichBuffer == GL_FRONT_LEFT) || (whichBuffer == GL_FRONT_RIGHT)) { // These don't really exist in our homegrown implementation. Their equivalents are the // regular system front/backbuffers. Due to the bufferswaps happening every video // refresh cycle and the complex logic on when and how to blit finalizedFBOs into // the system buffers and the asynchronous execution of the parallel flipper thread, // we don't know which buffer (GL_BACK or GL_FRONT) corresponds to the leftFront or // rightFront buffer. Let's be stupid and just return the current front buffer for // FRONT_LEFT and the current back buffer for FRONT_RIGHT, but warn user about the // ambiguity: whichBuffer = (whichBuffer == GL_FRONT_LEFT) ? GL_FRONT : GL_BACK; if (PsychPrefStateGet_Verbosity() > 2) { printf("PTB-WARNING: In Screen('GetImage'): You selected retrieval of one of the stereo front buffers, while our homegrown frame-sequential\n"); printf("PTB-WARNING: In Screen('GetImage'): stereo display mode is active. This will impair presentation timing and may cause flicker. The\n"); printf("PTB-WARNING: In Screen('GetImage'): mapping of 'frontLeftBuffer' and 'frontRightBuffer' to actual stimulus content is very ambiguous\n"); printf("PTB-WARNING: In Screen('GetImage'): in this mode. You may therefore end up with the content of the wrong buffer returned! Check results\n"); printf("PTB-WARNING: In Screen('GetImage'): carefully! Better read from 'backLeftBuffer' or 'backRightBuffer' for well defined results.\n\n"); } } } // Homegrown frame-sequential stereo active and backleft or backright buffer requested? if (((whichBuffer == GL_BACK_LEFT) || (whichBuffer == GL_BACK_RIGHT)) && (windowRecord->stereomode == kPsychFrameSequentialStereo)) { // We can get the equivalent of the backLeft/RightBuffer from the finalizedFBO's in this mode. Get their content: viewid = (whichBuffer == GL_BACK_RIGHT) ? 1 : 0; whichBuffer = GL_COLOR_ATTACHMENT0_EXT; // Bind finalizedFBO as framebuffer to read from: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->finalizedFBO[viewid]]->fboid); // Make sure binding gets released at end of routine: viewid = -1; } // No frame-sequential stereo: Full imaging pipeline active and one of the drawBuffer's requested? else if (buffername && (PsychMatch(buffername, "drawBuffer")) && (windowRecord->imagingMode & kPsychNeedFastBackingStore)) { // Activate drawBufferFBO: PsychSetDrawingTarget(windowRecord); whichBuffer = GL_COLOR_ATTACHMENT0_EXT; // Is the drawBufferFBO multisampled? viewid = (((windowRecord->stereomode > 0) && (windowRecord->stereodrawbuffer == 1)) ? 1 : 0); if (windowRecord->fboTable[windowRecord->drawBufferFBO[viewid]]->multisample > 0) { // It is! We can't read from a multisampled FBO. Need to perform a multisample resolve operation and read // from the resolved unisample buffer instead. This is only safe if the unisample buffer is either a dedicated // FBO, or - in case its the final system backbuffer etc. - if preflip operations haven't been performed yet. // If non dedicated buffer (aka finalizedFBO) and preflip ops have already happened, then the backbuffer contains // final content for an upcoming Screen('Flip') and we can't use (and therefore taint) that buffer. if ((windowRecord->inputBufferFBO[viewid] == windowRecord->finalizedFBO[viewid]) && (windowRecord->backBufferBackupDone)) { // Target for resolve is finalized FBO (probably system backbuffer) and preflip ops have run already. We // can't do the resolve op, as this would screw up the backbuffer with the final stimulus: printf("PTB-ERROR: Tried to 'GetImage' from a multisampled 'drawBuffer', but can't perform anti-aliasing pass due to\n"); printf("PTB-ERROR: lack of a dedicated resolve buffer.\n"); printf("PTB-ERROR: You can get what you wanted by either one of two options:\n"); printf("PTB-ERROR: Either enable a processing stage in the imaging pipeline, even if you don't need it, e.g., by setting\n"); printf("PTB-ERROR: the imagingmode argument in the 'OpenWindow' call to kPsychNeedImageProcessing. This will create a\n"); printf("PTB-ERROR: suitable resolve buffer. Or place the 'GetImage' call before any Screen('DrawingFinished') call, then\n"); printf("PTB-ERROR: i can (ab-)use the system backbuffer as a temporary resolve buffer.\n\n"); PsychErrorExitMsg(PsychError_user, "Tried to 'GetImage' from a multi-sampled 'drawBuffer'. Unsupported operation under given conditions."); } else { // Ok, the inputBufferFBO is a suitable temporary resolve buffer. Perform a multisample resolve blit to it: // A simple glBlitFramebufferEXT() call will do the copy & downsample operation: glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->drawBufferFBO[viewid]]->fboid); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->fboid); glBlitFramebufferEXT(0, 0, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->width, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->height, 0, 0, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->width, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->height, GL_COLOR_BUFFER_BIT, GL_NEAREST); // Bind inputBuffer as framebuffer: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->fboid); viewid = -1; } } } else { // No: Activate system framebuffer: PsychSetDrawingTarget(NULL); } } else { // Offscreen window or texture: Select drawing target as usual, // but set color attachment as read buffer: PsychSetDrawingTarget(windowRecord); whichBuffer = GL_COLOR_ATTACHMENT0_EXT; // We do not support multisampled readout: if (windowRecord->fboTable[windowRecord->drawBufferFBO[0]]->multisample > 0) { printf("PTB-ERROR: You tried to Screen('GetImage', ...); from an offscreen window or texture which has multisample anti-aliasing enabled.\n"); printf("PTB-ERROR: This operation is not supported. You must first use Screen('CopyWindow') to create a non-multisampled copy of the\n"); printf("PTB-ERROR: texture or offscreen window, then use 'GetImage' on that copy. The copy will be anti-aliased, so you'll get what you\n"); printf("PTB-ERROR: wanted with a bit more effort. Sorry for the inconvenience, but this is mostly a hardware limitation.\n\n"); PsychErrorExitMsg(PsychError_user, "Tried to 'GetImage' from a multi-sampled texture or offscreen window. Unsupported operation."); } } } else { // Normal case: No FBO based imaging - Select drawing target as usual: PsychSetDrawingTarget(windowRecord); } if (!isOES) { // Select requested read buffer, after some double-check: if (whichBuffer == 0) PsychErrorExitMsg(PsychError_user, "Invalid or unknown 'bufferName' argument provided."); glReadBuffer(whichBuffer); if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: In Screen('GetImage'): GL-Readbuffer whichBuffer = %i\n", whichBuffer); } else { // OES: No way to select readbuffer, it is "hard-coded" by system spec, depending // on framebuffer. For bound FBO, always color attachment zero, for system framebuffer, // always front buffer on single-buffered setup, back buffer on double-buffered setup: if (buffername && PsychIsOnscreenWindow(windowRecord) && (whichBuffer != GL_COLOR_ATTACHMENT0_EXT)) { // Some part of the real system framebuffer of an onscreen window explicitely requested. if ((windowRecord->windowType == kPsychSingleBufferOnscreen) && (whichBuffer != GL_FRONT) && (PsychPrefStateGet_Verbosity() > 1)) { printf("PTB-WARNING: Tried to Screen('GetImage') single-buffered framebuffer '%s', but only 'frontBuffer' supported on OpenGL-ES. Returning that instead.\n", buffername); } if ((windowRecord->windowType == kPsychDoubleBufferOnscreen) && (whichBuffer != GL_BACK) && (PsychPrefStateGet_Verbosity() > 1)) { printf("PTB-WARNING: Tried to Screen('GetImage') double-buffered framebuffer '%s', but only 'backBuffer' supported on OpenGL-ES. Returning that instead.\n", buffername); } } } if (whichBuffer == GL_COLOR_ATTACHMENT0_EXT) { // FBO of texture / offscreen window / onscreen drawBuffer/inputBuffer // has size of clientrect -- potentially larger or smaller than backbuffer: PsychCopyRect(windowRect, windowRecord->clientrect); } else { // Non-FBO backed texture / offscreen window / onscreen window has size // of raw rect (==clientrect for non-onscreen, == backbuffer size for onscreen): PsychCopyRect(windowRect, windowRecord->rect); } // Retrieve optional read rectangle: if(!PsychCopyInRectArg(2, FALSE, sampleRect)) PsychCopyRect(sampleRect, windowRect); if (IsPsychRectEmpty(sampleRect)) return(PsychError_none); // Compute sampling rectangle: if ((PsychGetWidthFromRect(sampleRect) >= INT_MAX) || (PsychGetHeightFromRect(sampleRect) >= INT_MAX)) { PsychErrorExitMsg(PsychError_user, "Too big 'rect' argument provided. Both width and height of the rect must not exceed 2^31 pixels!"); } sampleRectWidth = (size_t) PsychGetWidthFromRect(sampleRect); sampleRectHeight= (size_t) PsychGetHeightFromRect(sampleRect); // Regular image fetch to runtime, or adding to a movie? if (!isAddMovieFrame) { // Regular fetch: // Get optional floatprecision flag: We return data with float-precision if // this flag is set. By default we return uint8 data: PsychCopyInFlagArg(4, FALSE, &floatprecision); // Get the optional number of channels flag: By default we return 3 channels, // the Red, Green, and blue color channel: nrchannels = 3; PsychCopyInIntegerArg(5, FALSE, &nrchannels); if (nrchannels < 1 || nrchannels > 4) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' must be between 1 and 4!"); if (!floatprecision) { // Readback of standard 8bpc uint8 pixels: // No Luminance + Alpha on OES: if (isOES && (nrchannels == 2)) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' == 2 not supported on OpenGL-ES!"); PsychAllocOutUnsignedByteMatArg(1, TRUE, (int) sampleRectHeight, (int) sampleRectWidth, (int) nrchannels, &returnArrayBase); if (isOES) { // We only do RGBA reads on OES, then discard unwanted stuff ourselves: redPlane = (psych_uint8*) PsychMallocTemp((size_t) 4 * sampleRectWidth * sampleRectHeight); } else { redPlane = (psych_uint8*) PsychMallocTemp((size_t) nrchannels * sampleRectWidth * sampleRectHeight); } planeSize = sampleRectWidth * sampleRectHeight; glPixelStorei(GL_PACK_ALIGNMENT,1); invertedY = (int) (windowRect[kPsychBottom] - sampleRect[kPsychBottom]); if (isOES) { glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_UNSIGNED_BYTE, redPlane); stride = 4; } else { stride = nrchannels; if (nrchannels==1) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RED, GL_UNSIGNED_BYTE, redPlane); if (nrchannels==2) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, redPlane); if (nrchannels==3) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGB, GL_UNSIGNED_BYTE, redPlane); if (nrchannels==4) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_UNSIGNED_BYTE, redPlane); } //in one pass transpose and flip what we read with glReadPixels before returning. //-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns. //-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left. for(ix=0; ix < sampleRectWidth; ix++){ for(iy=0; iy < sampleRectHeight; iy++){ // Compute write-indices for returned data: redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0); greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 1); blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 2); alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 3); // Always return RED/LUMINANCE channel: returnArrayBase[redReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 0]; // Other channels on demand: if (nrchannels>1) returnArrayBase[greenReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 1]; if (nrchannels>2) returnArrayBase[blueReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 2]; if (nrchannels>3) returnArrayBase[alphaReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 3]; } } } else { // Readback of standard 32bpc float pixels into a double matrix: // No Luminance + Alpha on OES: if (isOES && (nrchannels == 2)) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' == 2 not supported on OpenGL-ES!"); // Only float readback on floating point FBO's with EXT_color_buffer_float support: if (isOES && ((whichBuffer != GL_COLOR_ATTACHMENT0_EXT) || (windowRecord->bpc < 16) || !glewIsSupported("GL_EXT_color_buffer_float"))) { printf("PTB-ERROR: Tried to 'GetImage' pixels in floating point format from a non-floating point surface, or not supported by your hardware.\n"); PsychErrorExitMsg(PsychError_user, "'GetImage' of floating point values from given object not supported on OpenGL-ES!"); } PsychAllocOutDoubleMatArg(1, TRUE, (int) sampleRectHeight, (int) sampleRectWidth, (int) nrchannels, &returnArrayBaseDouble); if (isOES) { dredPlane = (float*) PsychMallocTemp((size_t) 4 * sizeof(float) * sampleRectWidth * sampleRectHeight); stride = 4; } else { dredPlane = (float*) PsychMallocTemp((size_t) nrchannels * sizeof(float) * sampleRectWidth * sampleRectHeight); stride = nrchannels; } planeSize = sampleRectWidth * sampleRectHeight * sizeof(float); glPixelStorei(GL_PACK_ALIGNMENT, 1); invertedY = (int) (windowRect[kPsychBottom]-sampleRect[kPsychBottom]); if (!isOES) { if (nrchannels==1) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RED, GL_FLOAT, dredPlane); if (nrchannels==2) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_LUMINANCE_ALPHA, GL_FLOAT, dredPlane); if (nrchannels==3) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGB, GL_FLOAT, dredPlane); if (nrchannels==4) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_FLOAT, dredPlane); } else { glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_FLOAT, dredPlane); } //in one pass transpose and flip what we read with glReadPixels before returning. //-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns. //-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left. for(ix=0; ix < sampleRectWidth; ix++){ for(iy=0; iy < sampleRectHeight; iy++){ // Compute write-indices for returned data: redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0); greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 1); blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 2); alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 3); // Always return RED/LUMINANCE channel: returnArrayBaseDouble[redReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 0]; // Other channels on demand: if (nrchannels>1) returnArrayBaseDouble[greenReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 1]; if (nrchannels>2) returnArrayBaseDouble[blueReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 2]; if (nrchannels>3) returnArrayBaseDouble[alphaReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 3]; } } } } if (isAddMovieFrame) { // Adding of image to a movie requested: // Get optional moviehandle: moviehandle = 0; PsychCopyInIntegerArg(4, FALSE, &moviehandle); if (moviehandle < 0) PsychErrorExitMsg(PsychError_user, "Provided 'moviehandle' is negative. Must be greater or equal to zero!"); // Get optional frameduration: frameduration = 1; PsychCopyInIntegerArg(5, FALSE, &frameduration); if (frameduration < 1) PsychErrorExitMsg(PsychError_user, "Number of requested framedurations 'frameduration' is negative. Must be greater than zero!"); framepixels = PsychGetVideoFrameForMoviePtr(moviehandle, &twidth, &theight, &numChannels, &bitdepth); if (framepixels) { glPixelStorei(GL_PACK_ALIGNMENT,1); invertedY = (int) (windowRect[kPsychBottom] - sampleRect[kPsychBottom]); if (isOES) { if (bitdepth != 8) PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed due to wrong bpc value. Only 8 bpc supported on OpenGL-ES."); if (numChannels == 4) { // OES: BGRA supported? if (glewIsSupported("GL_EXT_read_format_bgra")) { // Yep: Readback in a compatible and acceptably fast format: glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_BGRA, GL_UNSIGNED_BYTE, framepixels); } else { // Suboptimal readback path. will also cause swapped colors in movie writing: glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_RGBA, GL_UNSIGNED_BYTE, framepixels); } } else if (numChannels == 3) { glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_RGB, GL_UNSIGNED_BYTE, framepixels); } else PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed due to wrong number of channels. Only 3 or 4 channels are supported on OpenGL-ES."); } else { // Desktop-GL: Use optimal format and support 16 bpc bitdepth as well. switch (numChannels) { case 4: glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_BGRA, (bitdepth <= 8) ? GL_UNSIGNED_INT_8_8_8_8 : GL_UNSIGNED_SHORT, framepixels); break; case 3: glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_RGB, (bitdepth <= 8) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT, framepixels); break; case 1: glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_RED, (bitdepth <= 8) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT, framepixels); break; default: PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed due to wrong number of channels. Only 1, 3 or 4 channels are supported on OpenGL."); break; } } // Add frame to movie, mark it as "upside down", with invalid -1 timestamp and a duration of frameduration ticks: if (PsychAddVideoFrameToMovie(moviehandle, frameduration, TRUE, -1) != 0) { PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed with error above!"); } } else { PsychErrorExitMsg(PsychError_user, "Invalid 'moviePtr' provided. Doesn't correspond to a movie open for recording!"); } } if (viewid == -1) { // Need to reset framebuffer binding to get rid of the inputBufferFBO which is bound due to // multisample resolve ops, or of other special FBO bindings --> Activate system framebuffer: PsychSetDrawingTarget(NULL); } return(PsychError_none); }
PsychError SCREENLoadNormalizedGammaTable(void) { int i, screenNumber, numEntries, inM, inN, inP, loadOnNextFlip, physicalDisplay, outputId; float *outRedTable, *outGreenTable, *outBlueTable, *inRedTable, *inGreenTable, *inBlueTable; double *inTable, *outTable; PsychWindowRecordType *windowRecord; //all subfunctions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(4)); // Get optional physicalDisplay argument - It defaults to zero on OS/X, -1 on Linux: physicalDisplay = -1; PsychCopyInIntegerArg(4, FALSE, &physicalDisplay); // Read in the screen number: // On OS/X we also accept screen indices for physical displays (as opposed to active dispays). // This only makes a difference in mirror-mode, where there is only 1 active display, but that // corresponds to two physical displays which can have different gamma setting requirements: if ((PSYCH_SYSTEM == PSYCH_OSX) && (physicalDisplay > 0)) { PsychCopyInIntegerArg(1, TRUE, &screenNumber); if (screenNumber < 1) PsychErrorExitMsg(PsychError_user, "A 'screenNumber' that is smaller than one provided, although 'physicalDisplay' flag set. This is not allowed!"); // Invert screenNumber as a sign its a physical display, not an active display: screenNumber = -1 * screenNumber; } else { PsychCopyInScreenNumberArg(1, TRUE, &screenNumber); } if ((PSYCH_SYSTEM == PSYCH_LINUX) && (physicalDisplay > -1)) { // Affect one specific display output for given screen: outputId = physicalDisplay; } else { // Other OS'es, and Linux with default setting: Affect all outputs // for a screen. outputId = -1; } // Load and sanity check the input matrix: inM = -1; inN = -1; inP = -1; if (!PsychAllocInDoubleMatArg(2, FALSE, &inM, &inN, &inP, &inTable)) { // Special case: Allow passing in an empty gamma table argument. This // triggers auto-load of identity LUT and setup of GPU for identity passthrough: inM = 0; inN = 3; inP = 1; } // Sanity check dimensions: if((inN != 3) || (inP != 1)) PsychErrorExitMsg(PsychError_user, "The gamma table must have 3 columns (Red, Green, Blue)."); // Identity passthrouh setup requested? if (inM == 0) { // Yes. Try to enable it, return its status code: PsychAllocInWindowRecordArg(1, TRUE, &windowRecord); i = PsychSetGPUIdentityPassthrough(windowRecord, screenNumber, TRUE); PsychCopyOutDoubleArg(1, FALSE, (double) i); // Done. return(PsychError_none); } #if PSYCH_SYSTEM != PSYCH_WINDOWS // OS-X and Linux allow tables with other than 256 slots: // OS/X either passes them to hw if in native size, or performs // software interpolation to convert it into native size. We allow any table size with 1 - x slots. // A table size of 1 row will have a special meaning. It interprets the 1 row of the table as gamma formula // min, max, gamma and lets the OS compute a corresponding gamma correction table. // A table size of zero rows will trigger an internal upload of an identity table via byte transfer. // On Linux we need to interpolate ourselves on non-matching table sizes. #else // Windows requires 256 slots: if((inM != 256) && (inM != 0)) { PsychErrorExitMsg(PsychError_user, "The gamma table must have 256 rows."); } #endif // Copy in optional loadOnNextFlip - flag. It defaults to zero. If provided // with a non-zero value, we will defer actual update of the gamma table to // the next bufferswap as initiated via Screen('Flip'). loadOnNextFlip = 0; PsychCopyInIntegerArg(3, FALSE, &loadOnNextFlip); if (loadOnNextFlip>0) { if ((PSYCH_SYSTEM == PSYCH_OSX) && (physicalDisplay > 0)) PsychErrorExitMsg(PsychError_user, "Non-zero 'loadOnNextFlip' flag not allowed if 'physicalDisplays' flag is non-zero!"); if ((PSYCH_SYSTEM == PSYCH_LINUX) && (physicalDisplay > -1)) PsychErrorExitMsg(PsychError_user, "Non-zero 'loadOnNextFlip' flag not allowed if 'physicalDisplays' setting is positive!"); // Allocate tables in associated windowRecord: We will update during next // Flip operation for specified windowRecord. PsychAllocInWindowRecordArg(1, TRUE, &windowRecord); // Sanity checks: if (!PsychIsOnscreenWindow(windowRecord)) PsychErrorExitMsg(PsychError_user, "Target window for gamma table upload is not an onscreen window!"); if (windowRecord->inRedTable && loadOnNextFlip!=2) PsychErrorExitMsg(PsychError_user, "This window has already a new gamma table assigned for upload on next Flip!"); if (windowRecord->inRedTable && windowRecord->inTableSize != inM) { free(windowRecord->inRedTable); windowRecord->inRedTable = NULL; free(windowRecord->inGreenTable); windowRecord->inGreenTable = NULL; free(windowRecord->inBlueTable); windowRecord->inBlueTable = NULL; } if (windowRecord->inRedTable == NULL) { // Allocate persistent memory: inRedTable=malloc(sizeof(float) * inM); inGreenTable=malloc(sizeof(float) * inM); inBlueTable=malloc(sizeof(float) * inM); // Assign the pointers to the windowRecord: windowRecord->inRedTable = inRedTable; windowRecord->inGreenTable = inGreenTable; windowRecord->inBlueTable = inBlueTable; windowRecord->inTableSize = inM; } else { inRedTable = windowRecord->inRedTable; inGreenTable = windowRecord->inGreenTable; inBlueTable = windowRecord->inBlueTable; } windowRecord->loadGammaTableOnNextFlip = (loadOnNextFlip == 1) ? 1 : 0; } else { // Allocate temporary tables: We will update immediately. inRedTable=PsychMallocTemp(sizeof(float) * inM); inGreenTable=PsychMallocTemp(sizeof(float) * inM); inBlueTable=PsychMallocTemp(sizeof(float) * inM); } for(i=0;i<inM;i++){ inRedTable[i]=(float)inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i, 0, 0)]; inGreenTable[i]=(float)inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i, 1, 0)]; inBlueTable[i]=(float)inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i, 2, 0)]; if(inRedTable[i]>1 || inRedTable[i]< 0 || inGreenTable[i] > 1 || inGreenTable[i] < 0 || inBlueTable[i] >1 || inBlueTable[i] < 0) PsychErrorExitMsg(PsychError_user, "Gamma Table Values must be in interval 0 =< x =< 1"); } if (loadOnNextFlip < 2) { //first read the existing gamma table so we can return it. PsychReadNormalizedGammaTable(screenNumber, outputId, &numEntries, &outRedTable, &outGreenTable, &outBlueTable); PsychAllocOutDoubleMatArg(1, FALSE, numEntries, 3, 0, &outTable); for(i=0;i<numEntries;i++){ outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 0, 0)]=(double)outRedTable[i]; outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 1, 0)]=(double)outGreenTable[i]; outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 2, 0)]=(double)outBlueTable[i]; } } //Now set the new gamma table if (loadOnNextFlip == 0) PsychLoadNormalizedGammaTable(screenNumber, outputId, inM, inRedTable, inGreenTable, inBlueTable); return(PsychError_none); }
PsychError SCREENGetMouseHelper(void) { #if PSYCH_SYSTEM == PSYCH_OSX Point mouseXY; UInt32 buttonState; double numButtons, *buttonArray; int i; boolean doButtonArray; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the numbers of inputs and outputs PsychErrorExit(PsychCapNumInputArgs(1)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(3)); //The maximum number of outputs //Buttons. // The only way I know to detect the number number of mouse buttons is directly via HID. The device reports //that information but OS X seems to ignore it above the level of the HID driver, that is, no OS X API above the HID driver //exposes it. So GetMouse.m function calls PsychHID detect the number of buttons and then passes that value to GetMouseHelper //which returns that number of button values in a vector. PsychCopyInDoubleArg(1, kPsychArgRequired, &numButtons); if(numButtons > 32) PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "numButtons must not exceed 32"); if(numButtons < 1) PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "numButtons must exceed 1"); doButtonArray=PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)numButtons, (int)1, &buttonArray); if(doButtonArray){ buttonState=GetCurrentButtonState(); for(i=0;i<numButtons;i++) buttonArray[i]=(double)(buttonState & (1<<i)); } //cursor position GetMouse(&mouseXY); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)mouseXY.h); PsychCopyOutDoubleArg(2, kPsychArgOptional, (double)mouseXY.v); #endif #if PSYCH_SYSTEM == PSYCH_WINDOWS static unsigned char disabledKeys[256]; static unsigned char firsttime = 1; int keysdown, i; unsigned char keyState[256]; double* buttonArray; double numButtons, timestamp; PsychNativeBooleanType* buttonStates; POINT point; PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; // Retrieve optional number of mouse buttons: numButtons = 0; PsychCopyInDoubleArg(1, FALSE, &numButtons); // Are we operating in 'GetMouseHelper' mode? numButtons>=0 indicates this. if (numButtons>=0) { // GetMouse-Mode: Return mouse button states and mouse cursor position: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)3, (int)1, &buttonArray); // Query and return mouse button state: PsychGetMouseButtonState(buttonArray); // Query and return cursor position in global coordinates: GetCursorPos(&point); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) point.x); PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) point.y); } else { // 'KeyboardHelper' mode: We implement either KbCheck() or KbWait() via X11. // This is a hack to provide keyboard queries until a PsychHID() implementation // for Microsoft Windows is available... if (firsttime) { // First time init: firsttime = 0; memset(keyState, 0, sizeof(keyState)); memset(disabledKeys, 0, sizeof(disabledKeys)); // These keycodes are always disabled: 0, 255: disabledKeys[0]=1; disabledKeys[255]=1; // Mouse buttone (left, right, middle) are also disabled by default: disabledKeys[1]=1; disabledKeys[2]=1; disabledKeys[4]=1; } if (numButtons==-1 || numButtons==-2) { // KbCheck()/KbWait() mode do { // Reset overall key state to "none pressed": keysdown=0; // Request current time of query: PsychGetAdjustedPrecisionTimerSeconds(×tamp); // Query state of all keys: for(i=1;i<255;i++){ keyState[i] = (GetAsyncKeyState(i) & -32768) ? 1 : 0; } // Disable all keys that are registered in disabledKeys. Check if // any non-disabled key is down. for (i=0; i<256; i++) { if (disabledKeys[i]>0) keyState[i] = 0; keysdown+=(unsigned int) keyState[i]; } // We repeat until any key pressed if in KbWait() mode, otherwise we // exit the loop after first iteration in KbCheck mode. if ((numButtons==-1) || ((numButtons==-2) && (keysdown>0))) break; // Sleep for a millisecond before next KbWait loop iteration: PsychWaitIntervalSeconds(0.001); } while(1); if (numButtons==-2) { // KbWait mode: Copy out time value. PsychCopyOutDoubleArg(1, kPsychArgOptional, timestamp); } else { // KbCheck mode: // Copy out overall keystate: PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown>0) ? 1 : 0); // Copy out timestamp: PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp); // Copy out keyboard state: PsychAllocOutBooleanMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates); // Build 256 elements return vector: for(i=0; i<255; i++) { buttonStates[i] = (PsychNativeBooleanType)((keyState[i+1]) ? 1 : 0); } // Special case: Null out last element: buttonStates[255] = (PsychNativeBooleanType) 0; } } } #endif #if PSYCH_SYSTEM == PSYCH_LINUX unsigned char keys_return[32]; char* keystring; PsychGenericScriptType *kbNames; CGDirectDisplayID dpy; Window rootwin, childwin; int i, j, mx, my, dx, dy; unsigned int mask_return; double numButtons, timestamp; double* buttonArray; PsychNativeBooleanType* buttonStates; int keysdown; XEvent event_return; XKeyPressedEvent keypressevent; int screenNumber; PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychCopyInDoubleArg(1, kPsychArgRequired, &numButtons); // Retrieve optional screenNumber argument: screenNumber = 0; PsychCopyInScreenNumberArg(2, FALSE, &screenNumber); // Map screenNumber to X11 display handle and screenid: PsychGetCGDisplayIDFromScreenNumber(&dpy, screenNumber); // Are we operating in 'GetMouseHelper' mode? numButtons>=0 indicates this. if (numButtons>=0) { // Mouse pointer query mode: XQueryPointer(dpy, RootWindow(dpy, PsychGetXScreenIdForScreen(screenNumber)), &rootwin, &childwin, &mx, &my, &dx, &dy, &mask_return); // Copy out mouse x and y position: PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) mx); PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) my); // Copy out mouse button state: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)numButtons, (int)1, &buttonArray); // Bits 8, 9 and 10 of mask_return seem to correspond to mouse buttons // 1, 2 and 3 of a mouse for some weird reason. Bits 0-7 describe keyboard modifier keys // like Alt, Ctrl, Shift, ScrollLock, NumLock, CapsLock... // We remap here, so the first three returned entries correspond to the mouse buttons and // the rest is attached behind, if requested... // Mouse buttons: Left, Middle, Right == 0, 1, 2, aka 1,2,3 in Matlab space... for (i=0; i<numButtons && i<3; i++) { buttonArray[i] = (mask_return & (1<<(i+8))) ? 1 : 0; } // Modifier keys 0 to 7 appended: for (i=3; i<numButtons && i<3+8; i++) { buttonArray[i] = (mask_return & (1<<(i-3))) ? 1 : 0; } // Everything else appended: for (i=11; i<numButtons; i++) { buttonArray[i] = (mask_return & (1<<i)) ? 1 : 0; } } else { // 'KeyboardHelper' mode: We implement either KbCheck() or KbWait() via X11. // This is a hack to provide keyboard queries until a PsychHID() implementation // for Linux is available... if (numButtons==-1 || numButtons==-2) { // KbCheck()/KbWait() mode: // Switch X-Server into synchronous mode: We need this to get // a higher timing precision. XSynchronize(dpy, TRUE); do { // Reset overall key state to "none pressed": keysdown=0; // Request current keyboard state from X-Server: XQueryKeymap(dpy, keys_return); // Request current time of query: PsychGetAdjustedPrecisionTimerSeconds(×tamp); // Any key down? for (i=0; i<32; i++) keysdown+=(unsigned int) keys_return[i]; // We repeat until any key pressed if in KbWait() mode, otherwise we // exit the loop after first iteration in KbCheck mode. if ((numButtons==-1) || ((numButtons==-2) && (keysdown>0))) break; // Sleep for a few milliseconds before next KbWait loop iteration: PsychWaitIntervalSeconds(0.01); } while(1); if (numButtons==-2) { // Copy out time: PsychCopyOutDoubleArg(1, kPsychArgOptional, timestamp); } else { // KbCheck mode: // Copy out overall keystate: PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown>0) ? 1 : 0); // copy out timestamp: PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp); // Copy keyboard state: PsychAllocOutBooleanMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates); // Map 32 times 8 bitvector to 256 element return vector: for(i=0; i<32; i++) { for(j=0; j<8; j++) { buttonStates[i*8 + j] = (PsychNativeBooleanType)(keys_return[i] & (1<<j)) ? 1 : 0; } } } } else if (numButtons == -3) { // numButtons == -3 --> KbName mapping mode: // Return the full keyboard keycode to ASCII character code mapping table... PsychAllocOutCellVector(1, kPsychArgOptional, 256, &kbNames); for(i=0; i<256; i++) { // Map keyboard scan code to KeySym: keystring = XKeysymToString(XKeycodeToKeysym(dpy, i, 0)); if (keystring) { // Character found: Return its ASCII name string: PsychSetCellVectorStringElement(i, keystring, kbNames); } else { // No character for this keycode: PsychSetCellVectorStringElement(i, "", kbNames); } } } else if (numButtons == -4) { // GetChar() emulation. /* do { */ /* // Fetch next keypress event from queue, block if none is available... */ /* keystring = NULL; */ /* XNextEvent(dpy, &event_return); */ /* // Check for valid keypress event and extract character: */ /* if (event_return.type == KeyPress) { */ /* keypressevent = (XKeyPressedEvent) event_return; */ /* keystring = NULL; */ /* keystring = XKeysymToString(XKeycodeToKeysym(dpy, keypressevent.keycode, 0)); */ /* } */ /* // Repeat until a valid char is returned. */ /* } while (keystring == NULL); */ /* // Copy out character: */ /* PsychCopyOutCharArg(1, kPsychArgOptional, (char) keystring); */ /* // Copy out time: */ /* PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) keypressevent.time); */ } } #endif return(PsychError_none); }
PsychError SCREENPreference(void) { PsychArgFormatType arg1Type; char *preferenceName, *newFontName; const char *tableCreator, *oldDefaultFontName; psych_bool preferenceNameArgumentValid, booleanInput, ignoreCase, tempFlag, textAlphaBlendingFlag, suppressAllWarningsFlag; int numInputArgs, i, newFontStyleNumber, newFontSize, tempInt, tempInt2, tempInt3, tempInt4; double returnDoubleValue, inputDoubleValue; double maxStddev, maxDeviation, maxDuration; int minSamples; double *dheads = NULL; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous or missing arguments PsychErrorExit(PsychCapNumInputArgs(5)); PsychErrorExit(PsychRequireNumInputArgs(1)); PsychErrorExit(PsychCapNumOutputArgs(4)); numInputArgs=PsychGetNumInputArgs(); arg1Type=PsychGetArgType(1); preferenceNameArgumentValid=FALSE; //Cases which require both a window pointer or screen number and preference name. Argument 1 is the wposn and argument 2 is the preference name. if( numInputArgs >= 2 && (PsychIsScreenNumberArg(1) || PsychIsScreenNumberArg(1)) && PsychGetArgType(2)==PsychArgType_char ){ PsychAllocInCharArg(2, kPsychArgRequired, &preferenceName); //preferences which require window pointer or screen number argument which we DO NOT support for(i=0;i<kPsychNumUnsupportedMacVideoPreferences;i++){ if(PsychMatch(preferenceName, unsupportedMacVideoPreferenceNames[i])) PsychErrorExit(PsychError_unsupportedOS9Preference); } //insert here conditionals to act on prefernces which accept a window pointer or screen number argument which we DO support. PsychErrorExit(PsychError_unrecognizedPreferenceName); } //Cases which do not require a wposn. Argument 1 is the preference name. if present Argument 2 is the new value if(arg1Type==PsychArgType_char){ PsychAllocInCharArg(1, kPsychArgRequired, &preferenceName); //Preferernces which we do not support and which do not require a wposn for(i=0;i<kPsychNumUnsupportedMacNonVideoPreferences;i++){ if(PsychMatch(preferenceName, unsupportedMacNonVideoPreferenceNames[i])) PsychErrorExit(PsychError_unsupportedOS9Preference); } //Preferences which we do support if(PsychMatch(preferenceName, "IgnoreCase")){ ignoreCase=!PsychIsPsychMatchCaseSensitive(); PsychCopyOutFlagArg(1, kPsychArgOptional, ignoreCase); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput); PsychSetPsychMatchCaseSenstive(!booleanInput); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Tick0Secs")){ if(PsychCopyInDoubleArg(2, kPsychArgOptional, &inputDoubleValue) && inputDoubleValue==PsychGetNanValue()) PsychEstimateGetSecsValueAtTickCountZero(); returnDoubleValue=PsychGetEstimatedSecsValueAtTickCountZero(); PsychCopyOutDoubleArg(1, kPsychArgOptional, returnDoubleValue); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "PsychTableVersion")){ if(numInputArgs==2) PsychErrorExit(PsychError_extraInputArg); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)PsychPrefStateGet_PsychTableVersion()); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "PsychTableCreator")){ if(numInputArgs==2) PsychErrorExit(PsychError_extraInputArg); tableCreator=PsychPrefStateGet_PsychTableCreator(); PsychCopyOutCharArg(1, kPsychArgOptional, tableCreator); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Process")){ if(numInputArgs==2) PsychErrorExit(PsychError_extraInputArg); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) (psych_int64) getpid()); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultFontName")){ PsychPrefStateGet_DefaultFontName(&oldDefaultFontName); PsychCopyOutCharArg(1, kPsychArgOptional, oldDefaultFontName); if(numInputArgs==2){ PsychAllocInCharArg(2, kPsychArgRequired, &newFontName); PsychPrefStateSet_DefaultFontName(newFontName); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "TextEncodingLocale")){ PsychCopyOutCharArg(1, kPsychArgOptional, PsychGetUnicodeTextConversionLocale()); if(numInputArgs==2){ PsychAllocInCharArg(2, kPsychArgRequired, &newFontName); if (!PsychSetUnicodeTextConversionLocale(newFontName)) PsychErrorExitMsg(PsychError_user, "Setting the 'TextEncodingLocale' failed, most likely because you provided an invalid/unknown locale setting string."); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultFontStyle")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextStyle()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontStyleNumber); PsychPrefStateSet_DefaultTextStyle(newFontStyleNumber); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "OverrideMultimediaEngine")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_UseGStreamer()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_UseGStreamer(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultTextYPositionIsBaseline")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextYPositionIsBaseline()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_TextYPositionIsBaseline(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "TextAntiAliasing")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextAntiAliasing()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_TextAntiAliasing(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "TextRenderer")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextRenderer()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_TextRenderer(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultFontSize")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextSize()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontSize); PsychPrefStateSet_DefaultTextSize(newFontSize); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DebugMakeTexture")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DebugMakeTexture()); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag); PsychPrefStateSet_DebugMakeTexture(tempFlag); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SkipSyncTests")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_SkipSyncTests()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_SkipSyncTests(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "VisualDebugLevel")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VisualDebugLevel()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VisualDebugLevel(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "VBLTimestampingMode")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VBLTimestampingMode()); if(numInputArgs>=2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VBLTimestampingMode(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SyncTestSettings")){ PsychPrefStateGet_SynctestThresholds(&maxStddev, &minSamples, &maxDeviation, &maxDuration); PsychCopyOutDoubleArg(1, kPsychArgOptional, maxStddev); PsychCopyOutDoubleArg(2, kPsychArgOptional, minSamples); PsychCopyOutDoubleArg(3, kPsychArgOptional, maxDeviation); PsychCopyOutDoubleArg(4, kPsychArgOptional, maxDuration); if(numInputArgs>=2){ PsychCopyInDoubleArg( 2, kPsychArgOptional, &maxStddev); PsychCopyInIntegerArg(3, kPsychArgOptional, &minSamples); PsychCopyInDoubleArg( 4, kPsychArgOptional, &maxDeviation); PsychCopyInDoubleArg( 5, kPsychArgOptional, &maxDuration); PsychPrefStateSet_SynctestThresholds(maxStddev, minSamples, maxDeviation, maxDuration); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "VBLEndlineOverride")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VBLEndlineOverride()); if(numInputArgs>=2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VBLEndlineOverride(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultVideocaptureEngine")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VideoCaptureEngine()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VideoCaptureEngine(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "WindowShieldingLevel")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_WindowShieldingLevel()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_WindowShieldingLevel(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "ConserveVRAM") || PsychMatch(preferenceName, "Workarounds1")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_ConserveVRAM()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_ConserveVRAM(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Verbosity")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_Verbosity()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_Verbosity(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "FrameRectCorrection")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_FrameRectCorrection()); if(numInputArgs==2){ PsychCopyInDoubleArg(2, kPsychArgRequired, &inputDoubleValue); PsychPrefStateSet_FrameRectCorrection(inputDoubleValue); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "EmulateOldPTB")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_EmulateOldPTB()); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag); PsychPrefStateSet_EmulateOldPTB(tempFlag); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Enable3DGraphics")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_3DGfx()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_3DGfx(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "TextAlphaBlending")){ textAlphaBlendingFlag=PsychPrefStateGet_TextAlphaBlending(); PsychCopyOutFlagArg(1, kPsychArgOptional, textAlphaBlendingFlag); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput); PsychPrefStateSet_TextAlphaBlending(booleanInput); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SuppressAllWarnings")){ suppressAllWarningsFlag=PsychPrefStateGet_SuppressAllWarnings(); PsychCopyOutFlagArg(1, kPsychArgOptional, suppressAllWarningsFlag); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput); PsychPrefStateSet_SuppressAllWarnings(booleanInput); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SynchronizeDisplays")){ if(numInputArgs >= 2) { // This is a special call: It currently doesn't set a preference setting, // but instead triggers an instantaneous synchronization of all available // display heads, if possible. We may have a more clever and "standard" interface // interface for this later on, but for first tests this will do. // Syncmethod is hard-coded to 0 -> Use whatever's available to sync. // timeout for retries is 5.0 seconds. // Acceptable residual offset is +/- 2 scanlines. // Returns the real residual offset after sync. PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); if (!PsychCopyInIntegerArg(3, kPsychArgOptional, &tempInt3)) { // No screenId specified: Resync default screen or whatever... tempInt2 = 0; if (PsychSynchronizeDisplayScreens(&tempInt2, NULL, &tempInt, tempInt, 5.0, 2)!=PsychError_none) PsychErrorExitMsg(PsychError_user, "Sync failed for reasons mentioned above."); } else { // Specific screenId provided: Resync crtc's associated with this screenId if possible: tempInt2 = 1; if (PsychSynchronizeDisplayScreens(&tempInt2, &tempInt3, &tempInt, tempInt, 5.0, 2)!=PsychError_none) PsychErrorExitMsg(PsychError_user, "Sync failed for reasons mentioned above."); } PsychCopyOutDoubleArg(1, kPsychArgOptional, tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "ScreenToHead")){ // screenId is required: PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); if (tempInt < 0 || tempInt >= PsychGetNumDisplays() || tempInt >= kPsychMaxPossibleDisplays) PsychErrorExitMsg(PsychError_user, "Invalid screenId provided. Out of valid range!"); // Return old mappings for this screenId: for (tempInt2 = 0; (tempInt2 < kPsychMaxPossibleCrtcs) && (PsychPrefStateGet_ScreenToHead(tempInt, tempInt2) >= 0); tempInt2++); PsychAllocOutDoubleMatArg(1, kPsychArgOptional, 2, tempInt2, 1, &dheads); tempInt4 = 0; for (tempInt3 = 0; tempInt3 < tempInt2; tempInt3++) { dheads[tempInt4++] = (double) PsychPrefStateGet_ScreenToHead(tempInt, tempInt3); dheads[tempInt4++] = (double) PsychPrefStateGet_ScreenToCrtcId(tempInt, tempInt3); } // Optionally retrieve and set new mappings for this screenId: if(numInputArgs>=3) { // Set new headId for screenId: PsychCopyInIntegerArg(3, kPsychArgRequired, &tempInt2); if (tempInt2 < 0) PsychErrorExitMsg(PsychError_user, "Invalid negative headId provided!"); // Set new crtcId for screenId: PsychCopyInIntegerArg(4, kPsychArgRequired, &tempInt3); if (tempInt3 < 0) PsychErrorExitMsg(PsychError_user, "Invalid negative crtcId provided!"); // Assign primary head by default (index 0), but allow optionally others as well: tempInt4 = 0; PsychCopyInIntegerArg(5, kPsychArgOptional, &tempInt4); if (tempInt4 < 0 || tempInt4 >= kPsychMaxPossibleCrtcs) PsychErrorExitMsg(PsychError_user, "Invalid rankId provided! Too many heads for one screen!"); PsychPrefStateSet_ScreenToHead(tempInt, tempInt2, tempInt3, tempInt4); } preferenceNameArgumentValid=TRUE; }else PsychErrorExit(PsychError_unrecognizedPreferenceName); } if(!preferenceNameArgumentValid) PsychErrorExitMsg(PsychError_user, "Invalid arguments to preferences command"); return(PsychError_none); }
PsychError SCREENGetImage(void) { PsychRectType windowRect,sampleRect; int nrchannels, ix, iy, sampleRectWidth, sampleRectHeight, invertedY, redReturnIndex, greenReturnIndex, blueReturnIndex, alphaReturnIndex, planeSize; int viewid; ubyte *returnArrayBase, *redPlane, *greenPlane, *bluePlane, *alphaPlane; float *dredPlane, *dgreenPlane, *dbluePlane, *dalphaPlane; double *returnArrayBaseDouble; PsychWindowRecordType *windowRecord; GLboolean isDoubleBuffer, isStereo; char* buffername = NULL; boolean floatprecision = FALSE; GLenum whichBuffer = 0; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the numbers of inputs and outputs PsychErrorExit(PsychCapNumInputArgs(5)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(1)); //The maximum number of outputs // Get windowRecord for this window: PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); // Set window as drawingtarget: Even important if this binding is changed later on! // We need to make sure all needed transitions are done - esp. in non-imaging mode, // so backbuffer is in a useable state: PsychSetDrawingTarget(windowRecord); // Disable shaders: PsychSetShader(windowRecord, 0); // Soft-Reset drawingtarget. This is important to make sure no FBO's are bound, // otherwise the following glGets for GL_DOUBLEBUFFER and GL_STEREO will retrieve // wrong results, leading to totally wrong read buffer assignments down the road!! PsychSetDrawingTarget(0x1); glGetBooleanv(GL_DOUBLEBUFFER, &isDoubleBuffer); glGetBooleanv(GL_STEREO, &isStereo); // Retrieve optional read rectangle: PsychGetRectFromWindowRecord(windowRect, windowRecord); if(!PsychCopyInRectArg(2, FALSE, sampleRect)) memcpy(sampleRect, windowRect, sizeof(PsychRectType)); if (IsPsychRectEmpty(sampleRect)) return(PsychError_none); // Assign read buffer: if(PsychIsOnscreenWindow(windowRecord)) { // Onscreen window: We read from the front- or front-left buffer by default. // This works on single-buffered and double buffered contexts in a consistent fashion: // Copy in optional override buffer name: PsychAllocInCharArg(3, FALSE, &buffername); // Override buffer name provided? if (buffername) { // Which one is it? // "frontBuffer" is always a valid choice: if (PsychMatch(buffername, "frontBuffer")) whichBuffer = GL_FRONT; // Allow selection of left- or right front stereo buffer in stereo mode: if (PsychMatch(buffername, "frontLeftBuffer") && isStereo) whichBuffer = GL_FRONT_LEFT; if (PsychMatch(buffername, "frontRightBuffer") && isStereo) whichBuffer = GL_FRONT_RIGHT; // Allow selection of backbuffer in double-buffered mode: if (PsychMatch(buffername, "backBuffer") && isDoubleBuffer) whichBuffer = GL_BACK; // Allow selection of left- or right back stereo buffer in stereo mode: if (PsychMatch(buffername, "backLeftBuffer") && isStereo && isDoubleBuffer) whichBuffer = GL_BACK_LEFT; if (PsychMatch(buffername, "backRightBuffer") && isStereo && isDoubleBuffer) whichBuffer = GL_BACK_RIGHT; // Allow AUX buffer access for debug purposes: if (PsychMatch(buffername, "aux0Buffer")) whichBuffer = GL_AUX0; if (PsychMatch(buffername, "aux1Buffer")) whichBuffer = GL_AUX1; if (PsychMatch(buffername, "aux2Buffer")) whichBuffer = GL_AUX2; if (PsychMatch(buffername, "aux3Buffer")) whichBuffer = GL_AUX3; } else { // Default is frontbuffer: whichBuffer=GL_FRONT; } } else { // Offscreen window or texture: They only have one buffer, which is the // backbuffer in double-buffered mode and the frontbuffer in single buffered mode: whichBuffer=(isDoubleBuffer) ? GL_BACK : GL_FRONT; } // Enable this windowRecords framebuffer as current drawingtarget. This should // also allow us to "GetImage" from Offscreen windows: if ((windowRecord->imagingMode & kPsychNeedFastBackingStore) || (windowRecord->imagingMode & kPsychNeedFastOffscreenWindows)) { // Special case: Imaging pipeline active - We need to activate system framebuffer // so we really read the content of the framebuffer and not of some FBO: if (PsychIsOnscreenWindow(windowRecord)) { // It's an onscreen window: if (buffername && (PsychMatch(buffername, "drawBuffer")) && (windowRecord->imagingMode & kPsychNeedFastBackingStore)) { // Activate drawBufferFBO: PsychSetDrawingTarget(windowRecord); whichBuffer = GL_COLOR_ATTACHMENT0_EXT; // Is the drawBufferFBO multisampled? viewid = (((windowRecord->stereomode > 0) && (windowRecord->stereodrawbuffer == 1)) ? 1 : 0); if (windowRecord->fboTable[windowRecord->drawBufferFBO[viewid]]->multisample > 0) { // It is! We can't read from a multisampled FBO. Need to perform a multisample resolve operation and read // from the resolved unisample buffer instead. This is only safe if the unisample buffer is either a dedicated // FBO, or - in case its the final system backbuffer etc. - if preflip operations haven't been performed yet. // If non dedicated buffer (aka finalizedFBO) and preflip ops have already happened, then the backbuffer contains // final content for an upcoming Screen('Flip') and we can't use (and therefore taint) that buffer. if ((windowRecord->inputBufferFBO[viewid] == windowRecord->finalizedFBO[viewid]) && (windowRecord->backBufferBackupDone)) { // Target for resolve is finalized FBO (probably system backbuffer) and preflip ops have run already. We // can't do the resolve op, as this would screw up the backbuffer with the final stimulus: printf("PTB-ERROR: Tried to 'GetImage' from a multisampled 'drawBuffer', but can't perform anti-aliasing pass due to\n"); printf("PTB-ERROR: lack of a dedicated resolve buffer.\n"); printf("PTB-ERROR: You can get what you wanted by either one of two options:\n"); printf("PTB-ERROR: Either enable a processing stage in the imaging pipeline, even if you don't need it, e.g., by setting\n"); printf("PTB-ERROR: the imagingmode argument in the 'OpenWindow' call to kPsychNeedImageProcessing, this will create a\n"); printf("PTB-ERROR: suitable resolve buffer. Or place the 'GetImage' call before any Screen('DrawingFinished') call, then\n"); printf("PTB-ERROR: i can (ab-)use the system backbuffer as a temporary resolve buffer.\n\n"); PsychErrorExitMsg(PsychError_user, "Tried to 'GetImage' from a multi-sampled 'drawBuffer'. Unsupported operation under given conditions."); } else { // Ok, the inputBufferFBO is a suitable temporary resolve buffer. Perform a multisample resolve blit to it: // A simple glBlitFramebufferEXT() call will do the copy & downsample operation: glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->drawBufferFBO[viewid]]->fboid); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->fboid); glBlitFramebufferEXT(0, 0, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->width, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->height, 0, 0, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->width, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->height, GL_COLOR_BUFFER_BIT, GL_NEAREST); // Bind inputBuffer as framebuffer: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->fboid); viewid = -1; } } } else { // Activate system framebuffer: PsychSetDrawingTarget(NULL); } } else { // Offscreen window or texture: Select drawing target as usual, // but set color attachment as read buffer: PsychSetDrawingTarget(windowRecord); whichBuffer = GL_COLOR_ATTACHMENT0_EXT; // We do not support multisampled readout: if (windowRecord->fboTable[windowRecord->drawBufferFBO[0]]->multisample > 0) { printf("PTB-ERROR: You tried to Screen('GetImage', ...); from an offscreen window or texture which has multisample anti-aliasing enabled.\n"); printf("PTB-ERROR: This operation is not supported. You must first use Screen('CopyWindow') to create a non-multisampled copy of the\n"); printf("PTB-ERROR: texture or offscreen window, then use 'GetImage' on that copy. The copy will be anti-aliased, so you'll get what you\n"); printf("PTB-ERROR: wanted with a bit more effort. Sorry for the inconvenience, but this is mostly a hardware limitation.\n\n"); PsychErrorExitMsg(PsychError_user, "Tried to 'GetImage' from a multi-sampled texture or offscreen window. Unsupported operation."); } } } else { // Normal case: No FBO based imaging - Select drawing target as usual: PsychSetDrawingTarget(windowRecord); } // Select requested read buffer, after some double-check: if (whichBuffer == 0) PsychErrorExitMsg(PsychError_user, "Invalid or unknown 'bufferName' argument provided."); glReadBuffer(whichBuffer); if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: In Screen('GetImage'): GL-Readbuffer whichBuffer = %i\n", whichBuffer); // Get optional floatprecision flag: We return data with float-precision if // this flag is set. By default we return uint8 data: PsychCopyInFlagArg(4, FALSE, &floatprecision); // Get the optional number of channels flag: By default we return 3 channels, // the Red, Green, and blue color channel: nrchannels = 3; PsychCopyInIntegerArg(5, FALSE, &nrchannels); if (nrchannels < 1 || nrchannels > 4) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' must be between 1 and 4!"); sampleRectWidth=PsychGetWidthFromRect(sampleRect); sampleRectHeight=PsychGetHeightFromRect(sampleRect); if (!floatprecision) { // Readback of standard 8bpc uint8 pixels: PsychAllocOutUnsignedByteMatArg(1, TRUE, sampleRectHeight, sampleRectWidth, nrchannels, &returnArrayBase); redPlane= PsychMallocTemp(nrchannels * sizeof(GL_UNSIGNED_BYTE) * sampleRectWidth * sampleRectHeight); planeSize=sampleRectWidth * sampleRectHeight; greenPlane= redPlane + planeSize; bluePlane= redPlane + 2 * planeSize; alphaPlane= redPlane + 3 * planeSize; glPixelStorei(GL_PACK_ALIGNMENT,1); invertedY=windowRect[kPsychBottom]-sampleRect[kPsychBottom]; glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_RED, GL_UNSIGNED_BYTE, redPlane); if (nrchannels>1) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_GREEN, GL_UNSIGNED_BYTE, greenPlane); if (nrchannels>2) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_BLUE, GL_UNSIGNED_BYTE, bluePlane); if (nrchannels>3) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_ALPHA, GL_UNSIGNED_BYTE, alphaPlane); //in one pass transpose and flip what we read with glReadPixels before returning. //-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns. //-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left. for(ix=0;ix<sampleRectWidth;ix++){ for(iy=0;iy<sampleRectHeight;iy++){ // Compute write-indices for returned data: redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0); greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 1); blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 2); alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 3); // Always return RED/LUMINANCE channel: returnArrayBase[redReturnIndex]=redPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth]; // Other channels on demand: if (nrchannels>1) returnArrayBase[greenReturnIndex]=greenPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth]; if (nrchannels>2) returnArrayBase[blueReturnIndex]=bluePlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth]; if (nrchannels>3) returnArrayBase[alphaReturnIndex]=alphaPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth]; } } } else { // Readback of standard 32bpc float pixels into a double matrix: PsychAllocOutDoubleMatArg(1, TRUE, sampleRectHeight, sampleRectWidth, nrchannels, &returnArrayBaseDouble); dredPlane= PsychMallocTemp(nrchannels * sizeof(GL_FLOAT) * sampleRectWidth * sampleRectHeight); planeSize=sampleRectWidth * sampleRectHeight * sizeof(GL_FLOAT); dgreenPlane= redPlane + planeSize; dbluePlane= redPlane + 2 * planeSize; dalphaPlane= redPlane + 3 * planeSize; glPixelStorei(GL_PACK_ALIGNMENT, 1); invertedY=windowRect[kPsychBottom]-sampleRect[kPsychBottom]; if (nrchannels==1) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_RED, GL_FLOAT, dredPlane); if (nrchannels==2) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_LUMINANCE_ALPHA, GL_FLOAT, dredPlane); if (nrchannels==3) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_RGB, GL_FLOAT, dredPlane); if (nrchannels==4) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_RGBA, GL_FLOAT, dredPlane); //in one pass transpose and flip what we read with glReadPixels before returning. //-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns. //-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left. for(ix=0;ix<sampleRectWidth;ix++){ for(iy=0;iy<sampleRectHeight;iy++){ // Compute write-indices for returned data: redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0); greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 1); blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 2); alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 3); // Always return RED/LUMINANCE channel: returnArrayBaseDouble[redReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 0]; // Other channels on demand: if (nrchannels>1) returnArrayBaseDouble[greenReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 1]; if (nrchannels>2) returnArrayBaseDouble[blueReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 2]; if (nrchannels>3) returnArrayBaseDouble[alphaReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 3]; } } } if (viewid == -1) { // Need to reset framebuffer binding to get rid of the inputBufferFBO which is bound due to // multisample resolve ops --> Activate system framebuffer: PsychSetDrawingTarget(NULL); } return(PsychError_none); }
PsychError SCREENReadNormalizedGammaTable(void) { int i, screenNumber, numEntries, reallutsize, physicalDisplay, outputId; float *redTable, *greenTable, *blueTable; double *gammaTable; //all subfunctions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(3)); PsychErrorExit(PsychCapNumInputArgs(2)); // Get optional physicalDisplay argument - It defaults to zero: physicalDisplay = -1; PsychCopyInIntegerArg(2, FALSE, &physicalDisplay); // Read in the screen number: // On OS/X we also accept screen indices for physical displays (as opposed to active dispays). // This only makes a difference in mirror-mode, where there is only 1 active display, but that // corresponds to two physical displays which can have different gamma setting requirements: if ((PSYCH_SYSTEM == PSYCH_OSX) && (physicalDisplay > 0)) { PsychCopyInIntegerArg(1, TRUE, &screenNumber); if (screenNumber < 1) PsychErrorExitMsg(PsychError_user, "A 'screenNumber' that is smaller than one provided, although 'physicalDisplay' flag set. This is not allowed!"); // Invert screenNumber as a sign its a physical display, not an active display: screenNumber = -1 * screenNumber; } else { PsychCopyInScreenNumberArg(1, TRUE, &screenNumber); } if ((PSYCH_SYSTEM == PSYCH_LINUX) && (physicalDisplay > -1)) { // Affect one specific display output for given screen: outputId = physicalDisplay; } else { // Other OS'es, and Linux with default setting: Affect all outputs // for a screen. outputId = -1; } // Retrieve gamma table: PsychReadNormalizedGammaTable(screenNumber, outputId, &numEntries, &redTable, &greenTable, &blueTable); // Copy it out to runtime: PsychAllocOutDoubleMatArg(1, FALSE, numEntries, 3, 0, &gammaTable); for(i=0;i<numEntries;i++){ gammaTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 0, 0)]=(double)redTable[i]; gammaTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 1, 0)]=(double)greenTable[i]; gammaTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 2, 0)]=(double)blueTable[i]; } // Copy out optional DAC resolution value: PsychCopyOutDoubleArg(2, FALSE, (double) PsychGetDacBitsFromDisplay(screenNumber)); // We default to the assumption that the real size of the hardware LUT is identical to // the size of the returned LUT: reallutsize = numEntries; #if PSYCH_SYSTEM == PSYCH_OSX // On OS-X we query the real LUT size from the OS and return that value: CGDirectDisplayID displayID; CFMutableDictionaryRef properties; CFNumberRef cfGammaLength; SInt32 lutslotcount; io_service_t displayService; kern_return_t kr; CFMutableArrayRef framebufferTimings0 = 0; CFDataRef framebufferTimings1 = 0; IODetailedTimingInformationV2 *framebufferTiming = NULL; // Retrieve display handle for screen: PsychGetCGDisplayIDFromScreenNumber(&displayID, screenNumber); if (PsychPrefStateGet_Verbosity()>5) printf("PTB-DEBUG: Screen %i has framebuffer address %p.\n", screenNumber, CGDisplayBaseAddress(displayID)); // Retrieve low-level IOKit service port for this display: displayService = CGDisplayIOServicePort(displayID); // Obtain the properties from that service kr = IORegistryEntryCreateCFProperties(displayService, &properties, NULL, 0); if((kr == kIOReturnSuccess) && ((cfGammaLength = (CFNumberRef) CFDictionaryGetValue(properties, CFSTR(kIOFBGammaCountKey)))!=NULL)) { CFNumberGetValue(cfGammaLength, kCFNumberSInt32Type, &lutslotcount); CFRelease(properties); reallutsize = (int) lutslotcount; } else { // Failed! if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to query real size of video LUT for screen %i! Will return safe default of %i slots.\n", screenNumber, reallutsize); } if (PsychPrefStateGet_Verbosity()>9) { CFDictionaryRef currentMode; CFNumberRef n; int modeId; currentMode = CGDisplayCurrentMode(displayID); n=CFDictionaryGetValue(currentMode, kCGDisplayMode); CFNumberGetValue(n, kCFNumberIntType, &modeId); printf("Current mode has id %i\n\n", modeId); kr = IORegistryEntryCreateCFProperties(displayService, &properties, NULL, 0); if((kr == kIOReturnSuccess) && ((framebufferTimings0 = (CFMutableArrayRef) CFDictionaryGetValue(properties, CFSTR(kIOFBDetailedTimingsKey) ) )!=NULL)) { for (i=0; i<CFArrayGetCount(framebufferTimings0); i++) { if ((framebufferTimings1 = CFArrayGetValueAtIndex(framebufferTimings0, i)) != NULL) { if ((framebufferTiming = (IODetailedTimingInformationV2*) CFDataGetBytePtr(framebufferTimings1)) != NULL) { printf("[%i] : VActive = %li, VBL = %li, VSYNC = %li, VSYNCWIDTH = %li , VBORDERBOT = %li, VTOTAL = %li \n", i, framebufferTiming->verticalActive, framebufferTiming->verticalBlanking, framebufferTiming->verticalSyncOffset, framebufferTiming->verticalSyncPulseWidth, framebufferTiming->verticalBorderBottom, framebufferTiming->verticalActive + framebufferTiming->verticalBlanking); } } } CFRelease(properties); } else { // Failed! if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to query STUFF for screen %i --> %p!\n", screenNumber, properties); } } #endif // Copy out optional real LUT size (number of slots): PsychCopyOutDoubleArg(3, FALSE, (double) reallutsize); return(PsychError_none); }
PsychError SCREENGetMouseHelper(void) { const char *valuatorInfo[]={"label", "min", "max", "resolution", "mode", "sourceID"}; int numValuatorStructFieldNames = 6; int numIValuators = 0; PsychGenericScriptType *valuatorStruct = NULL; #if PSYCH_SYSTEM == PSYCH_OSX Point mouseXY; UInt32 buttonState; double *buttonArray; int numButtons, i; psych_bool doButtonArray; PsychWindowRecordType *windowRecord; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the numbers of inputs and outputs PsychErrorExit(PsychCapNumInputArgs(3)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(6)); //The maximum number of outputs //Buttons. // The only way I know to detect the number number of mouse buttons is directly via HID. The device reports //that information but OS X seems to ignore it above the level of the HID driver, that is, no OS X API above the HID driver //exposes it. So GetMouse.m function calls PsychHID detect the number of buttons and then passes that value to GetMouseHelper //which returns that number of button values in a vector. PsychCopyInIntegerArg(1, kPsychArgRequired, &numButtons); if(numButtons > 32) PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "numButtons must not exceed 32"); // Special codes -10 to -15? --> Console keyboard queries: if(numButtons <= -10 && numButtons >= -15) { ConsoleInputHelper((int) numButtons); return(PsychError_none); } if(numButtons < 1) PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "numButtons must exceed 1"); doButtonArray=PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)numButtons, (int)1, &buttonArray); if(doButtonArray){ buttonState=GetCurrentButtonState(); for(i=0;i<numButtons;i++) buttonArray[i]=(double)(buttonState & (1<<i)); } // Get cursor position: #ifndef __LP64__ // 32-Bit Carbon version: GetGlobalMouse(&mouseXY); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)mouseXY.h); PsychCopyOutDoubleArg(2, kPsychArgOptional, (double)mouseXY.v); #else // 64-Bit HIToolbox version (OSX 10.5 and later): HIPoint outPoint; HIGetMousePosition(kHICoordSpaceScreenPixel, NULL, &outPoint); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) outPoint.x); PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) outPoint.y); #endif // Return optional keyboard input focus status: if (numButtons > 0) { // Window provided? // We only have the function GetUserFocusWindow on 32-Bit Carbon. // We have a drop-in replacement in OSX/PsychCocoaGlue.c for 64-Bit Cocoa. if (PsychIsWindowIndexArg(2)) { // Yes: Check if it has focus. PsychAllocInWindowRecordArg(2, TRUE, &windowRecord); if (!PsychIsOnscreenWindow(windowRecord)) { PsychErrorExitMsg(PsychError_user, "Provided window handle isn't an onscreen window, as required."); } PsychCopyOutDoubleArg(4, kPsychArgOptional, (double) (GetUserFocusWindow() == windowRecord->targetSpecific.windowHandle) ? 1 : 0); } else { // No. Just always return "has focus": PsychCopyOutDoubleArg(4, kPsychArgOptional, (double) 1); } } // Return optional valuator values: Unimplemented on OS/X. Just return an empty matrix. // The buttonArray is just a dummy assignment without any meaning. PsychCopyOutDoubleMatArg(5, kPsychArgOptional, (int) 1, (int) 0, (int) 1, buttonArray); PsychCopyOutDoubleMatArg(6, kPsychArgOptional, (int) 1, (int) 0, (int) 1, buttonArray); #endif #if PSYCH_SYSTEM == PSYCH_WINDOWS static unsigned char disabledKeys[256]; static unsigned char firsttime = 1; int keysdown, i, priorityLevel; unsigned char keyState[256]; double* buttonArray; double numButtons, timestamp; PsychNativeBooleanType* buttonStates; POINT point; HANDLE currentProcess; DWORD oldPriority = NORMAL_PRIORITY_CLASS; const DWORD realtime_class = REALTIME_PRIORITY_CLASS; PsychWindowRecordType *windowRecord; PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; // Retrieve optional number of mouse buttons: numButtons = 0; PsychCopyInDoubleArg(1, FALSE, &numButtons); // Are we operating in 'GetMouseHelper' mode? numButtons>=0 indicates this. if (numButtons>=0) { // GetMouse-Mode: Return mouse button states and mouse cursor position: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)3, (int)1, &buttonArray); // Query and return mouse button state: PsychGetMouseButtonState(buttonArray); // Query and return cursor position in global coordinates: GetCursorPos(&point); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) point.x); PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) point.y); // Window provided? if (PsychIsWindowIndexArg(2)) { // Yes: Check if it has focus. PsychAllocInWindowRecordArg(2, TRUE, &windowRecord); if (!PsychIsOnscreenWindow(windowRecord)) { PsychErrorExitMsg(PsychError_user, "Provided window handle isn't an onscreen window, as required."); } PsychCopyOutDoubleArg(4, kPsychArgOptional, (double) (GetForegroundWindow() == windowRecord->targetSpecific.windowHandle) ? 1 : 0); } else { // No. Just always return "has focus": PsychCopyOutDoubleArg(4, kPsychArgOptional, (double) 1); } // Return optional valuator values: Unimplemented on Windows. Just return an empty matrix. // The ×tamp is just a dummy assignment without any meaning. PsychCopyOutDoubleMatArg(5, kPsychArgOptional, (int) 1, (int) 0, (int) 1, ×tamp); PsychCopyOutDoubleMatArg(6, kPsychArgOptional, (int) 1, (int) 0, (int) 1, buttonArray); } else { // 'KeyboardHelper' mode: We implement either KbCheck() or KbWait() via X11. // This is a hack to provide keyboard queries until a PsychHID() implementation // for Microsoft Windows is available... // Special codes -10 to -15? --> Console keyboard queries: if(numButtons <= -10 && numButtons >= -15) { ConsoleInputHelper((int) numButtons); return(PsychError_none); } if (firsttime) { // First time init: firsttime = 0; memset(keyState, 0, sizeof(keyState)); memset(disabledKeys, 0, sizeof(disabledKeys)); // These keycodes are always disabled: 0, 255: disabledKeys[0]=1; disabledKeys[255]=1; // Mouse buttone (left, right, middle) are also disabled by default: disabledKeys[1]=1; disabledKeys[2]=1; disabledKeys[4]=1; } if (numButtons==-1 || numButtons==-2) { // KbCheck()/KbWait() mode do { // Reset overall key state to "none pressed": keysdown=0; // Request current time of query: PsychGetAdjustedPrecisionTimerSeconds(×tamp); // Query state of all keys: for(i=1;i<255;i++){ keyState[i] = (GetAsyncKeyState(i) & -32768) ? 1 : 0; } // Disable all keys that are registered in disabledKeys. Check if // any non-disabled key is down. for (i=0; i<256; i++) { if (disabledKeys[i]>0) keyState[i] = 0; keysdown+=(unsigned int) keyState[i]; } // We repeat until any key pressed if in KbWait() mode, otherwise we // exit the loop after first iteration in KbCheck mode. if ((numButtons==-1) || ((numButtons==-2) && (keysdown>0))) break; // Sleep for a millisecond before next KbWait loop iteration: PsychWaitIntervalSeconds(0.001); } while(1); if (numButtons==-2) { // KbWait mode: Copy out time value. PsychCopyOutDoubleArg(1, kPsychArgOptional, timestamp); } else { // KbCheck mode: // Copy out overall keystate: PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown>0) ? 1 : 0); // Copy out timestamp: PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp); // Copy out keyboard state: PsychAllocOutBooleanMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates); // Build 256 elements return vector: for(i=0; i<255; i++) { buttonStates[i] = (PsychNativeBooleanType)((keyState[i+1]) ? 1 : 0); } // Special case: Null out last element: buttonStates[255] = (PsychNativeBooleanType) 0; } } if (numButtons==-3) { // Priority() - helper mode: The 2nd argument is the priority level: // Determine our processID: currentProcess = GetCurrentProcess(); // Get current scheduling policy: oldPriority = GetPriorityClass(currentProcess); // Map to PTB's scheme: switch(oldPriority) { case NORMAL_PRIORITY_CLASS: priorityLevel = 0; break; case HIGH_PRIORITY_CLASS: priorityLevel = 1; break; case REALTIME_PRIORITY_CLASS: priorityLevel = 2; break; default: priorityLevel = 0; } // Copy it out as optional return argument: PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) priorityLevel); // Query if a new level should be set: priorityLevel = -1; PsychCopyInIntegerArg(2, kPsychArgOptional, &priorityLevel); // Priority level provided? if (priorityLevel > -1) { // Map to new scheduling class: if (priorityLevel > 2) PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "Invalid Priority level: Requested Priority() level must not exceed 2."); switch(priorityLevel) { case 0: // Standard scheduling: SetPriorityClass(currentProcess, NORMAL_PRIORITY_CLASS); // Disable any MMCSS scheduling for us: PsychSetThreadPriority((psych_thread*) 0x1, 0, 0); break; case 1: // High priority scheduling: SetPriorityClass(currentProcess, HIGH_PRIORITY_CLASS); // Additionally try to schedule us MMCSS: This will lift us roughly into the // same scheduling range as REALTIME_PRIORITY_CLASS, even if we are non-admin users // on Vista and Windows-7 and later, however with a scheduler safety net applied. PsychSetThreadPriority((psych_thread*) 0x1, 10, 0); break; case 2: // Realtime scheduling: // This can fail if Matlab is not running under a user account with proper permissions: if ((0 == SetPriorityClass(currentProcess, REALTIME_PRIORITY_CLASS)) || (REALTIME_PRIORITY_CLASS != GetPriorityClass(currentProcess))) { // Failed to get RT-Scheduling. Let's try at least high priority scheduling: SetPriorityClass(currentProcess, HIGH_PRIORITY_CLASS); // Additionally try to schedule us MMCSS: This will lift us roughly into the // same scheduling range as REALTIME_PRIORITY_CLASS, even if we are non-admin users // on Vista and Windows-7 and later, however with a scheduler safety net applied. PsychSetThreadPriority((psych_thread*) 0x1, 10, 0); } break; } } // End of Priority() helper for Win32. } } #endif #if PSYCH_SYSTEM == PSYCH_LINUX double myvaluators[100]; int numvaluators; unsigned char keys_return[32]; char* keystring; PsychGenericScriptType *kbNames; CGDirectDisplayID dpy; Window rootwin, childwin, mywin; int i, j, mx, my, dx, dy; double mxd, myd, dxd, dyd; unsigned int mask_return; double timestamp; int numButtons; double* buttonArray; PsychNativeBooleanType* buttonStates; int keysdown; XEvent event_return; XKeyPressedEvent keypressevent; int screenNumber; int priorityLevel; struct sched_param schedulingparam; PsychWindowRecordType *windowRecord; int mouseIndex; XIButtonState buttons_return; XIModifierState modifiers_return; XIGroupState group_return; PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychCopyInIntegerArg(1, kPsychArgRequired, &numButtons); // Retrieve optional screenNumber argument: if (numButtons!=-5) { screenNumber = 0; if (PsychIsScreenNumberArg(2)) { PsychCopyInScreenNumberArg(2, FALSE, &screenNumber); } // Map screenNumber to X11 display handle and screenid: PsychGetCGDisplayIDFromScreenNumber(&dpy, screenNumber); if (PsychIsWindowIndexArg(2)) { PsychAllocInWindowRecordArg(2, TRUE, &windowRecord); if (!PsychIsOnscreenWindow(windowRecord)) { PsychErrorExitMsg(PsychError_user, "Provided window handle isn't an onscreen window, as required."); } screenNumber = windowRecord->screenNumber; mywin = windowRecord->targetSpecific.xwindowHandle; // Map screenNumber to X11 display handle and screenid: PsychGetCGDisplayIDFromScreenNumber(&dpy, screenNumber); } else { mywin = RootWindow(dpy, PsychGetXScreenIdForScreen(screenNumber)); } } // Default to "old school" mouse query - System default mouse via X core protocol: mouseIndex = -1; PsychCopyInIntegerArg(3, FALSE, &mouseIndex); // Are we operating in 'GetMouseHelper' mode? numButtons>=0 indicates this. if (numButtons>=0) { // Mouse pointer query mode: numvaluators = 0; if (mouseIndex >= 0) { // XInput-2 query for handling of multiple mouse pointers: // Query input device list for screen: int nDevices; XIDeviceInfo* indevs = PsychGetInputDevicesForScreen(screenNumber, &nDevices); // Sanity check: if (NULL == indevs) PsychErrorExitMsg(PsychError_user, "Sorry, your system does not support individual mouse pointer queries."); if (mouseIndex >= nDevices) PsychErrorExitMsg(PsychError_user, "Invalid 'mouseIndex' provided. No such device."); if ((indevs[mouseIndex].use != XIMasterPointer) && (indevs[mouseIndex].use != XISlavePointer) && (indevs[mouseIndex].use != XIFloatingSlave)) { PsychErrorExitMsg(PsychError_user, "Invalid 'mouseIndex' provided. Not a pointer device."); } // We requery the device info struct to retrieve updated live device state: // Crucial for slave pointers to get any state at all, but also needed on // master pointers to get the state of additional valuators, e.g., pen pressure, // touch area, tilt etc. for digitizer tablets, touch pads etc. For master pointers, // the primary 2 axis for 2D (x,y) position and the button/modifier state will be // queried via a dedicated XIQueryPointer() call, so that info gets overriden. indevs = XIQueryDevice(dpy, indevs[mouseIndex].deviceid, &numButtons); modifiers_return.effective = 0; // Query real number of mouse buttons and the raw button and axis state // stored inside the device itself. This is done mostly because slave pointer // devices don't support XIQueryPointer() so we get their relevant info from the // XIDeviceInfo struct itself: numButtons = 0; numvaluators = 0; memset(myvaluators, 0, sizeof(myvaluators)); if (PsychIsArgPresent(PsychArgOut, 6)) { // Usercode wants valuator info structs: for (i = 0; i < indevs->num_classes; i++) if (indevs->classes[i]->type == XIValuatorClass) numIValuators++; PsychAllocOutStructArray(6, TRUE, numIValuators, numValuatorStructFieldNames, valuatorInfo, &valuatorStruct); } for (i = 0; i < indevs->num_classes; i++) { // printf("Class %i: Type %i\n", i, (int) indevs->classes[i]->type); if (indevs->classes[i]->type == XIButtonClass) { // Number of buttons: For all pointers. numButtons = ((XIButtonClassInfo*) indevs->classes[i])->num_buttons; // Button state for slave pointers. Will get overriden for master pointers: buttons_return.mask = ((XIButtonClassInfo*) indevs->classes[i])->state.mask; buttons_return.mask_len = ((XIButtonClassInfo*) indevs->classes[i])->state.mask_len; } // Axis state for slave pointers. First two axis (x,y) will get overriden for master pointers: if (indevs->classes[i]->type == XIValuatorClass) { XIValuatorClassInfo* axis = (XIValuatorClassInfo*) indevs->classes[i]; if (axis->number == 0) mxd = axis->value; // x-Axis. if (axis->number == 1) myd = axis->value; // y-Axis. // Additional axis, e.g., digitizer tablet, touchpads etc.: if (axis->number >= 0 && axis->number < 100) { myvaluators[axis->number] = axis->value; numvaluators = (numvaluators >= axis->number + 1) ? numvaluators : axis->number + 1; } // Assign valuator info struct, if requested: if (valuatorStruct) { if (axis->label != None) { char* atomlabel = XGetAtomName(dpy, axis->label); PsychSetStructArrayStringElement("label", axis->number, atomlabel, valuatorStruct); XFree(atomlabel); } else { PsychSetStructArrayStringElement("label", axis->number, "None", valuatorStruct); } PsychSetStructArrayDoubleElement("min", axis->number, (double) axis->min, valuatorStruct); PsychSetStructArrayDoubleElement("max", axis->number, (double) axis->max, valuatorStruct); PsychSetStructArrayDoubleElement("resolution", axis->number, (double) axis->resolution, valuatorStruct); PsychSetStructArrayDoubleElement("mode", axis->number, (double) axis->mode, valuatorStruct); PsychSetStructArrayDoubleElement("sourceID", axis->number, (double) axis->sourceid, valuatorStruct); } // printf("AXIS %i, LABEL = %s, MIN = %f, MAX = %f, VAL = %f\n", axis->number, (char*) "NONE", (float) axis->min, (float) axis->max, (float) axis->value); } } // Add 32 buttons for modifier key state vector: numButtons += 32; // A real master pointer: Use official query for mouse devices. if (indevs->use == XIMasterPointer) { // Query pointer location and state: XIQueryPointer(dpy, indevs->deviceid, RootWindow(dpy, PsychGetXScreenIdForScreen(screenNumber)), &rootwin, &childwin, &mxd, &myd, &dxd, &dyd, &buttons_return, &modifiers_return, &group_return); } // Copy out mouse x and y position: PsychCopyOutDoubleArg(1, kPsychArgOptional, mxd); PsychCopyOutDoubleArg(2, kPsychArgOptional, myd); // Copy out mouse button state: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int) numButtons, (int)1, &buttonArray); memset(buttonArray, 0, sizeof(double) * numButtons); if (numButtons > 0) { // Mouse buttons: const int buttonOffset = 1; // Buttons start at bit 1, not 0 for some strange reason? At least so on Ubuntu 10.10 and 11.10 with 2 mice and 1 joystick? for (i = buttonOffset; (i < numButtons - 32) && ((i / 8 ) < buttons_return.mask_len); i++) { buttonArray[i - buttonOffset] = (double) ((buttons_return.mask[i / 8] & (1 << (i % 8))) ? 1 : 0); } // Free mask if retrieved via XIQueryPointer(): if (indevs->use == XIMasterPointer) free(buttons_return.mask); // Append modifier key state from associated master keyboard. Last 32 entries: for (i = 0; i < 32; i++) { buttonArray[numButtons - 32 + i] = (double) ((modifiers_return.effective & (1 << i)) ? 1 : 0); } } // Release live state info structure: XIFreeDeviceInfo(indevs); } else { // Old school core protocol query of virtual core pointer: XQueryPointer(dpy, RootWindow(dpy, PsychGetXScreenIdForScreen(screenNumber)), &rootwin, &childwin, &mx, &my, &dx, &dy, &mask_return); // Copy out mouse x and y position: PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) mx); PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) my); // Copy out mouse button state: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)numButtons, (int)1, &buttonArray); // Bits 8, 9 and 10 of mask_return seem to correspond to mouse buttons // 1, 2 and 3 of a mouse for some weird reason. Bits 0-7 describe keyboard modifier keys // like Alt, Ctrl, Shift, ScrollLock, NumLock, CapsLock... // We remap here, so the first three returned entries correspond to the mouse buttons and // the rest is attached behind, if requested... // Mouse buttons: Left, Middle, Right == 0, 1, 2, aka 1,2,3 in Matlab space... for (i=0; i<numButtons && i<3; i++) { buttonArray[i] = (mask_return & (1<<(i+8))) ? 1 : 0; } // Modifier keys 0 to 7 appended: for (i=3; i<numButtons && i<3+8; i++) { buttonArray[i] = (mask_return & (1<<(i-3))) ? 1 : 0; } // Everything else appended: for (i=11; i<numButtons; i++) { buttonArray[i] = (mask_return & (1<<i)) ? 1 : 0; } } // Return optional 4th argument: Focus state. Returns 1 if our window has // keyboard input focus, zero otherwise: XGetInputFocus(dpy, &rootwin, &i); PsychCopyOutDoubleArg(4, kPsychArgOptional, (double) (rootwin == mywin) ? 1 : 0); // Return optional valuator values: PsychCopyOutDoubleMatArg(5, kPsychArgOptional, (int) 1, (int) numvaluators, (int) 1, &myvaluators[0]); } else { // 'KeyboardHelper' mode: We implement either KbCheck() or KbWait() via X11. // This is a hack to provide keyboard queries until a PsychHID() implementation // for Linux is available... // Special codes -10 to -15? --> Console keyboard queries: if(numButtons <= -10 && numButtons >= -15) { ConsoleInputHelper((int) numButtons); return(PsychError_none); } if (numButtons==-1 || numButtons==-2) { // KbCheck()/KbWait() mode: // Switch X-Server into synchronous mode: We need this to get // a higher timing precision. XSynchronize(dpy, TRUE); do { // Reset overall key state to "none pressed": keysdown=0; // Request current keyboard state from X-Server: XQueryKeymap(dpy, keys_return); // Request current time of query: PsychGetAdjustedPrecisionTimerSeconds(×tamp); // Any key down? for (i=0; i<32; i++) keysdown+=(unsigned int) keys_return[i]; // We repeat until any key pressed if in KbWait() mode, otherwise we // exit the loop after first iteration in KbCheck mode. if ((numButtons==-1) || ((numButtons==-2) && (keysdown>0))) break; // Sleep for a few milliseconds before next KbWait loop iteration: PsychWaitIntervalSeconds(0.01); } while(1); if (numButtons==-2) { // Copy out time: PsychCopyOutDoubleArg(1, kPsychArgOptional, timestamp); } else { // KbCheck mode: // Copy out overall keystate: PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown>0) ? 1 : 0); // copy out timestamp: PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp); // Copy keyboard state: PsychAllocOutBooleanMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates); // Map 32 times 8 bitvector to 256 element return vector: for(i=0; i<32; i++) { for(j=0; j<8; j++) { buttonStates[i*8 + j] = (PsychNativeBooleanType)(keys_return[i] & (1<<j)) ? 1 : 0; } } } } else if (numButtons == -3) { // numButtons == -3 --> KbName mapping mode: // Return the full keyboard keycode to ASCII character code mapping table... PsychAllocOutCellVector(1, kPsychArgOptional, 256, &kbNames); for(i=0; i<256; i++) { // Map keyboard scan code to KeySym: keystring = XKeysymToString(XKeycodeToKeysym(dpy, i, 0)); if (keystring) { // Character found: Return its ASCII name string: PsychSetCellVectorStringElement(i, keystring, kbNames); } else { // No character for this keycode: PsychSetCellVectorStringElement(i, "", kbNames); } } } else if (numButtons == -4) { // GetChar() emulation. /* do { */ /* // Fetch next keypress event from queue, block if none is available... */ /* keystring = NULL; */ /* XNextEvent(dpy, &event_return); */ /* // Check for valid keypress event and extract character: */ /* if (event_return.type == KeyPress) { */ /* keypressevent = (XKeyPressedEvent) event_return; */ /* keystring = NULL; */ /* keystring = XKeysymToString(XKeycodeToKeysym(dpy, keypressevent.keycode, 0)); */ /* } */ /* // Repeat until a valid char is returned. */ /* } while (keystring == NULL); */ /* // Copy out character: */ /* PsychCopyOutCharArg(1, kPsychArgOptional, (char) keystring); */ /* // Copy out time: */ /* PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) keypressevent.time); */ } else if (numButtons==-5) { // Priority() - helper mode: The 2nd argument is the priority level: // Query scheduling policy and priority: pthread_getschedparam(pthread_self(), &priorityLevel, &schedulingparam); // If scheduling mode is a realtime mode (RoundRobin realtime RR, or FIFO realtime), // then assign RT priority level (range 1-99) as current priorityLevel, otherwise // assign non realtime priority level zero: priorityLevel = (priorityLevel == SCHED_RR || priorityLevel == SCHED_FIFO) ? schedulingparam.sched_priority : 0; // Copy it out as optional return argument: PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) priorityLevel); // Query if a new level should be set: priorityLevel = -1; PsychCopyInIntegerArg(2, kPsychArgOptional, &priorityLevel); errno=0; // Priority level provided? if (priorityLevel > -1) { // Map to new scheduling class: if (priorityLevel > 99 || priorityLevel < 0) PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "Invalid Priority level: Requested Priority() level must be between zero and 99!"); if (priorityLevel > 0) { // Realtime FIFO scheduling and all pages of Matlab/Octave locked into memory: schedulingparam.sched_priority = priorityLevel; priorityLevel = pthread_setschedparam(pthread_self(), SCHED_FIFO, &schedulingparam); if (priorityLevel == -1) { // Failed! if(!PsychPrefStateGet_SuppressAllWarnings()) { printf("PTB-ERROR: Failed to enable realtime-scheduling with Priority(%i) [%s]!\n", schedulingparam.sched_priority, strerror(errno)); if (errno==EPERM) { printf("PTB-ERROR: You need to run Matlab/Octave with root-privileges, or run the script PsychLinuxConfiguration once for this to work.\n"); } } errno=0; } else { // RT-Scheduling active. Lock all current and future memory: priorityLevel = mlockall(MCL_CURRENT | MCL_FUTURE); if (priorityLevel!=0) { // Failed! Report problem as warning, but don't worry further. if(!PsychPrefStateGet_SuppressAllWarnings()) printf("PTB-WARNING: Failed to enable system memory locking with Priority(%i) [%s]!\n", schedulingparam.sched_priority, strerror(errno)); // Undo any possibly partial mlocks.... munlockall(); errno=0; } } } else { // Standard scheduling and no memory locking: schedulingparam.sched_priority = 0; priorityLevel = pthread_setschedparam(pthread_self(), SCHED_OTHER, &schedulingparam); if (priorityLevel == -1) { // Failed! if(!PsychPrefStateGet_SuppressAllWarnings()) { printf("PTB-ERROR: Failed to disable realtime-scheduling with Priority(%i) [%s]!\n", schedulingparam.sched_priority, strerror(errno)); if (errno==EPERM) { printf("PTB-ERROR: You need to run Matlab/Octave with root-privileges, or run the script PsychLinuxConfiguration once for this to work.\n"); } } errno=0; } munlockall(); errno=0; } // End of setup of new Priority... } // End of Priority() helper for Linux. } } // End of special functions handling for Linux... #endif 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); }