PsychError SCREENBeginOpenGL(void) { static char useString[] = "Screen('BeginOpenGL', windowPtr [, sharecontext=0]);"; static char synopsisString[] = "Prepare window 'windowPtr' for OpenGL rendering by external OpenGL code. " "This allows to use OpenGL drawing routines other than the ones implemented " "in Screen() to draw to a Psychtoolbox onscreen- or offscreen window via execution of " "OpenGL commands. Typical clients of this function are mogl (Richard F. Murrays OpenGL for Matlab wrapper), " "the new Eyelink-Toolbox and third party Matlab Mex-Files which contain OpenGL rendering routines. " "You *have to* call this command once before using any of those external drawing commands for the window. " "After drawing, you *must* switch back to PTB's rendering via the Screen('EndOpenGL', windowPtr); command. " "Normally, you won't provide the optional flag 'sharecontext', so PTB will automatically isolate the OpenGL " "state of your code from its internal state. However, if you provide sharecontext=1, then PTB will allow " "your code to use and affect PTBs internal context. Only do this if you really know what you're doing! " "If you provide sharecontext=2 then PTB will give you your own private context, but it will synchronize " "the state of that context with its internal state - Seldomly needed, but here for your convenience. " "The context state isolation is as strict as possible without seriously affecting performance and functionality: " "All OpenGL context state is separated, with two exceptions: The framebuffer binding (if any) is always synchronized " "with PTB (and reset to zero when calling 'EndOpenGL' or another Screen command) to allow external code to transparently " "render into PTBs internal framebuffers - Needed for the imaging pipeline to work. Ressources like textures, display lists, " "FBOs, VBOs, PBOs and GLSL shaders are shared between PTB and your code as well for efficiency reasons. Both types of " "ressource sharing shouldn't be a problem, because either you are a beginner or advanced OpenGL programmer and won't use " "those facilities anyway, or you are an expert user - in which case you'll know how to prevent any conflicts easily."; static char seeAlsoString[] = "EndOpenGL SetOpenGLTexture GetOpenGLTexture moglcore"; PsychWindowRecordType *windowRecord; GLint fboid, coltexid, ztexid, stexid; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(2)); // The maximum number of inputs PsychErrorExit(PsychRequireNumInputArgs(1)); // Number of required inputs. PsychErrorExit(PsychCapNumOutputArgs(0)); // The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); // Already in userspace mode? if (PsychIsUserspaceRendering()) PsychErrorExitMsg(PsychError_user, "Tried to call Screen('BeginOpenGL'), but userspace rendering is already active! Missing or mismatched Screen('EndOpenGL')? Check your code."); // (Optional) context sharing flag provided? sharecontext = 0; PsychCopyInIntegerArg(2, FALSE, &sharecontext); if (sharecontext<0 || sharecontext>2) PsychErrorExitMsg(PsychError_user, "Invalid value for 'sharecontext' provided. Not in range 0 to 2."); // Master override: If context isolation is disabled then we use the PTB internal context... if (PsychPrefStateGet_ConserveVRAM() & kPsychDisableContextIsolation) sharecontext = 1; // Set it as drawing target: This will set up the proper FBO bindings as well: PsychSetDrawingTarget(windowRecord); // Store it as a reference for later 'EndOpenGL' call: preswitchWindowRecord = windowRecord; // Userspace wants its own private rendering context, optionally updated to match PTBs internal state? if (sharecontext == 0 || sharecontext == 2) { // Yes. This is the normal case for 3D rendering. MOGLs and PTBs contexts are separated to // increase robustness, only ressources like textures, display lists, PBO's, VBO's, FBO's // and GLSL shaders are shared, but not the current renderstate. // Make sure 3D rendering is globally enabled, otherwise this is considered a userspace bug: if (PsychPrefStateGet_3DGfx()==0) PsychErrorExitMsg(PsychError_user, "Tried to call 'BeginOpenGL' for external rendering, but 3D rendering not globally enabled! Call 'InitializeMatlabOpenGL' at the beginning of your script!!"); // Query current FBO binding. We need to manually transfer this to the userspace context, so // it can render into our window: if (glBindFramebufferEXT) { fboid = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fboid); if (fboid>0) { // Query attachments of FBO: glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, &coltexid); glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, &ztexid); glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, &stexid); } } // Flush our context before context switch: glFlush(); // Unbind possible FBOs, so system FB is active in our context: if (glBindFramebufferEXT && (fboid > 0)) { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glFlush(); } // Switch to userspace context for this window, optionally sync state with PTBs context: PsychOSSetUserGLContext(windowRecord, (sharecontext==2) ? TRUE : FALSE); // All following ops apply to the usercontext, not our internal context: // Manually establish proper FBO binding for userspace. This will get reset automaticaly on back-transition // inside PsychSetGLContext on its first invocation. If we are in non-imaging mode then there's nothing to do. if (glBindFramebufferEXT && (fboid > 0)) { if (!glIsFramebufferEXT(fboid)) { // Special case: Need to bind a special FBO and the underlying OpenGL driver is faulty, // i.e. it doesn't share FBO names accross our OpenGL contexts as it should according to // spec.: We manually create a clone of our internal FBO - Create an FBO in the userspace // context with the same FBO handle, then manually reattach the proper attachments... if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Faulty graphics driver - FBO sharing doesn't work properly, trying work-around. Update your drivers as soon as possible!\n"); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboid); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_EXT, coltexid, 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_EXT, ztexid, 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_EXT, stexid, 0); if (GL_FRAMEBUFFER_COMPLETE_EXT != glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)) { // Game over :( PsychErrorExitMsg(PsychError_internal, "Graphics driver malfunction: Failed to clone PTBs internal FBO for userspace GLContext inside SCREENBeginOpenGL as part of workaround code! Upgrade your gfx-drivers!!"); } // If we reach this point, then the workaround for the worst OS in existence has worked. } else { // Need to bind a special FBO and the system works correctly - no workaround needed. Just bind it in new context: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboid); } } // Is this the first time that the userspace rendering context of this // onscreen window is selected for real userspace rendering? if (windowRecord->needsViewportSetup && PsychIsOnscreenWindow(windowRecord)) { // Yes. Need to perform one-time setup actions for this context: windowRecord->needsViewportSetup = FALSE; // Need to setup glViewPort, scissor rectangle, projection and modelview // matrices to values that match the windows client rectangle. We need to // do this here because some imaging pipeline display modes, e.g, stereomodes // for top-bottom stereo or dualview stereo may have altered the useable client // rendering area after the context was initially created. OpenGL spec states that // at least the viewport and scissor rectangles are set to the full client window // area at first bind of a context to its drawable, so we emulate this here on first // 'BeginOpenGL' to avoid unpleasant surprises for unsuspecting users: PsychSetupView(windowRecord, FALSE); } // Running without imaging pipeline and a stereo mode is active? if ((windowRecord->stereomode) > 0 && !(windowRecord->imagingMode & kPsychNeedFastBackingStore)) { // Perform setup work for stereo drawbuffers in fixed function mode: PsychSwitchFixedFunctionStereoDrawbuffer(windowRecord); } } else { // Userspace shares context with PTB. Let's disable possibly bound GLSL shaders: PsychSetShader(windowRecord, 0); } // Check for GL errors: PsychTestForGLErrors(); // Set the userspace flag: PsychSetUserspaceGLFlag(TRUE); // Ready for userspace rendering: 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 SCREENNull(void) { const double defaultMatrix[] = {1.1, 1.2, 1.3, 1.4, 2.1, 2.2, 2.3, 2.4}; const double defaultM=2, defaultN=4; double tempValue; double *array; int i, m,n, p, numInArgs, numOutArgs, numNamedOutArgs; char *str; PsychArgFormatType format; const char defaultString[] = "I am the default string\n"; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //demonstrate how we find the function and subfunction names //printf("Psychtoolbox function: %s, subfunction %s\n", PsychGetModuleName(), PsychGetFunctionName() ); //copy all the input argument to their outputs if we have doubles, if not error. numInArgs = PsychGetNumInputArgs(); numOutArgs = PsychGetNumOutputArgs(); numNamedOutArgs = PsychGetNumNamedOutputArgs(); PsychErrorExit(PsychCapNumOutputArgs(numInArgs)); /* printf("number of input arguments: %d\n", numInArgs); printf("number of output arguments: %d\n", numOutArgs); printf("number of named output arguments: %d\n", numNamedOutArgs); */ //iterate over each of the supplied inputs. If the input is a two-dimensional array //of doubles or a character array, then copy it to the output. for(i=1;i<=numInArgs;i++){ format = PsychGetArgType(i); switch(format){ case PsychArgType_double: if(PsychGetArgM(i)==1 && PsychGetArgN(i)==1){ tempValue=i; //if 1x1 double then the default return value is the arg position. PsychCopyInDoubleArg(i, FALSE, &tempValue); PsychCopyOutDoubleArg(i, FALSE, tempValue); }else{ PsychAllocInDoubleMatArg(i, FALSE, &m, &n, &p, &array); PsychCopyOutDoubleMatArg(i, FALSE, m, n, p, array); } break; case PsychArgType_char: str=NULL; //This tells PsychGetCharArg() to use its own (volatile) memory. PsychAllocInCharArg(i, FALSE, &str); PsychCopyOutCharArg(i, FALSE, str); break; case PsychArgType_default: PsychCopyOutCharArg(i, FALSE, defaultString); break; } } return(PsychError_none); }
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 SCREENPreference(void) { PsychArgFormatType arg1Type; char *preferenceName, *newFontName; const char *tableCreator, *oldDefaultFontName; Boolean preferenceNameArgumentValid, booleanInput, ignoreCase, tempFlag, textAlphaBlendingFlag, suppressAllWarningsFlag; int numInputArgs, i, newFontStyleNumber, newFontSize, tempInt; double returnDoubleValue, inputDoubleValue; //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(3)); PsychErrorExit(PsychRequireNumInputArgs(1)); PsychErrorExit(PsychCapNumOutputArgs(1)); 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)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, "DefaultFontStyle")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextStyle()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontStyleNumber); PsychPrefStateSet_DefaultTextStyle(newFontStyleNumber); } 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, "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, "ConserveVRAM")){ 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, "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){ PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag); PsychPrefStateSet_3DGfx(tempFlag); } 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 PsychErrorExit(PsychError_unrecognizedPreferenceName); } if(!preferenceNameArgumentValid) PsychErrorExitMsg(PsychError_user, "Invalid arguments to preferences command"); return(PsychError_none); }
// M$-Windows implementation of Screen('Computer'): This is very rudimentary for now. // We only report the operating sytem type (="Windows") and MAC-Address, but don't report any more useful // information. MAC query does not work yet - We do not have the neccessary libraries to compile the code :( PsychError SCREENComputer(void) { // Info struct for queries to OS: OSVERSIONINFO osvi; char versionString[256]; // const char *majorStructFieldNames[]={"macintosh", "windows", "osx" ,"linux", "kern", "hw", "processUserLongName", // "processUserShortName", "consoleUserName", "machineName", "localHostName", "location", "MACAddress", "system" }; const char *majorStructFieldNames[]={"macintosh", "windows", "osx" ,"linux", "system", "MACAddress"}; const char *kernStructFieldNames[]={"ostype", "osrelease", "osrevision", "version","hostname"}; const char *hwStructFieldNames[]={"machine", "model", "ncpu", "physmem", "usermem", "busfreq", "cpufreq"}; int numMajorStructDimensions=1, numKernStructDimensions=1, numHwStructDimensions=1; int numMajorStructFieldNames=5, numKernStructFieldNames=5, numHwStructFieldNames=7; // char ethernetMACStr[20]; // unsigned char MACData[6]; // UUID uuid; int i; PsychGenericScriptType *kernStruct, *hwStruct, *majorStruct; //all subfunctions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(0)); //fill the major struct PsychAllocOutStructArray(1, FALSE, numMajorStructDimensions, numMajorStructFieldNames, majorStructFieldNames, &majorStruct); PsychSetStructArrayDoubleElement("macintosh", 0, 0, majorStruct); PsychSetStructArrayDoubleElement("windows", 0, 1, majorStruct); PsychSetStructArrayDoubleElement("linux", 0, 0, majorStruct); PsychSetStructArrayDoubleElement("osx", 0, 0, majorStruct); // Query info about Windows version: versionString[0]=0; memset(&osvi, 0, sizeof(OSVERSIONINFO)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osvi); // Convert into string with major.minor.buildnumber - Name of service packs (max 128 chars) etc.: // Versions to products: 6.1 = Windows-7, 6.0 = Vista, 5.2 = Windows Server 2003, 5.1 = WindowsXP, 5.0 = Windows 2000, 4.x = NT sprintf(versionString, "%i.%i.%i - %s", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber, (char*) osvi.szCSDVersion); PsychSetStructArrayStringElement("system", 0, versionString, majorStruct); // Query hardware MAC address of primary ethernet interface: This is a unique id of the computer, // good enough to disambiguate our statistics: // sprintf(ethernetMACStr, "00:00:00:00:00:00"); // Ask OS to create UUID. Windows uses the MAC address of primary interface // in bytes 2 to 7 to do this: //UuidCreateSequential( &uuid ); // Ask OS to create UUID //for (i=2; i<8; i++) MACData[i - 2] = uuid.Data4[i]; //sprintf(ethernetMACStr, "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X", // MACData[0] & 0xff, MACData[1] & 0xff, // MACData[2] & 0xff, MACData[3] & 0xff, // MACData[4] & 0xff, MACData[5] & 0xff); // PsychSetStructArrayStringElement("MACAddress", 0, ethernetMACStr, majorStruct); return(PsychError_none); }
PsychError PsychModuleInit(void) { // Initialize all Screen('Preference', ...); settings to startup defaults: PrepareScreenPreferences(); // Register the project exit function PsychErrorExitMsg(PsychRegisterExit(&ScreenExitFunction), "Failed to register the Screen exit function."); // Register the project function which is called when the module // is invoked with no arguments: PsychErrorExitMsg(PsychRegister(NULL, &PsychDisplayScreenSynopsis), "Failed to register the Screen synopsis function."); // Register the module name PsychErrorExitMsg(PsychRegister("Screen", NULL), "Failed to register the Screen module name."); // Register named subfunctions // Enable for debugging purposes PsychErrorExit(PsychRegister("Null", &SCREENNull)); PsychErrorExit(PsychRegister("Version", &MODULEVersion)); PsychErrorExit(PsychRegister("Computer", &SCREENComputer)); PsychErrorExit(PsychRegister("Screens", &SCREENScreens)); PsychErrorExit(PsychRegister("PixelSize",&SCREENPixelSize)); PsychErrorExit(PsychRegister("PixelSizes",&SCREENPixelSizes)); PsychErrorExit(PsychRegister("OpenWindow", &SCREENOpenWindow)); PsychErrorExit(PsychRegister("OpenOffscreenWindow", &SCREENOpenOffscreenWindow)); PsychErrorExit(PsychRegister("Close", &SCREENClose)); PsychErrorExit(PsychRegister("CloseAll", &SCREENCloseAll)); PsychErrorExit(PsychRegister("Flip", &SCREENFlip)); PsychErrorExit(PsychRegister("AsyncFlipBegin", &SCREENFlip)); PsychErrorExit(PsychRegister("AsyncFlipEnd", &SCREENFlip)); PsychErrorExit(PsychRegister("AsyncFlipCheckEnd", &SCREENFlip)); PsychErrorExit(PsychRegister("WaitUntilAsyncFlipCertain" , &SCREENWaitUntilAsyncFlipCertain)); PsychErrorExit(PsychRegister("FillRect", &SCREENFillRect)); PsychErrorExit(PsychRegister("GetImage", &SCREENGetImage)); PsychErrorExit(PsychRegister("PutImage", &SCREENPutImage)); PsychErrorExit(PsychRegister("HideCursorHelper", &SCREENHideCursorHelper)); PsychErrorExit(PsychRegister("ShowCursorHelper", &SCREENShowCursorHelper)); PsychErrorExit(PsychRegister("SetMouseHelper", &SCREENSetMouseHelper)); PsychErrorExit(PsychRegister("Rect", &SCREENRect)); PsychErrorExit(PsychRegister("WindowScreenNumber", &SCREENWindowScreenNumber)); PsychErrorExit(PsychRegister("Windows", &SCREENWindows)); PsychErrorExit(PsychRegister("WindowKind", &SCREENWindowKind)); PsychErrorExit(PsychRegister("IsOffscreen", &SCREENIsOffscreen)); PsychErrorExit(PsychRegister("ReadNormalizedGammaTable", &SCREENReadNormalizedGammaTable)); PsychErrorExit(PsychRegister("LoadNormalizedGammaTable", &SCREENLoadNormalizedGammaTable)); PsychErrorExit(PsychRegister("FrameRate", &SCREENNominalFramerate)); PsychErrorExit(PsychRegister("NominalFrameRate", &SCREENNominalFramerate)); PsychErrorExit(PsychRegister("glPoint", &SCREENglPoint)); PsychErrorExit(PsychRegister("gluDisk", &SCREENgluDisk)); PsychErrorExit(PsychRegister("FillOval", &SCREENFillOval)); PsychErrorExit(PsychRegister("FrameOval", &SCREENFrameOval)); PsychErrorExit(PsychRegister("TextModes", &SCREENTextModes)); PsychErrorExit(PsychRegister("TextMode", &SCREENTextMode)); PsychErrorExit(PsychRegister("TextSize", &SCREENTextSize)); PsychErrorExit(PsychRegister("TextStyle", &SCREENTextStyle)); PsychErrorExit(PsychRegister("TextFont", &SCREENTextFont)); PsychErrorExit(PsychRegister("TextBounds", &SCREENTextBounds)); PsychErrorExit(PsychRegister("DrawText", &SCREENDrawText)); PsychErrorExit(PsychRegister("TextColor", &SCREENTextColor)); PsychErrorExit(PsychRegister("Preference", &SCREENPreference)); PsychErrorExit(PsychRegister("MakeTexture", &SCREENMakeTexture)); PsychErrorExit(PsychRegister("DrawTexture", &SCREENDrawTexture)); PsychErrorExit(PsychRegister("FrameRect", &SCREENFrameRect)); PsychErrorExit(PsychRegister("DrawLine", &SCREENDrawLine)); PsychErrorExit(PsychRegister("FillPoly", &SCREENFillPoly)); PsychErrorExit(PsychRegister("FramePoly", &SCREENFramePoly)); PsychErrorExit(PsychRegister("GlobalRect", &SCREENGlobalRect)); PsychErrorExit(PsychRegister("DrawDots", &SCREENDrawDots)); PsychErrorExit(PsychRegister("GetTimeList", &SCREENGetTimeList)); PsychErrorExit(PsychRegister("ClearTimeList", &SCREENClearTimeList)); PsychErrorExit(PsychRegister("BlendFunction", &SCREENBlendFunction)); PsychErrorExit(PsychRegister("WindowSize", &SCREENWindowSize)); PsychErrorExit(PsychRegister("GetMouseHelper", &SCREENGetMouseHelper)); PsychErrorExit(PsychRegister("TextBackgroundColor", &SCREENTextBackgroundColor)); PsychErrorExit(PsychRegister("LineStipple", &SCREENLineStipple)); PsychErrorExit(PsychRegister("SelectStereoDrawBuffer", &SCREENSelectStereoDrawBuffer)); PsychErrorExit(PsychRegister("DrawingFinished", &SCREENDrawingFinished)); PsychErrorExit(PsychRegister("DrawLines", &SCREENDrawLines)); PsychErrorExit(PsychRegister("GetFlipInterval", &SCREENGetFlipInterval)); PsychErrorExit(PsychRegister("CloseMovie", &SCREENCloseMovie)); PsychErrorExit(PsychRegister("OpenMovie", &SCREENOpenMovie)); PsychErrorExit(PsychRegister("PlayMovie", &SCREENPlayMovie)); PsychErrorExit(PsychRegister("SetMovieTimeIndex", &SCREENSetMovieTimeIndex)); PsychErrorExit(PsychRegister("GetMovieTimeIndex", &SCREENGetMovieTimeIndex)); PsychErrorExit(PsychRegister("GetMovieImage", &SCREENGetMovieImage)); PsychErrorExit(PsychRegister("glPushMatrix", &SCREENglPushMatrix)); PsychErrorExit(PsychRegister("glPopMatrix", &SCREENglPopMatrix)); PsychErrorExit(PsychRegister("glLoadIdentity", &SCREENglLoadIdentity)); PsychErrorExit(PsychRegister("glTranslate", &SCREENglTranslate)); PsychErrorExit(PsychRegister("glScale", &SCREENglScale)); PsychErrorExit(PsychRegister("glRotate", &SCREENglRotate)); PsychErrorExit(PsychRegister("PreloadTextures", &SCREENPreloadTextures)); PsychErrorExit(PsychRegister("FillArc", &SCREENFillArc)); PsychErrorExit(PsychRegister("DrawArc", &SCREENDrawArc)); PsychErrorExit(PsychRegister("FrameArc", &SCREENFrameArc)); PsychErrorExit(PsychRegister("CopyWindow", &SCREENCopyWindow)); PsychErrorExit(PsychRegister("WaitBlanking", &SCREENWaitBlanking)); PsychErrorExit(PsychRegister("GetOpenGLTexture", &SCREENGetOpenGLTexture)); PsychErrorExit(PsychRegister("SetOpenGLTexture", &SCREENSetOpenGLTexture)); PsychErrorExit(PsychRegister("BeginOpenGL", &SCREENBeginOpenGL)); PsychErrorExit(PsychRegister("EndOpenGL", &SCREENEndOpenGL)); PsychErrorExit(PsychRegister("OpenVideoCapture", &SCREENOpenVideoCapture)); PsychErrorExit(PsychRegister("CloseVideoCapture", &SCREENCloseVideoCapture)); PsychErrorExit(PsychRegister("StartVideoCapture", &SCREENStartVideoCapture)); PsychErrorExit(PsychRegister("StopVideoCapture", &SCREENStopVideoCapture)); PsychErrorExit(PsychRegister("GetCapturedImage", &SCREENGetCapturedImage)); PsychErrorExit(PsychRegister("SetVideoCaptureParameter", &SCREENSetVideoCaptureParameter)); PsychErrorExit(PsychRegister("VideoCaptureDevices", &SCREENVideoCaptureDevices)); PsychErrorExit(PsychRegister("LoadCLUT", &SCREENLoadCLUT)); PsychErrorExit(PsychRegister("DisplaySize", &SCREENDisplaySize)); PsychErrorExit(PsychRegister("SetOpenGLTextureFromMemPointer", &SCREENSetOpenGLTextureFromMemPointer)); PsychErrorExit(PsychRegister("ColorRange", &SCREENColorRange)); PsychErrorExit(PsychRegister("HookFunction", &SCREENHookFunction)); PsychErrorExit(PsychRegister("OpenProxy", &SCREENOpenProxy)); PsychErrorExit(PsychRegister("TransformTexture", &SCREENTransformTexture)); PsychErrorExit(PsychRegister("DrawTextures", &SCREENDrawTextures)); PsychErrorExit(PsychRegister("GetWindowInfo", &SCREENGetWindowInfo)); PsychErrorExit(PsychRegister("GetOpenGLDrawMode", &SCREENGetOpenGLDrawMode)); PsychErrorExit(PsychRegister("Resolutions", &SCREENResolutions)); PsychErrorExit(PsychRegister("Resolution", &SCREENResolution)); PsychErrorExit(PsychRegister("ConfigureDisplay", &SCREENConfigureDisplay)); PsychErrorExit(PsychRegister("CreateMovie", &SCREENCreateMovie)); PsychErrorExit(PsychRegister("FinalizeMovie", &SCREENFinalizeMovie)); PsychErrorExit(PsychRegister("AddFrameToMovie", &SCREENGetImage)); PsychErrorExit(PsychRegister("AddAudioBufferToMovie", &SCREENAddAudioBufferToMovie)); PsychErrorExit(PsychRegister("GetFlipInfo", &SCREENGetFlipInfo)); PsychSetModuleAuthorByInitials("awi"); PsychSetModuleAuthorByInitials("dhb"); PsychSetModuleAuthorByInitials("dgp"); PsychSetModuleAuthorByInitials("kas"); PsychSetModuleAuthorByInitials("fjc"); PsychSetModuleAuthorByInitials("mk"); PsychSetModuleAuthorByInitials("cb"); InitializeSynopsis(); InitWindowBank(); PsychMovieInit(); PsychVideoCaptureInit(); PsychMovieWritingInit(); // Call wait-routine for 0.1 secs: This to initialize the time glue on MS-Windows, // so the first call to a timing function won't delay: PsychWaitIntervalSeconds(0.1); // Reset the "userspaceGL" flag which tells PTB that userspace GL rendering was active // due to Screen('BeginOpenGL') command. PsychSetUserspaceGLFlag(FALSE); // Call display glue init last, as its error handling could go crazy if triggered // before the init routines above got executed: InitializePsychDisplayGlue(); return(PsychError_none); }
PsychError SCREENPutImage(void) { PsychRectType windowRect,positionRect; int ix, iy, numPlanes, bitsPerColor, matrixRedIndex, matrixGreenIndex, matrixBlueIndex, matrixAlphaIndex, matrixGrayIndex; int inputM, inputN, inputP, positionRectWidth, positionRectHeight; PsychWindowRecordType *windowRecord; unsigned char *inputMatrixByte; double *inputMatrixDouble; GLuint *compactMat, matrixGrayValue, matrixRedValue, matrixGreenValue, matrixBlueValue, matrixAlphaValue, compactPixelValue; PsychArgFormatType inputMatrixType; GLfloat xZoom=1, yZoom=-1; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the number of inputs PsychErrorExit(PsychCapNumInputArgs(4)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get the image matrix inputMatrixType=PsychGetArgType(2); switch(inputMatrixType){ case PsychArgType_none : case PsychArgType_default: PsychErrorExitMsg(PsychError_user, "imageArray argument required"); break; case PsychArgType_uint8 : PsychAllocInUnsignedByteMatArg(2, TRUE, &inputM, &inputN, &inputP, &inputMatrixByte); break; case PsychArgType_double : PsychAllocInDoubleMatArg(2, TRUE, &inputM, &inputN, &inputP, &inputMatrixDouble); break; default : PsychErrorExitMsg(PsychError_user, "imageArray must be uint8 or double type"); break; } //get the window and get the rect and stuff PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); numPlanes=PsychGetNumPlanesFromWindowRecord(windowRecord); bitsPerColor=PsychGetColorSizeFromWindowRecord(windowRecord); PsychGetRectFromWindowRecord(windowRect, windowRecord); if(PsychCopyInRectArg(3, FALSE, positionRect)){ positionRectWidth=(int)PsychGetWidthFromRect(positionRect); positionRectHeight=(int)PsychGetHeightFromRect(positionRect); if(inputP != 1 && inputP != 3 && inputP != 4) PsychErrorExitMsg(PsychError_user, "Third dimension of image matrix must be 1, 3, or 4"); if( positionRectWidth != inputN || positionRectHeight != inputM){ //calculate the zoom factor xZoom=(GLfloat)positionRectWidth/(GLfloat)inputN; yZoom=-((GLfloat)positionRectHeight/(GLfloat)inputM); } }else{ positionRect[kPsychLeft]=0; positionRect[kPsychTop]=0; positionRect[kPsychRight]=inputN; positionRect[kPsychBottom]=inputM; PsychCenterRect(positionRect, windowRect, positionRect); //This should be centered } //put up the image if(numPlanes==1){ //screen planes, not image matrix planes. PsychErrorExitMsg(PsychError_unimplemented, "Put Image does not yet support indexed mode"); //remember to test here for inputP==3 because that would be wrong. }else if(numPlanes==4){ compactMat=(GLuint *)mxMalloc(sizeof(GLuint) * inputN * inputM); for(ix=0;ix<inputN;ix++){ for(iy=0;iy<inputM;iy++){ if(inputP==1){ matrixGrayIndex=PsychIndexElementFrom3DArray(inputM, inputN, 1, iy, ix, 0); if(inputMatrixType==PsychArgType_uint8) matrixGrayValue=(GLuint)inputMatrixByte[matrixGrayIndex]; else //inputMatrixType==PsychArgType_double matrixGrayValue=(GLuint)inputMatrixDouble[matrixGrayIndex]; compactPixelValue=((matrixGrayValue<<8 | matrixGrayValue)<<8 | matrixGrayValue)<<8 | 255; compactMat[iy*inputN+ix]=compactPixelValue; }else if(inputP==3){ matrixRedIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 0); matrixGreenIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 1); matrixBlueIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 2); if(inputMatrixType==PsychArgType_uint8){ matrixRedValue=(GLuint)inputMatrixByte[matrixRedIndex]; matrixGreenValue=(GLuint)inputMatrixByte[matrixGreenIndex]; matrixBlueValue=(GLuint)inputMatrixByte[matrixBlueIndex]; matrixAlphaValue=(GLuint)255; }else{ matrixRedValue=(GLuint)inputMatrixDouble[matrixRedIndex]; matrixGreenValue=(GLuint)inputMatrixDouble[matrixGreenIndex]; matrixBlueValue=(GLuint)inputMatrixDouble[matrixBlueIndex]; matrixAlphaValue=(GLuint)255; } compactPixelValue= ((matrixRedValue<<8 | matrixGreenValue )<<8 | matrixBlueValue)<<8 | matrixAlphaValue; compactMat[iy*inputN+ix]=compactPixelValue; }else if(inputP==4){ matrixRedIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 0); matrixGreenIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 1); matrixBlueIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 2); matrixAlphaIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 3); if(inputMatrixType==PsychArgType_uint8){ matrixRedValue=(GLuint)inputMatrixByte[matrixRedIndex]; matrixGreenValue=(GLuint)inputMatrixByte[matrixGreenIndex]; matrixBlueValue=(GLuint)inputMatrixByte[matrixBlueIndex]; matrixAlphaValue=(GLuint)inputMatrixByte[matrixAlphaIndex]; }else{ matrixRedValue=(GLuint)inputMatrixDouble[matrixRedIndex]; matrixGreenValue=(GLuint)inputMatrixDouble[matrixGreenIndex]; matrixBlueValue=(GLuint)inputMatrixDouble[matrixBlueIndex]; matrixAlphaValue=(GLuint)inputMatrixDouble[matrixAlphaIndex]; } compactPixelValue= ((matrixRedValue<<8 | matrixGreenValue )<<8 | matrixBlueValue)<<8 | matrixAlphaValue; compactMat[iy*inputN+ix]=compactPixelValue; } } } PsychSetGLContext(windowRecord); PsychUpdateAlphaBlendingFactorLazily(windowRecord); glRasterPos2i((GLint)(positionRect[kPsychLeft]), (GLint)(positionRect[kPsychTop])); PsychTestForGLErrors(); glPixelStorei(GL_UNPACK_ALIGNMENT, (GLint)(sizeof(GLuint))); //4 PsychTestForGLErrors(); glPixelZoom(xZoom,yZoom); PsychTestForGLErrors(); glDrawPixels(inputN, inputM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, compactMat); free((void *)compactMat); PsychTestForGLErrors(); PsychFlushGL(windowRecord); //OS X: This does nothing if we are multi buffered, otherwise it glFlushes PsychTestForGLErrors(); }else if(numPlanes==3) PsychErrorExitMsg(PsychError_unimplemented, "PutImage found hardware without an alpha channel."); return(PsychError_none); }
PsychError PsychModuleInit(void) { //register the project exit function PsychErrorExit(PsychRegisterExit(&PsychCVExit)); // Register the project function which is called when the module // is invoked with no arguments: PsychErrorExit(PsychRegister(NULL, &PSYCHCVDisplaySynopsis)); // Report the version #if PSYCH_SYSTEM == PSYCH_OSX PsychErrorExit(PsychRegister("Version", &MODULEVersion)); #endif // Register the module name PsychErrorExit(PsychRegister("PsychCV", NULL)); // Register synopsis and named subfunctions. PsychErrorExit(PsychRegister("Verbosity", &PSYCHCVVerbosity)); #ifdef PSYCHCV_USE_OPENCV PsychErrorExit(PsychRegister("OpenEyesInitialize", &PSYCHCVOpenEyesInitialize)); PsychErrorExit(PsychRegister("OpenEyesShutdown", &PSYCHCVOpenEyesShutdown)); PsychErrorExit(PsychRegister("OpenEyesParameters", &PSYCHCVOpenEyesParameters)); PsychErrorExit(PsychRegister("OpenEyesTrackEyePosition", &PSYCHCVOpenEyesTrackEyePosition)); #endif PsychErrorExit(PsychRegister("CopyMatrixToMemBuffer", &PSYCHCVCopyMatrixToMemBuffer)); PsychErrorExit(PsychRegister("ARInitialize", &PSYCHCVARInitialize)); PsychErrorExit(PsychRegister("ARShutdown", &PSYCHCVARShutdown)); PsychErrorExit(PsychRegister("ARLoadMarker", &PSYCHCVARLoadMarker)); PsychErrorExit(PsychRegister("ARDetectMarkers", &PSYCHCVARDetectMarkers)); PsychErrorExit(PsychRegister("ARRenderImage", &PSYCHCVARRenderImage)); PsychErrorExit(PsychRegister("ARTrackerSettings", &PSYCHCVARTrackerSettings)); PsychErrorExit(PsychRegister("ARRenderSettings", &PSYCHCVARRenderSettings)); // Setup synopsis help strings: InitializeSynopsis(); //Scripting glue won't require this if the function takes no arguments. // Setup module author: PsychSetModuleAuthorByInitials("mk"); // Call wait-routine for 0.1 secs: This to initialize the time glue on MS-Windows, // so the first call to a timing function won't delay: PsychWaitIntervalSeconds(0.1); // Perform all remaining initialization: PsychCVInitialize(); // Startup finished. return(PsychError_none); }
PsychError SCREENShowCursorHelper(void) { int screenNumber, cursorid, mouseIdx; #if PSYCH_SYSTEM == PSYCH_LINUX Cursor mycursor; #endif //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumInputArgs(3)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs PsychCopyInScreenNumberArg(1, TRUE, &screenNumber); mouseIdx = -1; PsychCopyInIntegerArg(3, FALSE, &mouseIdx); PsychShowCursor(screenNumber, mouseIdx); // Copy in optional cursor shape id argument: The default of -1 means to // not change cursor appearance. Any other positive value changes to an // OS dependent shape (== the mapping of numbers to shapes is OS dependent). cursorid = -1; PsychCopyInIntegerArg(2, FALSE, &cursorid); // Cursor change request? if (cursorid!=-1) { // Yes. #if PSYCH_SYSTEM == PSYCH_OSX // OS/X: PsychCocoaSetThemeCursor(cursorid); #endif #if PSYCH_SYSTEM == PSYCH_LINUX // GNU/Linux with X11 windowing system: // Map screenNumber to X11 display handle and screenid: CGDirectDisplayID dpy; PsychGetCGDisplayIDFromScreenNumber(&dpy, screenNumber); // Create cursor spec from passed cursorid: mycursor = XCreateFontCursor(dpy, (unsigned int) cursorid); if (mouseIdx < 0) { // Set cursor for our window: PsychOSDefineX11Cursor(screenNumber, -1, mycursor); } else { // XInput cursor: Master pointers only. int nDevices; XIDeviceInfo* indevs = PsychGetInputDevicesForScreen(screenNumber, &nDevices); // Sanity check: if (NULL == indevs) PsychErrorExitMsg(PsychError_user, "Sorry, your system does not support individual mouse pointers."); if (mouseIdx >= nDevices) PsychErrorExitMsg(PsychError_user, "Invalid 'mouseIndex' provided. No such cursor pointer."); if (indevs[mouseIdx].use != XIMasterPointer) PsychErrorExitMsg(PsychError_user, "Invalid 'mouseIndex' provided. No such master cursor pointer."); PsychOSDefineX11Cursor(screenNumber, indevs[mouseIdx].deviceid, mycursor); } XFlush(dpy); // Done (hopefully). #endif #if PSYCH_SYSTEM == PSYCH_WINDOWS // Microsoft Windows: LPCTSTR lpCursorName; #ifndef IDC_HAND #define IDC_HAND MAKEINTRESOURCE(32649) #endif // Map provided cursor id to a Windows system id for such a cursor: switch(cursorid) { case 0: // Standard arrow cursor: lpCursorName = IDC_ARROW; break; case 1: // haircross cursor: lpCursorName = IDC_CROSS; break; case 2: // hand cursor: lpCursorName = IDC_HAND; break; case 3: // Arrows in all 4 directions cursor: lpCursorName = IDC_SIZEALL; break; case 4: // north-south cursor: lpCursorName = IDC_SIZENS; break; case 5: // east-west cursor: lpCursorName = IDC_SIZEWE; break; case 6: // hourglass cursor: lpCursorName = IDC_WAIT; break; case 7: // No cursor: lpCursorName = IDC_NO; break; default: // Default for unknown id is the standard arrow cursor: lpCursorName = IDC_ARROW; } // Load and set a cursor, based on the selected lpCursorName cursor id string: SetCursor(LoadCursor(NULL, lpCursorName)); #endif // End of cursor shape setup. } return(PsychError_none); }
PsychError SCREENMakeTexture(void) { int ix; PsychWindowRecordType *textureRecord; PsychWindowRecordType *windowRecord; PsychRectType rect; Boolean isImageMatrixBytes, isImageMatrixDoubles; int numMatrixPlanes, xSize, ySize, iters; unsigned char *byteMatrix; double *doubleMatrix; GLuint *texturePointer; GLubyte *texturePointer_b; GLfloat *texturePointer_f; double *rp, *gp, *bp, *ap; GLubyte *rpb, *gpb, *bpb, *apb; int usepoweroftwo, usefloatformat, assume_texorientation, textureShader; double optimized_orientation; Boolean bigendian; // Detect endianity (byte-order) of machine: ix=255; rpb=(GLubyte*) &ix; bigendian = ( *rpb == 255 ) ? FALSE : TRUE; ix = 0; rpb = NULL; if(PsychPrefStateGet_DebugMakeTexture()) //MARK #1 StoreNowTime(); //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //Get the window structure for the onscreen window. It holds the onscreein GL context which we will need in the //final step when we copy the texture from system RAM onto the screen. PsychErrorExit(PsychCapNumInputArgs(7)); PsychErrorExit(PsychRequireNumInputArgs(2)); PsychErrorExit(PsychCapNumOutputArgs(1)); //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); if((windowRecord->windowType!=kPsychDoubleBufferOnscreen) && (windowRecord->windowType!=kPsychSingleBufferOnscreen)) PsychErrorExitMsg(PsychError_user, "MakeTexture called on something else than a onscreen window"); // Get optional texture orientation flag: assume_texorientation = 0; PsychCopyInIntegerArg(6, FALSE, &assume_texorientation); // Get optional texture shader handle: textureShader = 0; PsychCopyInIntegerArg(7, FALSE, &textureShader); //get the argument and sanity check it. isImageMatrixBytes=PsychAllocInUnsignedByteMatArg(2, kPsychArgAnything, &ySize, &xSize, &numMatrixPlanes, &byteMatrix); isImageMatrixDoubles=PsychAllocInDoubleMatArg(2, kPsychArgAnything, &ySize, &xSize, &numMatrixPlanes, &doubleMatrix); if(numMatrixPlanes > 4) PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Specified image matrix exceeds maximum depth of 4 layers"); if(ySize<1 || xSize <1) PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Specified image matrix must be at least 1 x 1 pixels in size"); if(! (isImageMatrixBytes || isImageMatrixDoubles)) PsychErrorExitMsg(PsychError_user, "Illegal argument type"); //not likely. // Is this a special image matrix which is already pre-transposed to fit our optimal format? if (assume_texorientation == 2) { // Yes. Swap xSize and ySize to take this into account: ix = xSize; xSize = ySize; ySize = ix; ix = 0; } // Build defining rect for this texture: PsychMakeRect(rect, 0, 0, xSize, ySize); // Copy in texture preferred draw orientation hint. We default to zero degrees, if // not provided. // This parameter is not yet used. It is silently ignorerd for now... optimized_orientation = 0; PsychCopyInDoubleArg(3, FALSE, &optimized_orientation); // Copy in special creation mode flag: It defaults to zero. If set to 1 then we // always create a power-of-two GL_TEXTURE_2D texture. This is useful if one wants // to create and use drifting gratings with no effort - texture wrapping is only available // for GL_TEXTURE_2D, not for non-pot types. It is also useful if the texture is to be // exported to external OpenGL code to simplify tex coords assignments. usepoweroftwo=0; PsychCopyInIntegerArg(4, FALSE, &usepoweroftwo); // Check if size constraints are fullfilled for power-of-two mode: if (usepoweroftwo & 1) { for(ix = 1; ix < xSize; ix*=2); if (ix!=xSize) { PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Power-of-two texture requested but width of imageMatrix is not a power of two!"); } for(ix = 1; ix < ySize; ix*=2); if (ix!=ySize) { PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Power-of-two texture requested but height of imageMatrix is not a power of two!"); } } // Check if creation of a floating point texture is requested? We default to non-floating point, // standard 8 bpc textures if this parameter is not provided. usefloatformat = 0; PsychCopyInIntegerArg(5, FALSE, &usefloatformat); if (usefloatformat<0 || usefloatformat>2) PsychErrorExitMsg(PsychError_user, "Invalid value for 'floatprecision' parameter provided! Valid values are 0 for 8bpc int, 1 for 16bpc float or 2 for 32bpc float."); if (usefloatformat && !isImageMatrixDoubles) { // Floating point texture requested. We only support this if our input is a double matrix, not // for uint8 matrices - converting them to float precision would be just a waste of ressources // without any benefit for precision. PsychErrorExitMsg(PsychError_user, "Creation of a floating point precision texture requested, but uint8 matrix provided! Only double matrices are acceptable for this mode."); } //Create a texture record. Really just a window record adapted for textures. PsychCreateWindowRecord(&textureRecord); //this also fills the window index field. textureRecord->windowType=kPsychTexture; // MK: We need to assign the screen number of the onscreen-window, so PsychCreateTexture() // can query the size of the screen/onscreen-window... textureRecord->screenNumber=windowRecord->screenNumber; textureRecord->depth=32; PsychCopyRect(textureRecord->rect, rect); //Allocate the texture memory and copy the MATLAB matrix into the texture memory. // MK: We only allocate the amount really needed for given format, aka numMatrixPlanes - Bytes per pixel. if (usefloatformat) { // Allocate a double for each color component and pixel: textureRecord->textureMemorySizeBytes= sizeof(double) * numMatrixPlanes * xSize * ySize; } else { // Allocate one byte per color component and pixel: textureRecord->textureMemorySizeBytes= numMatrixPlanes * xSize * ySize; } // MK: Allocate memory page-aligned... -> Helps Apple texture range extensions et al. if(PsychPrefStateGet_DebugMakeTexture()) //MARK #2 StoreNowTime(); textureRecord->textureMemory=malloc(textureRecord->textureMemorySizeBytes); if(PsychPrefStateGet_DebugMakeTexture()) //MARK #3 StoreNowTime(); texturePointer=textureRecord->textureMemory; // Does script explicitely request usage of a GL_TEXTURE_2D power-of-two texture? if (usepoweroftwo & 1) { // Enforce creation as a power-of-two texture: textureRecord->texturetarget=GL_TEXTURE_2D; } // Now the conversion routines that convert Matlab/Octave matrices into memory // buffers suitable for OpenGL: if (usefloatformat) { // Conversion routines for HDR 16 bpc or 32 bpc textures -- Slow path. // Our input is always double matrices... iters = xSize * ySize; // Our input buffer is always of GL_FLOAT precision: textureRecord->textureexternaltype = GL_FLOAT; texturePointer_f=(GLfloat*) texturePointer; if(numMatrixPlanes==1) { for(ix=0;ix<iters;ix++){ *(texturePointer_f++)= (GLfloat) *(doubleMatrix++); } textureRecord->depth=(usefloatformat==1) ? 16 : 32; textureRecord->textureinternalformat = (usefloatformat==1) ? GL_LUMINANCE_FLOAT16_APPLE : GL_LUMINANCE_FLOAT32_APPLE; textureRecord->textureexternalformat = GL_LUMINANCE; } if(numMatrixPlanes==2) { rp=(double*) ((psych_uint64) doubleMatrix); ap=(double*) ((psych_uint64) doubleMatrix + (psych_uint64) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_f++)= (GLfloat) *(rp++); *(texturePointer_f++)= (GLfloat) *(ap++); } textureRecord->depth=(usefloatformat==1) ? 32 : 64; textureRecord->textureinternalformat = (usefloatformat==1) ? GL_LUMINANCE_ALPHA_FLOAT16_APPLE : GL_LUMINANCE_ALPHA_FLOAT32_APPLE; textureRecord->textureexternalformat = GL_LUMINANCE_ALPHA; } if(numMatrixPlanes==3) { rp=(double*) ((psych_uint64) doubleMatrix); gp=(double*) ((psych_uint64) doubleMatrix + (psych_uint64) iters*sizeof(double)); bp=(double*) ((psych_uint64) gp + (psych_uint64) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_f++)= (GLfloat) *(rp++); *(texturePointer_f++)= (GLfloat) *(gp++); *(texturePointer_f++)= (GLfloat) *(bp++); } textureRecord->depth=(usefloatformat==1) ? 48 : 96; textureRecord->textureinternalformat = (usefloatformat==1) ? GL_RGB_FLOAT16_APPLE : GL_RGB_FLOAT32_APPLE; textureRecord->textureexternalformat = GL_RGB; } if(numMatrixPlanes==4) { rp=(double*) ((psych_uint64) doubleMatrix); gp=(double*) ((psych_uint64) doubleMatrix + (psych_uint64) iters*sizeof(double)); bp=(double*) ((psych_uint64) gp + (psych_uint64) iters*sizeof(double)); ap=(double*) ((psych_uint64) bp + (psych_uint64) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_f++)= (GLfloat) *(rp++); *(texturePointer_f++)= (GLfloat) *(gp++); *(texturePointer_f++)= (GLfloat) *(bp++); *(texturePointer_f++)= (GLfloat) *(ap++); } textureRecord->depth=(usefloatformat==1) ? 64 : 128; textureRecord->textureinternalformat = (usefloatformat==1) ? GL_RGBA_FLOAT16_APPLE : GL_RGBA_FLOAT32_APPLE; textureRecord->textureexternalformat = GL_RGBA; } // This is a special workaround for bugs in FLOAT16 texture creation on Mac OS/X 10.4.x and 10.5.x. // The OpenGL fails to properly flush very small values (< 1e-9) to zero when creating a FLOAT16 // type texture. Instead it seems to initialize with trash data, corrupting the texture. // Therefore, if FLOAT16 texture creation is requested, we loop over the whole input buffer and // set all values with magnitude smaller than 1e-9 to zero. Better safe than sorry... if(usefloatformat==1) { texturePointer_f=(GLfloat*) texturePointer; iters = iters * numMatrixPlanes; for(ix=0; ix<iters; ix++, texturePointer_f++) if(fabs((double) *texturePointer_f) < 1e-9) { *texturePointer_f = 0.0; } } // End of HDR conversion code... } else { // Standard LDR texture 8 bpc conversion routines -- Fast path. // Improved implementation: Takes 13 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==1){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(doubleMatrix++); } textureRecord->depth=8; } // Improved version: Takes 3 ms on a 800x800 texture... // NB: Implementing memcpy manually by a for-loop takes 10 ms! This is a huge difference. // -> That's because memcpy on MacOS-X is implemented with hand-coded, highly tuned Assembler code for PowerPC. // -> It's always wise to use system-routines if available, instead of coding it by yourself! if(isImageMatrixBytes && numMatrixPlanes==1){ memcpy((void*) texturePointer, (void*) byteMatrix, xSize*ySize); textureRecord->depth=8; } // New version: Takes 33 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==2){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; rp=(double*) ((psych_uint64) doubleMatrix); ap=(double*) ((psych_uint64) doubleMatrix + (psych_uint64) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(ap++); } textureRecord->depth=16; } // New version: Takes 20 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==2){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; rpb=(GLubyte*) ((psych_uint64) byteMatrix); apb=(GLubyte*) ((psych_uint64) byteMatrix + (psych_uint64) iters); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(rpb++); *(texturePointer_b++)= *(apb++); } textureRecord->depth=16; } // Improved version: Takes 43 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==3){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; rp=(double*) ((psych_uint64) doubleMatrix); gp=(double*) ((psych_uint64) doubleMatrix + (psych_uint64) iters*sizeof(double)); bp=(double*) ((psych_uint64) gp + (psych_uint64) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(gp++); *(texturePointer_b++)= (GLubyte) *(bp++); } textureRecord->depth=24; } // Improved version: Takes 25 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==3){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; rpb=(GLubyte*) ((psych_uint64) byteMatrix); gpb=(GLubyte*) ((psych_uint64) byteMatrix + (psych_uint64) iters); bpb=(GLubyte*) ((psych_uint64) gpb + (psych_uint64) iters); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(rpb++); *(texturePointer_b++)= *(gpb++); *(texturePointer_b++)= *(bpb++); } textureRecord->depth=24; } // Improved version: Takes 55 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==4){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; rp=(double*) ((psych_uint64) doubleMatrix); gp=(double*) ((psych_uint64) doubleMatrix + (psych_uint64) iters*sizeof(double)); bp=(double*) ((psych_uint64) gp + (psych_uint64) iters*sizeof(double)); ap=(double*) ((psych_uint64) bp + (psych_uint64) iters*sizeof(double)); if (bigendian) { // Code for big-endian machines like PowerPC: for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(ap++); *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(gp++); *(texturePointer_b++)= (GLubyte) *(bp++); } } else { // Code for little-endian machines like Intel Pentium: for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(bp++); *(texturePointer_b++)= (GLubyte) *(gp++); *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(ap++); } } textureRecord->depth=32; } // Improved version: Takes 33 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==4){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; rpb=(GLubyte*) ((psych_uint64) byteMatrix); gpb=(GLubyte*) ((psych_uint64) byteMatrix + (psych_uint64) iters); bpb=(GLubyte*) ((psych_uint64) gpb + (psych_uint64) iters); apb=(GLubyte*) ((psych_uint64) bpb + (psych_uint64) iters); if (bigendian) { // Code for big-endian machines like PowerPC: for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(apb++); *(texturePointer_b++)= *(rpb++); *(texturePointer_b++)= *(gpb++); *(texturePointer_b++)= *(bpb++); } } else { // Code for little-endian machines like Intel Pentium: for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(bpb++); *(texturePointer_b++)= *(gpb++); *(texturePointer_b++)= *(rpb++); *(texturePointer_b++)= *(apb++); } } textureRecord->depth=32; } } // End of 8 bpc texture conversion code (fast-path for LDR textures) // The memory buffer now contains our texture data in a format ready to submit to OpenGL. // Assign parent window and copy its inheritable properties: PsychAssignParentWindow(textureRecord, windowRecord); // Texture orientation is zero aka transposed aka non-renderswapped. textureRecord->textureOrientation = ((assume_texorientation != 2) && (assume_texorientation != 3)) ? 0 : 2; // This is our best guess about the number of image channels: textureRecord->nrchannels = numMatrixPlanes; // Let's create and bind a new texture object and fill it with our new texture data. PsychCreateTexture(textureRecord); // Assign GLSL filter-/lookup-shaders if needed: PsychAssignHighPrecisionTextureShaders(textureRecord, windowRecord, usefloatformat, (usepoweroftwo & 2) ? 1 : 0); // User specified override shader for this texture provided? This is useful for // basic image processing and procedural texture shading: if (textureShader!=0) { // Assign provided shader as filtershader to this texture: We negate it so // that the texture blitter routines know this is a custom shader, not our // built in filter shader: textureRecord->textureFilterShader = -1 * textureShader; } // Texture ready. Mark it valid and return handle to userspace: PsychSetWindowRecordValid(textureRecord); PsychCopyOutDoubleArg(1, FALSE, textureRecord->windowIndex); // Swapping the texture to upright orientation requested? if (assume_texorientation == 1) { // Transform sourceRecord source texture into a normalized, upright texture if it isn't already in // that format. We require this standard orientation for simplified shader design. PsychSetShader(windowRecord, 0); PsychNormalizeTextureOrientation(textureRecord); } // Shall the texture be finally declared "normally oriented"? // This is either due to explicit renderswapping if assume_textureorientation == 1, // or because it was already pretransposed in Matlab/Octave if assume_textureorientation == 2, // or because user space tells us the texture is isotropic if assume_textureorientation == 3. if (assume_texorientation > 0) { // Yes. Label it as such: textureRecord->textureOrientation = 2; } if(PsychPrefStateGet_DebugMakeTexture()) //MARK #4 StoreNowTime(); return(PsychError_none); }
PsychError SCREENCreateMovie(void) { static char useString[] = "moviePtr = Screen('CreateMovie', windowPtr, movieFile [, width][, height][, frameRate=30][, movieOptions][, numChannels=4][, bitdepth=8]);"; static char synopsisString[] = "Create a new movie file with filename 'movieFile' and according to given 'movieOptions'.\n" "The function returns a handle 'moviePtr' to the file.\n" "Currently only single-track video encoding is supported.\n" "See 'Screen AddAudioBufferToMovie?' on how to add audio tracks to movies.\n" "\n" "Movie creation is a 3 step procedure:\n" "1. Create a movie and define encoding options via 'CreateMovie'.\n" "2. Add video and audio data to the movie via calls to 'AddFrameToMovie' et al.\n" "3. Finalize and close the movie via a call to 'FinalizeMovie'.\n\n" "All following parameters are optional and have reasonable defaults:\n\n" "'width' Width of movie video frames in pixels. Defaults to width of window 'windowPtr'.\n" "'height' Height of movie video frames in pixels. Defaults to height of window 'windowPtr'.\n" "'frameRate' Playback framerate of movie. Defaults to 30 fps. Technically this is not the " "playback framerate but the granularity in 1/frameRate seconds with which the duration of " "a single movie frame can be specified. When you call 'AddFrameToMovie', there's an optional " "parameter 'frameDuration' which defaults to one. The parameter defines the display duration " "of that frame as the fraction 'frameDuration' / 'frameRate' seconds, so 'frameRate' defines " "the denominator of that term. However, for a default 'frameDuration' of one, this is equivalent " "to the 'frameRate' of the movie, at least if you leave everything at defaults.\n\n" "'movieoptions' a textstring which allows to define additional parameters via keyword=parm pairs. " "For GStreamer movie writing, you can provide the same options as for GStreamer video recording. " "See 'help VideoRecording' for supported options and tips.\n" "Keywords unknown to a certain implementation or codec will be silently ignored:\n" "EncodingQuality=x Set encoding quality to value x, in the range 0.0 for lowest movie quality to " "1.0 for highest quality. Default is 0.5 = normal quality. 1.0 often provides near-lossless encoding.\n" "'numChannels' Optional number of image channels to encode: Can be 1, 3 or 4 on OpenGL graphics hardware, " "and 3 or 4 on OpenGL-ES hardware. 1 = Red/Grayscale channel only, 3 = RGB, 4 = RGBA. Please note that not " "all video codecs can encode pure 1 channel data or RGBA data, ie. an alpha channel. If an unsuitable codec " "is selected, movie writing may fail, or unsupported channels (e.g., the alpha channel) may get silently " "discarded. It could also happen that a codec which doesn't support 1 channel storage will replicate " "the Red/Grayscale data into all three RGB channels, leading to no data loss but increased movie file size. " "Default is to request RGBA 4 channel data from the system, encoding to RGBA or RGB, depending on codec.\n" "'bitdepth' Optional color/intensity resolution of each channel: Default is 8 bpc, for 8 bit per component " "storage. OpenGL graphics hardware, but not OpenGL-ES, also supports 16 bpc image readback. However, not all " "codecs can encode with > 8 bpc color/luminance precision, so encoding with 16 bpc may fail or silently reduce " "precision to less bits, possibly 8 bpc or less. If you specify the special keyword UsePTB16BPC in 'movieoptions', " "then PTB will use its own proprietary 16 bpc format for 1 or 3 channel mode. This format can only be read by " "PTB's own movie playback functions, not by other software.\n" "In general, embedded OpenGL-ES graphics hardware is more restricted in the type of image data it can return. " "Most video codecs are lossy codecs. They will intentionally throw away color or spatial precision of encoded " "video to reduce video file size or network bandwidth, often in a way that is not easily perceptible to the " "naked eye. If you require high fidelity, make sure to double-check your results for a given codec + parameter " "setup, e.g., via encoding + decoding the movie and checking the original data against decoded data.\n" "\n"; static char seeAlsoString[] = "FinalizeMovie AddFrameToMovie CloseMovie PlayMovie GetMovieImage GetMovieTimeIndex SetMovieTimeIndex"; PsychWindowRecordType *windowRecord; char *moviefile; char *movieOptions; int moviehandle = -1; double framerate = 30.0; int width; int height; int numChannels, bitdepth; char defaultOptions[2] = ""; // All sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none);}; PsychErrorExit(PsychCapNumInputArgs(8)); // Max. 8 input args. PsychErrorExit(PsychRequireNumInputArgs(2)); // Min. 2 input args required. PsychErrorExit(PsychCapNumOutputArgs(1)); // Max. 1 output args. // Get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); // Only onscreen windows allowed: if(!PsychIsOnscreenWindow(windowRecord)) { PsychErrorExitMsg(PsychError_user, "CreateMovie called on something else than an onscreen window."); } // Get the movie name string: moviefile = NULL; PsychAllocInCharArg(2, kPsychArgRequired, &moviefile); // Get the optional size: // Default Width and Height of movie frames is derived from size of window: width = (int) PsychGetWidthFromRect(windowRecord->clientrect); height = (int) PsychGetHeightFromRect(windowRecord->clientrect); PsychCopyInIntegerArg(3, kPsychArgOptional, &width); PsychCopyInIntegerArg(4, kPsychArgOptional, &height); // Get the optional framerate: PsychCopyInDoubleArg(5, kPsychArgOptional, &framerate); // Get the optional options string: movieOptions = defaultOptions; PsychAllocInCharArg(6, kPsychArgOptional, &movieOptions); // Get optional number of channels of movie: numChannels = 4; PsychCopyInIntegerArg(7, kPsychArgOptional, &numChannels); if (numChannels != 1 && numChannels != 3 && numChannels != 4) PsychErrorExitMsg(PsychError_user, "Invalid number of channels 'numChannels' provided. Only 1, 3 or 4 channels allowed!"); // Get optional bitdepth of movie: bitdepth = 8; PsychCopyInIntegerArg(8, kPsychArgOptional, &bitdepth); if (bitdepth != 8 && bitdepth != 16) PsychErrorExitMsg(PsychError_user, "Invalid 'bitdepth' provided. Only 8 bpc or 16 bpc allowed!"); // Create movie of given size and framerate with given options: moviehandle = PsychCreateNewMovieFile(moviefile, width, height, framerate, numChannels, bitdepth, movieOptions, NULL); if (0 > moviehandle) { PsychErrorExitMsg(PsychError_user, "CreateMovie failed for reason mentioned above."); } // Return handle to it: PsychCopyOutDoubleArg(1, FALSE, (double) moviehandle); return(PsychError_none); }
PsychError SCREENOpenMovie(void) { PsychWindowRecordType *windowRecord; char *moviefile; char *movieOptions; char dummmyOptions[1]; int moviehandle = -1; int framecount; double durationsecs; double framerate; double aspectRatio; int width; int height; int asyncFlag = 0; int specialFlags1 = 0; static psych_bool firstTime = TRUE; double preloadSecs = 1; int rc; int pixelFormat = 4; int maxNumberThreads = -1; if (firstTime) { // Setup asyncopeninfo on first invocation: firstTime = FALSE; asyncmovieinfo.asyncstate = 0; // State = No async open in progress. } // All sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none);}; PsychErrorExit(PsychCapNumInputArgs(8)); // Max. 8 input args. PsychErrorExit(PsychRequireNumInputArgs(1)); // Min. 1 input args required. PsychErrorExit(PsychCapNumOutputArgs(7)); // Max. 7 output args. // Get the window record from the window record argument and get info from the window record windowRecord = NULL; PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, FALSE, &windowRecord); // Only onscreen windows allowed: if(windowRecord && !PsychIsOnscreenWindow(windowRecord)) { PsychErrorExitMsg(PsychError_user, "OpenMovie called on something else than an onscreen window."); } // Get the movie name string: moviefile = NULL; PsychAllocInCharArg(2, kPsychArgRequired, &moviefile); // Get the (optional) asyncFlag: PsychCopyInIntegerArg(3, FALSE, &asyncFlag); PsychCopyInDoubleArg(4, FALSE, &preloadSecs); if (preloadSecs < 0 && preloadSecs!= -1 && preloadSecs!= -2) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid (negative, but not equal -1) 'preloadSecs' argument!"); // Get the (optional) specialFlags1: PsychCopyInIntegerArg(5, FALSE, &specialFlags1); if (specialFlags1 < 0) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid 'specialFlags1' setting! Only positive values allowed."); // Get the (optional) pixelFormat: PsychCopyInIntegerArg(6, FALSE, &pixelFormat); if (pixelFormat < 1 || pixelFormat > 10) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid 'pixelFormat' setting! Only values 1 to 10 are allowed."); // Get the (optional) maxNumberThreads: PsychCopyInIntegerArg(7, FALSE, &maxNumberThreads); if (maxNumberThreads < -1) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid 'maxNumberThreads' setting! Only values of -1 or greater are allowed."); // Get the (optional) movie options string: As PsychAllocInCharArg() no-ops if // the optional string isn't provided, we need to point movieOptions to an empty // 0-terminated string by default, so we don't have a dangling pointer: dummmyOptions[0] = 0; movieOptions = &dummmyOptions[0]; PsychAllocInCharArg(8, FALSE, &movieOptions); // Queueing of a new movie for seamless playback requested? if (asyncFlag & 2) { // Yes. Do a special call, just passing the moviename of the next // movie to play. Pass the relevant moviehandle as retrieved from // preloadSecs: moviehandle = (int) preloadSecs; preloadSecs = 0; PsychCreateMovie(windowRecord, moviefile, preloadSecs, &moviehandle, asyncFlag, specialFlags1, pixelFormat, maxNumberThreads, movieOptions); if (moviehandle == -1) PsychErrorExitMsg(PsychError_user, "Could not queue new moviefile for gapless playback."); return(PsychError_none); } // Asynchronous Open operation in progress or requested? if ((asyncmovieinfo.asyncstate == 0) && !(asyncFlag & 1)) { // No. We should just synchronously open the movie: // Try to open the named 'moviefile' and create & initialize a corresponding movie object. // A handle to the movie object is returned upon successfull operation. PsychCreateMovie(windowRecord, moviefile, preloadSecs, &moviehandle, asyncFlag, specialFlags1, pixelFormat, maxNumberThreads, movieOptions); } else { // Asynchronous open operation requested or running: switch(asyncmovieinfo.asyncstate) { case 0: // No async open running, but async open requested // Fill all information needed for opening the movie into the info struct: asyncmovieinfo.asyncstate = 1; // Mark state as "Operation in progress" asyncmovieinfo.moviename = strdup(moviefile); asyncmovieinfo.preloadSecs = preloadSecs; asyncmovieinfo.asyncFlag = asyncFlag; asyncmovieinfo.specialFlags1 = specialFlags1; asyncmovieinfo.pixelFormat = pixelFormat; asyncmovieinfo.maxNumberThreads = maxNumberThreads; asyncmovieinfo.movieOptions = strdup(movieOptions); if (windowRecord) { memcpy(&asyncmovieinfo.windowRecord, windowRecord, sizeof(PsychWindowRecordType)); } else { memset(&asyncmovieinfo.windowRecord, 0, sizeof(PsychWindowRecordType)); } asyncmovieinfo.moviehandle = -1; // Increase our scheduling priority to basic RT priority: This way we should get // more cpu time for our PTB main thread than the async. background prefetch-thread: // On Windows we must not go higher than basePriority 1 (HIGH PRIORITY) or bad interference can happen. // On OS/X we use basePriority 2 for robust realtime, using up to (4+1) == 5 msecs of time in every 10 msecs slice, allowing for up to 1 msec jitter/latency for ops. // On Linux we just use standard basePriority 2 RT-FIFO scheduling and trust the os to do the right thing. if ((rc=PsychSetThreadPriority(NULL, ((PSYCH_SYSTEM == PSYCH_WINDOWS) ? 1 : 2), ((PSYCH_SYSTEM == PSYCH_OSX) ? 4 : 0)))!=0) { printf("PTB-WARNING: In OpenMovie(): Failed to raise priority of main thread [System error %i]. Expect movie timing problems.\n", rc); } // Start our own movie loader Posix-Thread: PsychCreateThread(&asyncmovieinfo.pid, NULL, PsychAsyncCreateMovie, &asyncmovieinfo); // Async movie open initiated. We return control to host environment: return(PsychError_none); break; case 1: // Async open operation in progress, but not yet finished. // Should we wait for completion or just return? if (asyncFlag & 1) { // Async poll requested. We just return -1 to signal that open isn't finished yet: PsychCopyOutDoubleArg(1, TRUE, -1); return(PsychError_none); } // We fall through to case 2 - Wait for "Load operation successfully finished." case 2: // Async open operation successfully finished. Parse asyncinfo struct and return it to host environment: // We need to join our terminated worker thread to release its ressources. If the worker-thread // isn't done yet (fallthrough from case 1 for sync. wait), this join will block us until worker // completes: PsychDeleteThread(&asyncmovieinfo.pid); asyncmovieinfo.asyncstate = 0; // Reset state to idle: moviehandle = asyncmovieinfo.moviehandle; // Release options string: free(asyncmovieinfo.movieOptions); // Movie successfully opened? if (moviehandle < 0) { // Movie loading failed for some reason. printf("PTB-ERROR: When trying to asynchronously load movie %s, the operation failed: ", asyncmovieinfo.moviename); free(asyncmovieinfo.moviename); PsychErrorExitMsg(PsychError_user, "Asynchronous loading of the movie failed."); } free(asyncmovieinfo.moviename); // We can fall out of the switch statement and continue with the standard synchronous load code as if // the movie had been loaded synchronously. break; default: PsychErrorExitMsg(PsychError_internal, "Unhandled async movie state condition encountered! BUG!!"); } } // Upon sucessfull completion, we'll have a valid handle in 'moviehandle'. PsychCopyOutDoubleArg(1, TRUE, (double) moviehandle); // Retrieve infos about new movie: // Is the "count" output argument (total number of frames) requested by user? if (PsychGetNumOutputArgs() > 5) { // Yes. Query the framecount (expensive!) and return it: PsychGetMovieInfos(moviehandle, &width, &height, &framecount, &durationsecs, &framerate, NULL, &aspectRatio); PsychCopyOutDoubleArg(6, TRUE, (double) framecount); } else { // No. Don't compute and return it. PsychGetMovieInfos(moviehandle, &width, &height, NULL, &durationsecs, &framerate, NULL, &aspectRatio); } PsychCopyOutDoubleArg(2, FALSE, (double) durationsecs); PsychCopyOutDoubleArg(3, FALSE, (double) framerate); PsychCopyOutDoubleArg(4, FALSE, (double) width); PsychCopyOutDoubleArg(5, FALSE, (double) height); PsychCopyOutDoubleArg(7, FALSE, (double) aspectRatio); // Ready! return(PsychError_none); }
PsychError MACHPRIORITYMachPriority(void) { //double *returnValue; //int newPriority; const char *outerStructFieldNames[]={"thread", "flavor", "policy"}; const char *policyStructFieldNames[]={"period", "computation", "constraint", "preemptible"}; int numOuterStructDimensions=1, numOuterStructFieldNames=3, numPolicyStructDimensions=1, numPolicyStructFieldNames=4; PsychGenericScriptType *outerStructArray, *policyStructArray; int kernError, numInputArgs; thread_act_t threadID; static thread_policy_flavor_t currentThreadFlavor=THREAD_STANDARD_POLICY; struct thread_time_constraint_policy oldPolicyInfo, newPolicyInfo; mach_msg_type_number_t msgTypeNumber; boolean_t getDefault; char *flavorStr; boolean setNewMode; double *periodArg, *computationArg, *constraintArg, *preemptibleArg; char errorMessageStr[256]; //check to see if the user supplied superfluous arguments PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(4)); //actually we permit only zero or three arguments. numInputArgs=PsychGetNumInputArgs(); if(numInputArgs==4) setNewMode=TRUE; else if(numInputArgs==0) setNewMode=FALSE; else PsychErrorExitMsg(PsychError_user,"Incorrect number of arguments. Either zero or four arguments accepted"); //read the current settings threadID= mach_thread_self(); currentThreadFlavor=THREAD_TIME_CONSTRAINT_POLICY; msgTypeNumber=THREAD_TIME_CONSTRAINT_POLICY_COUNT; getDefault=FALSE; kernError= thread_policy_get(threadID, currentThreadFlavor, (int *)&oldPolicyInfo, &msgTypeNumber, &getDefault); if(kernError != KERN_SUCCESS) PsychErrorExitMsg(PsychError_internal,"\"thread_policy_get()\" returned and error when reading current thread policy"); //fill in the outgoig struct with current thread settings values. //outer struct PsychAllocOutStructArray(1, FALSE, numOuterStructDimensions, numOuterStructFieldNames, outerStructFieldNames, &outerStructArray); PsychSetStructArrayDoubleElement("thread", 0, (double)threadID, outerStructArray); flavorStr=GetFlavorStringFromFlavorConstant(currentThreadFlavor); PsychSetStructArrayStringElement("flavor", 0, flavorStr, outerStructArray); //enclosed policy struct PsychAllocOutStructArray(-1, FALSE, numPolicyStructDimensions, numPolicyStructFieldNames, policyStructFieldNames, &policyStructArray); PsychSetStructArrayDoubleElement("period", 0, (double)oldPolicyInfo.period, policyStructArray); PsychSetStructArrayDoubleElement("computation", 0, (double)oldPolicyInfo.computation, policyStructArray); PsychSetStructArrayDoubleElement("constraint", 0, (double)oldPolicyInfo.constraint, policyStructArray); PsychSetStructArrayDoubleElement("preemptible", 0, (double)oldPolicyInfo.preemptible, policyStructArray); PsychSetStructArrayStructElement("policy",0, policyStructArray, outerStructArray); //Set the priority if(setNewMode){ //if there is only one argument then it must be the flavor argument re PsychAllocInDoubleArg(1, TRUE, &periodArg); PsychAllocInDoubleArg(2, TRUE, &computationArg); PsychAllocInDoubleArg(3, TRUE, &constraintArg); PsychAllocInDoubleArg(4, TRUE, &preemptibleArg); newPolicyInfo.period=(uint32_t)*periodArg; newPolicyInfo.computation=(uint32_t)*computationArg; newPolicyInfo.constraint=(uint32_t)*constraintArg; newPolicyInfo.preemptible=(boolean_t)*preemptibleArg; kernError= thread_policy_set(threadID, THREAD_TIME_CONSTRAINT_POLICY, (int *)&newPolicyInfo, THREAD_TIME_CONSTRAINT_POLICY_COUNT); if(kernError != KERN_SUCCESS){ sprintf(errorMessageStr,"%s%d", "\"thread_policy_set()\" returned and error when setting new thread policy: ", (int)kernError); PsychErrorExitMsg(PsychError_internal, errorMessageStr); } } return(PsychError_none); }
PsychError SCREENGetWindowInfo(void) { const char *FieldNames[]={ "Beamposition", "LastVBLTimeOfFlip", "LastVBLTime", "VBLCount", "TimeAtSwapRequest", "TimePostSwapRequest", "RawSwapTimeOfFlip", "VBLTimePostFlip", "OSSwapTimestamp", "GPULastFrameRenderTime", "StereoMode", "ImagingMode", "MultiSampling", "MissedDeadlines", "FlipCount", "StereoDrawBuffer", "GuesstimatedMemoryUsageMB", "VBLStartline", "VBLEndline", "VideoRefreshFromBeamposition", "GLVendor", "GLRenderer", "GLVersion", "GPUCoreId", "GLSupportsFBOUpToBpc", "GLSupportsBlendingUpToBpc", "GLSupportsTexturesUpToBpc", "GLSupportsFilteringUpToBpc", "GLSupportsPrecisionColors", "GLSupportsFP32Shading", "BitsPerColorComponent", "IsFullscreen", "SpecialFlags", "SwapGroup", "SwapBarrier" }; const int fieldCount = 35; PsychGenericScriptType *s; PsychWindowRecordType *windowRecord; double beamposition, lastvbl; int infoType = 0, retIntArg; double auxArg1, auxArg2, auxArg3; CGDirectDisplayID displayId; psych_uint64 postflip_vblcount; double vbl_startline; long scw, sch; psych_bool onscreen; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumInputArgs(5)); //The maximum number of inputs PsychErrorExit(PsychRequireNumInputArgs(1)); //The required number of inputs PsychErrorExit(PsychCapNumOutputArgs(1)); //The maximum number of outputs // Query infoType flag: Defaults to zero. PsychCopyInIntegerArg(2, FALSE, &infoType); if (infoType < 0 || infoType > 5) PsychErrorExitMsg(PsychError_user, "Invalid 'infoType' argument specified! Valid are 0, 1, 2, 3, 4 and 5."); // Windowserver info requested? if (infoType == 2 || infoType == 3) { // Return info about WindowServer: #if PSYCH_SYSTEM == PSYCH_OSX const char *CoreGraphicsFieldNames[]={ "CGSFps", "CGSValue1", "CGSValue2", "CGSValue3", "CGSDebugOptions" }; const int CoreGraphicsFieldCount = 5; float cgsFPS, val1, val2, val3; // This (undocumented) Apple call retrieves information about performance statistics of // the Core graphics server, also known as WindowServer or Quartz compositor: CGSGetPerformanceData(_CGSDefaultConnection(), &cgsFPS, &val1, &val2, &val3); if (CGSGetDebugOptions(&retIntArg)) { if (PsychPrefStateGet_Verbosity() > 1) printf("PTB-WARNING: GetWindowInfo: Call to CGSGetDebugOptions() failed!\n"); } PsychAllocOutStructArray(1, FALSE, 1, CoreGraphicsFieldCount, CoreGraphicsFieldNames, &s); PsychSetStructArrayDoubleElement("CGSFps", 0 , cgsFPS, s); PsychSetStructArrayDoubleElement("CGSValue1", 0, val1, s); PsychSetStructArrayDoubleElement("CGSValue2", 0, val2, s); PsychSetStructArrayDoubleElement("CGSValue3", 0, val3, s); PsychSetStructArrayDoubleElement("CGSDebugOptions", 0, (double) retIntArg, s); if ( (infoType == 3) && PsychCopyInDoubleArg(3, FALSE, &auxArg1) ) { // Type 3 setup request with auxArg1 provided. Apple auxArg1 as debugFlag setting // for the CoreGraphics server: DANGEROUS! if (CGSSetDebugOptions((unsigned int) auxArg1)) { if (PsychPrefStateGet_Verbosity() > 1) printf("PTB-WARNING: GetWindowInfo: Call to CGSSetDebugOptions() failed!\n"); } } #endif #if PSYCH_SYSTEM == PSYCH_WINDOWS psych_uint64 onsetVBLCount, frameId; double onsetVBLTime, compositionRate; psych_uint64 targetVBL; PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); // Query all DWM presentation timing info, return full info as struct in optional return argument '1': if (PsychOSGetPresentationTimingInfo(windowRecord, TRUE, 0, &onsetVBLCount, &onsetVBLTime, &frameId, &compositionRate, 1)) { // Query success: Info struct has been created and returned by PsychOSGetPresentationTimingInfo()... auxArg1 = auxArg2 = 0; auxArg3 = 2; // Want us to change settings? if ( (infoType == 3) && PsychCopyInDoubleArg(3, FALSE, &auxArg1) && PsychCopyInDoubleArg(4, FALSE, &auxArg2) && PsychCopyInDoubleArg(5, FALSE, &auxArg3)) { if (auxArg1 < 0) auxArg1 = 0; targetVBL = auxArg1; if (PsychOSSetPresentParameters(windowRecord, targetVBL, (int) auxArg3, auxArg2)) { if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: GetWindowInfo: Call to PsychOSSetPresentParameters(%i, %i, %f) SUCCESS!\n", (int) auxArg1, (int) auxArg3, auxArg2); } else { if (PsychPrefStateGet_Verbosity() > 1) printf("PTB-WARNING: GetWindowInfo: Call to PsychOSSetPresentParameters() failed!\n"); } } } else { // Unsupported / Failed: PsychCopyOutDoubleArg(1, FALSE, -1); } #endif #if PSYCH_SYSTEM == PSYCH_LINUX if (infoType == 2) { // MMIO register Read for screenid "auxArg1", register offset "auxArg2": PsychCopyInDoubleArg(3, TRUE, &auxArg1); PsychCopyInDoubleArg(4, TRUE, &auxArg2); PsychCopyOutDoubleArg(1, FALSE, (double) PsychOSKDReadRegister((int) auxArg1, (unsigned int) auxArg2, NULL)); } if (infoType == 3) { // MMIO register Write for screenid "auxArg1", register offset "auxArg2", to value "auxArg3": PsychCopyInDoubleArg(3, TRUE, &auxArg1); PsychCopyInDoubleArg(4, TRUE, &auxArg2); PsychCopyInDoubleArg(5, TRUE, &auxArg3); PsychOSKDWriteRegister((int) auxArg1, (unsigned int) auxArg2, (unsigned int) auxArg3, NULL); } #endif // Done. return(PsychError_none); } // Get the window record: PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); onscreen = PsychIsOnscreenWindow(windowRecord); if (onscreen) { // Query rasterbeam position: Will return -1 if unsupported. PsychGetCGDisplayIDFromScreenNumber(&displayId, windowRecord->screenNumber); beamposition = (double) PsychGetDisplayBeamPosition(displayId, windowRecord->screenNumber); } else { beamposition = -1; } if (infoType == 1) { // Return the measured beamposition: PsychCopyOutDoubleArg(1, FALSE, beamposition); } else if (infoType == 4) { // Return async flip state: 1 = Active, 0 = Inactive. PsychCopyOutDoubleArg(1, FALSE, (((NULL != windowRecord->flipInfo) && (0 != windowRecord->flipInfo->asyncstate)) ? 1 : 0)); } else if (infoType == 5) { // Create a GL_EXT_timer_query object for this window: if (glewIsSupported("GL_EXT_timer_query")) { // Pending queries finished? if (windowRecord->gpuRenderTimeQuery > 0) { PsychErrorExitMsg(PsychError_user, "Tried to create a new GPU rendertime query, but last query not yet finished! Call Screen('Flip') first!"); } // Enable our rendering context by selecting this window as drawing target: PsychSetDrawingTarget(windowRecord); // Generate Query object: glGenQueries(1, &windowRecord->gpuRenderTimeQuery); // Emit Query: GPU will measure elapsed processing time in Nanoseconds, starting // with the first GL command executed after this command: glBeginQuery(GL_TIME_ELAPSED_EXT, windowRecord->gpuRenderTimeQuery); // Reset last measurement: windowRecord->gpuRenderTime = 0; } else { if (PsychPrefStateGet_Verbosity() > 4) printf("PTB-INFO: GetWindowInfo for infoType 5: GPU timer query objects are unsupported on this platform and GPU. Call ignored!\n"); } } else { // Return all information: PsychAllocOutStructArray(1, FALSE, 1, fieldCount, FieldNames, &s); // Rasterbeam position: PsychSetStructArrayDoubleElement("Beamposition", 0, beamposition, s); // Time of last vertical blank when a double-buffer swap occured: if ((windowRecord->flipCount > 0) && (windowRecord->time_at_last_vbl == 0) && (PsychPrefStateGet_VBLTimestampingMode() == 4)) { // If time_at_last_vbl for an already finished or at least pending flip isn't available and // we have support for OS-Builtin timestamping enabled, we try to employ OS-Builtin timestamping // to get a timestamp for the most recent pending or finished flip. If this fails or is unsupported, // it will have no effect: PsychOSGetSwapCompletionTimestamp(windowRecord, 0, &(windowRecord->time_at_last_vbl)); } // Return it - or the value zero if it is (still) undefined/unavailable: PsychSetStructArrayDoubleElement("LastVBLTimeOfFlip", 0, windowRecord->time_at_last_vbl, s); // Uncorrected timestamp of flip swap completion: PsychSetStructArrayDoubleElement("RawSwapTimeOfFlip", 0, windowRecord->rawtime_at_swapcompletion, s); // Timestamp immediately prior to call to PsychOSFlipWindowBuffers(), i.e., time at swap request submission: PsychSetStructArrayDoubleElement("TimeAtSwapRequest", 0, windowRecord->time_at_swaprequest, s); // Timestamp immediately after call to PsychOSFlipWindowBuffers() returns, i.e., time at swap request submission completion: PsychSetStructArrayDoubleElement("TimePostSwapRequest", 0, windowRecord->time_post_swaprequest, s); // Timestamp immediately after call to PsychOSFlipWindowBuffers() returns, i.e., time at swap request submission completion: PsychSetStructArrayDoubleElement("VBLTimePostFlip", 0, windowRecord->postflip_vbltimestamp, s); // Swap completion timestamp for most recently completed swap, according to OS-builtin PsychOSGetSwapCompletionTimestamp() method: PsychSetStructArrayDoubleElement("OSSwapTimestamp", 0, windowRecord->osbuiltin_swaptime, s); // Result from last GPU rendertime query as triggered by infoType 5: Zero if undefined. PsychSetStructArrayDoubleElement("GPULastFrameRenderTime", 0, windowRecord->gpuRenderTime, s); // Try to determine system time of last VBL on display, independent of any // flips / bufferswaps. lastvbl = -1; postflip_vblcount = 0; // On supported systems, we can query the OS for the system time of last VBL, so we can // use the most recent VBL timestamp as baseline for timing calculations, // instead of one far in the past. if (onscreen) { lastvbl = PsychOSGetVBLTimeAndCount(windowRecord, &postflip_vblcount); } // If we couldn't determine this information we just set lastvbl to the last known // vbl timestamp of last flip -- better than nothing... if (lastvbl < 0) lastvbl = windowRecord->time_at_last_vbl; PsychSetStructArrayDoubleElement("LastVBLTime", 0, lastvbl, s); PsychSetStructArrayDoubleElement("VBLCount", 0, (double) (psych_int64) postflip_vblcount, s); // Misc. window parameters: PsychSetStructArrayDoubleElement("StereoMode", 0, windowRecord->stereomode, s); PsychSetStructArrayDoubleElement("ImagingMode", 0, windowRecord->imagingMode, s); PsychSetStructArrayDoubleElement("SpecialFlags", 0, windowRecord->specialflags, s); PsychSetStructArrayDoubleElement("IsFullscreen", 0, (windowRecord->specialflags & kPsychIsFullscreenWindow) ? 1 : 0, s); PsychSetStructArrayDoubleElement("MultiSampling", 0, windowRecord->multiSample, s); PsychSetStructArrayDoubleElement("MissedDeadlines", 0, windowRecord->nr_missed_deadlines, s); PsychSetStructArrayDoubleElement("FlipCount", 0, windowRecord->flipCount, s); PsychSetStructArrayDoubleElement("StereoDrawBuffer", 0, windowRecord->stereodrawbuffer, s); PsychSetStructArrayDoubleElement("GuesstimatedMemoryUsageMB", 0, (double) windowRecord->surfaceSizeBytes / 1024 / 1024, s); PsychSetStructArrayDoubleElement("BitsPerColorComponent", 0, (double) windowRecord->bpc, s); // Query real size of the underlying display in order to define the vbl_startline: PsychGetScreenSize(windowRecord->screenNumber, &scw, &sch); vbl_startline = (double) sch; PsychSetStructArrayDoubleElement("VBLStartline", 0, vbl_startline, s); // And VBL endline: PsychSetStructArrayDoubleElement("VBLEndline", 0, windowRecord->VBL_Endline, s); // Video refresh interval duration from beamposition method: PsychSetStructArrayDoubleElement("VideoRefreshFromBeamposition", 0, windowRecord->ifi_beamestimate, s); // Swap group assignment and swap barrier assignment, if any: PsychSetStructArrayDoubleElement("SwapGroup", 0, windowRecord->swapGroup, s); PsychSetStructArrayDoubleElement("SwapBarrier", 0, windowRecord->swapBarrier, s); // Which basic GPU architecture is this? PsychSetStructArrayStringElement("GPUCoreId", 0, windowRecord->gpuCoreId, s); // FBO's supported, and how deep? if (windowRecord->gfxcaps & kPsychGfxCapFBO) { if (windowRecord->gfxcaps & kPsychGfxCapFPFBO32) { PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 32, s); } else if (windowRecord->gfxcaps & kPsychGfxCapFPFBO16) { PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 16, s); } else PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 8, s); } else { PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 0, s); } // How deep is alpha blending supported? if (windowRecord->gfxcaps & kPsychGfxCapFPBlend32) { PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 32, s); } else if (windowRecord->gfxcaps & kPsychGfxCapFPBlend16) { PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 16, s); } else PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 8, s); // How deep is texture mapping supported? if (windowRecord->gfxcaps & kPsychGfxCapFPTex32) { PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 32, s); } else if (windowRecord->gfxcaps & kPsychGfxCapFPTex16) { PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 16, s); } else PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 8, s); // How deep is texture filtering supported? if (windowRecord->gfxcaps & kPsychGfxCapFPFilter32) { PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 32, s); } else if (windowRecord->gfxcaps & kPsychGfxCapFPFilter16) { PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 16, s); } else PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 8, s); if (windowRecord->gfxcaps & kPsychGfxCapVCGood) { PsychSetStructArrayDoubleElement("GLSupportsPrecisionColors", 0, 1, s); } else PsychSetStructArrayDoubleElement("GLSupportsPrecisionColors", 0, 0, s); if (windowRecord->gfxcaps & kPsychGfxCapFP32Shading) { PsychSetStructArrayDoubleElement("GLSupportsFP32Shading", 0, 1, s); } else PsychSetStructArrayDoubleElement("GLSupportsFP32Shading", 0, 0, s); // Renderer information: This comes last, and would fail if async flips // are active, because it needs PsychSetDrawingTarget, which in turn needs async // flips to be inactive: PsychSetDrawingTarget(windowRecord); PsychSetStructArrayStringElement("GLVendor", 0, (char*) glGetString(GL_VENDOR), s); PsychSetStructArrayStringElement("GLRenderer", 0, (char*) glGetString(GL_RENDERER), s); PsychSetStructArrayStringElement("GLVersion", 0, (char*) glGetString(GL_VERSION), s); } // Done. 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 SCREENComputer(void) { const char *majorStructFieldNames[]={"macintosh", "windows", "osx" ,"linux", "kern", "hw", "processUserLongName", "processUserShortName", "consoleUserName", "machineName", "localHostName", "location", "MACAddress", "system" }; const char *kernStructFieldNames[]={"ostype", "osrelease", "osrevision", "version","hostname"}; const char *hwStructFieldNames[]={"machine", "model", "ncpu", "physmem", "usermem", "busfreq", "cpufreq"}; int numMajorStructDimensions=1, numKernStructDimensions=1, numHwStructDimensions=1; int numMajorStructFieldNames=14, numKernStructFieldNames=5, numHwStructFieldNames=7; PsychGenericScriptType *kernStruct, *hwStruct, *majorStruct; //char tempStr[CTL_MAXNAME]; //this seems like a bug in Darwin, CTL_MAXNAME is shorter than the longest name. char tempStr[256], *ethernetMACStr; size_t tempIntSize, tempStrSize, tempULongIntSize; int mib[2]; int tempInt; unsigned long int tempULongInt; char *tempStrPtr; CFStringRef tempCFStringRef; psych_bool stringSuccess; int stringLengthChars, ethernetMACStrSizeBytes; long gestaltResult; OSErr gestaltError; // Str255 systemVersionStr, systemVersionStrForward; int i,strIndex, bcdDigit, lengthSystemVersionString; long osMajor, osMinor, osBugfix; char systemVersionStr[256]; //all subfunctions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(0)); //fill the major struct PsychAllocOutStructArray(1, FALSE, numMajorStructDimensions, numMajorStructFieldNames, majorStructFieldNames, &majorStruct); PsychSetStructArrayDoubleElement("macintosh", 0, 0, majorStruct); PsychSetStructArrayDoubleElement("windows", 0, 0, majorStruct); PsychSetStructArrayDoubleElement("linux", 0, 0, majorStruct); PsychSetStructArrayDoubleElement("osx", 0, 1, majorStruct); //fill the kern struct and implant it within the major struct PsychAllocOutStructArray(-1, FALSE, numKernStructDimensions, numKernStructFieldNames, kernStructFieldNames, &kernStruct); mib[0]=CTL_KERN; mib[1]=KERN_OSTYPE; tempStrSize=sizeof(tempStr); ReportSysctlError(sysctl(mib, 2, tempStr, &tempStrSize, NULL, 0)); PsychSetStructArrayStringElement("ostype", 0, tempStr, kernStruct); mib[1]=KERN_OSRELEASE; tempStrSize=sizeof(tempStr); ReportSysctlError(sysctl(mib, 2, tempStr, &tempStrSize, NULL, 0)); PsychSetStructArrayStringElement("osrelease", 0, tempStr, kernStruct); mib[1]=KERN_OSREV; tempIntSize=sizeof(tempInt); ReportSysctlError(sysctl(mib, 2, &tempInt, &tempIntSize, NULL, 0)); PsychSetStructArrayDoubleElement("osrevision", 0, (double)tempInt, kernStruct); mib[1]=KERN_VERSION; tempStrSize=sizeof(tempStr); ReportSysctlError(sysctl(mib, 2, tempStr, &tempStrSize, NULL, 0)); PsychSetStructArrayStringElement("version", 0, tempStr, kernStruct); mib[1]=KERN_HOSTNAME; tempStrSize=sizeof(tempStr); ReportSysctlError(sysctl(mib, 2, tempStr, &tempStrSize, NULL, 0)); PsychSetStructArrayStringElement("hostname", 0, tempStr, kernStruct); PsychSetStructArrayStructElement("kern",0, kernStruct, majorStruct); //fill the hw struct and implant it within the major struct PsychAllocOutStructArray(-1, FALSE, numHwStructDimensions, numHwStructFieldNames, hwStructFieldNames, &hwStruct); mib[0]=CTL_HW; mib[1]=HW_MACHINE; tempStrSize=sizeof(tempStr); ReportSysctlError(sysctl(mib, 2, tempStr, &tempStrSize, NULL, 0)); PsychSetStructArrayStringElement("machine", 0, tempStr, hwStruct); mib[1]=HW_MODEL; tempStrSize=sizeof(tempStr); ReportSysctlError(sysctl(mib, 2, tempStr, &tempStrSize, NULL, 0)); PsychSetStructArrayStringElement("model", 0, tempStr, hwStruct); mib[1]=HW_NCPU; tempIntSize=sizeof(tempInt); ReportSysctlError(sysctl(mib, 2, &tempInt, &tempIntSize, NULL, 0)); PsychSetStructArrayDoubleElement("ncpu", 0, (double)tempInt, hwStruct); mib[1]=HW_MEMSIZE; long long tempLongInt; tempULongIntSize=sizeof(tempLongInt); ReportSysctlError(sysctl(mib, 2, &tempLongInt, &tempULongIntSize, NULL, 0)); PsychSetStructArrayDoubleElement("physmem", 0, (double)tempLongInt, hwStruct); mib[1]=HW_USERMEM; tempULongIntSize=sizeof(tempULongInt); ReportSysctlError(sysctl(mib, 2, &tempULongInt, &tempULongIntSize, NULL, 0)); PsychSetStructArrayDoubleElement("usermem", 0, (double)tempULongInt, hwStruct); mib[1]=HW_BUS_FREQ; tempULongIntSize=sizeof(tempULongInt); ReportSysctlError(sysctl(mib, 2, &tempULongInt, &tempULongIntSize, NULL, 0)); PsychSetStructArrayDoubleElement("busfreq", 0, (double)tempULongInt, hwStruct); mib[1]=HW_CPU_FREQ; tempULongIntSize=sizeof(tempULongInt); ReportSysctlError(sysctl(mib, 2, &tempULongInt, &tempULongIntSize, NULL, 0)); PsychSetStructArrayDoubleElement("cpufreq", 0, (double)tempULongInt, hwStruct); PsychSetStructArrayStructElement("hw",0, hwStruct, majorStruct); //fill in the process user, console user and machine name in the root struct. tempCFStringRef= CSCopyMachineName(); if (tempCFStringRef) { stringLengthChars=(int) CFStringGetMaximumSizeForEncoding(CFStringGetLength(tempCFStringRef), kCFStringEncodingUTF8); tempStrPtr=malloc(sizeof(char) * (stringLengthChars+1)); stringSuccess= CFStringGetCString(tempCFStringRef, tempStrPtr, stringLengthChars+1, kCFStringEncodingUTF8); if(stringSuccess) { PsychSetStructArrayStringElement("machineName", 0, tempStrPtr, majorStruct); } else { PsychSetStructArrayStringElement("machineName", 0, "UNKNOWN! QUERY FAILED DUE TO EMPTY OR PROBLEMATIC NAME.", majorStruct); } free(tempStrPtr); CFRelease(tempCFStringRef); } else { PsychSetStructArrayStringElement("machineName", 0, "UNKNOWN! QUERY FAILED DUE TO EMPTY OR PROBLEMATIC NAME.", majorStruct); } tempCFStringRef= CSCopyUserName(TRUE); //use short name if (tempCFStringRef) { stringLengthChars=(int) CFStringGetMaximumSizeForEncoding(CFStringGetLength(tempCFStringRef), kCFStringEncodingUTF8); tempStrPtr=malloc(sizeof(char) * (stringLengthChars+1)); stringSuccess= CFStringGetCString(tempCFStringRef, tempStrPtr, stringLengthChars+1, kCFStringEncodingUTF8); if(stringSuccess) { PsychSetStructArrayStringElement("processUserShortName", 0, tempStrPtr, majorStruct); } else { PsychSetStructArrayStringElement("processUserShortName", 0, "UNKNOWN! QUERY FAILED DUE TO EMPTY OR PROBLEMATIC NAME.", majorStruct); } free(tempStrPtr); CFRelease(tempCFStringRef); } else { PsychSetStructArrayStringElement("processUserShortName", 0, "UNKNOWN! QUERY FAILED DUE TO EMPTY OR PROBLEMATIC NAME.", majorStruct); } tempCFStringRef= CSCopyUserName(FALSE); //use long name if (tempCFStringRef) { stringLengthChars=(int) CFStringGetMaximumSizeForEncoding(CFStringGetLength(tempCFStringRef), kCFStringEncodingUTF8); tempStrPtr=malloc(sizeof(char) * (stringLengthChars+1)); stringSuccess= CFStringGetCString(tempCFStringRef, tempStrPtr, stringLengthChars+1, kCFStringEncodingUTF8); if(stringSuccess) { PsychSetStructArrayStringElement("processUserLongName", 0, tempStrPtr, majorStruct); } else { PsychSetStructArrayStringElement("processUserLongName", 0, "UNKNOWN! QUERY FAILED DUE TO EMPTY OR PROBLEMATIC NAME.", majorStruct); } free(tempStrPtr); CFRelease(tempCFStringRef); } else { PsychSetStructArrayStringElement("processUserLongName", 0, "UNKNOWN! QUERY FAILED DUE TO EMPTY OR PROBLEMATIC NAME.", majorStruct); } tempCFStringRef= SCDynamicStoreCopyConsoleUser(NULL, NULL, NULL); if (tempCFStringRef) { stringLengthChars=(int) CFStringGetMaximumSizeForEncoding(CFStringGetLength(tempCFStringRef), kCFStringEncodingUTF8); tempStrPtr=malloc(sizeof(char) * (stringLengthChars+1)); stringSuccess= CFStringGetCString(tempCFStringRef, tempStrPtr, stringLengthChars+1, kCFStringEncodingUTF8); if(stringSuccess) { PsychSetStructArrayStringElement("consoleUserName", 0, tempStrPtr, majorStruct); } else { PsychSetStructArrayStringElement("consoleUserName", 0, "UNKNOWN! QUERY FAILED DUE TO EMPTY OR PROBLEMATIC NAME.", majorStruct); } free(tempStrPtr); CFRelease(tempCFStringRef); } else { PsychSetStructArrayStringElement("consoleUserName", 0, "UNKNOWN! QUERY FAILED DUE TO EMPTY OR PROBLEMATIC NAME.", majorStruct); } tempCFStringRef= SCDynamicStoreCopyLocalHostName(NULL); if (tempCFStringRef) { stringLengthChars=(int) CFStringGetMaximumSizeForEncoding(CFStringGetLength(tempCFStringRef), kCFStringEncodingUTF8); tempStrPtr=malloc(sizeof(char) * (stringLengthChars+1)); stringSuccess= CFStringGetCString(tempCFStringRef, tempStrPtr, stringLengthChars+1, kCFStringEncodingUTF8); if(stringSuccess) { PsychSetStructArrayStringElement("localHostName", 0, tempStrPtr, majorStruct); } else { PsychSetStructArrayStringElement("localHostName", 0, "UNKNOWN! QUERY FAILED DUE TO EMPTY OR PROBLEMATIC NAME.", majorStruct); } free(tempStrPtr); CFRelease(tempCFStringRef); } else { PsychSetStructArrayStringElement("localHostName", 0, "UNKNOWN! QUERY FAILED DUE TO EMPTY OR PROBLEMATIC NAME.", majorStruct); } tempCFStringRef= SCDynamicStoreCopyLocation(NULL); if (tempCFStringRef) { stringLengthChars=(int) CFStringGetMaximumSizeForEncoding(CFStringGetLength(tempCFStringRef), kCFStringEncodingUTF8); tempStrPtr=malloc(sizeof(char) * (stringLengthChars+1)); stringSuccess= CFStringGetCString(tempCFStringRef, tempStrPtr, stringLengthChars+1, kCFStringEncodingUTF8); if(stringSuccess) { PsychSetStructArrayStringElement("location", 0, tempStrPtr, majorStruct); } else { PsychSetStructArrayStringElement("location", 0, "UNKNOWN! QUERY FAILED DUE TO EMPTY OR PROBLEMATIC NAME.", majorStruct); } free(tempStrPtr); CFRelease(tempCFStringRef); } else { PsychSetStructArrayStringElement("location", 0, "UNKNOWN! QUERY FAILED DUE TO EMPTY OR PROBLEMATIC NAME.", majorStruct); } //Add the ethernet MAC address of the primary ethernet interface to the stuct. This can serve as a unique identifier for the computer. ethernetMACStrSizeBytes=GetPrimaryEthernetAddressStringLengthBytes(TRUE)+1; ethernetMACStr=(char*) malloc(sizeof(char) * ethernetMACStrSizeBytes); GetPrimaryEthernetAddressString(ethernetMACStr, TRUE, TRUE); PsychSetStructArrayStringElement("MACAddress", 0, ethernetMACStr, majorStruct); free(ethernetMACStr); //Add the system version string: Gestalt(gestaltSystemVersionMajor, &osMajor); Gestalt(gestaltSystemVersionMinor, &osMinor); Gestalt(gestaltSystemVersionBugFix, &osBugfix); sprintf(systemVersionStr, "Mac OS %i.%i.%i", osMajor, osMinor, osBugfix); //embed it in the return struct PsychSetStructArrayStringElement("system", 0, systemVersionStr, majorStruct); /* OLD DEAD Implementation, left for now as a reference... //Add the system version string: gestaltError=Gestalt(gestaltSystemVersion, &gestaltResult); //The result is a four-digit value stored in BCD in the lower 16-bits of the result. There are implicit decimal // points between the last three digis. For example Mac OS 10.3.6 is: // // 0000 0000 0000 0000 0001 0000 0011 0110 // 1 0 3 6 // 1 0. 3. 6 strIndex=0; //4th digit. bcdDigit=gestaltResult & 15; gestaltResult= gestaltResult>>4; strIndex=strIndex+sprintf(systemVersionStr+strIndex, "%i", bcdDigit); //decimal point strIndex=strIndex+sprintf(systemVersionStr+strIndex, "%s", "."); //3rd digit bcdDigit=gestaltResult & 15; gestaltResult= gestaltResult>>4; strIndex=strIndex+sprintf(systemVersionStr+strIndex, "%i", bcdDigit); //decimal point strIndex=strIndex+sprintf(systemVersionStr+strIndex, "%s", "."); //second digit //2nd digit. bcdDigit=gestaltResult & 15; gestaltResult= gestaltResult>>4; strIndex=strIndex+sprintf(systemVersionStr+strIndex, "%i", bcdDigit); //1st digit bcdDigit=gestaltResult & 15; gestaltResult= gestaltResult>>4; strIndex=strIndex+sprintf(systemVersionStr+strIndex, "%i", bcdDigit); //preface with "Mac OS " strIndex=strIndex+sprintf(systemVersionStr+strIndex, "%s", " SO caM"); //reverse to make it forward lengthSystemVersionString=strlen(systemVersionStr); for(i=0;i<lengthSystemVersionString;i++){ systemVersionStrForward[lengthSystemVersionString-1-i]=systemVersionStr[i]; } systemVersionStrForward[lengthSystemVersionString]='\0'; //embed it in the return struct PsychSetStructArrayStringElement("system", 0, systemVersionStrForward, majorStruct); */ return(PsychError_none); }
PsychError SCREENFillPoly(void) { PsychColorType color; PsychWindowRecordType *windowRecord; double whiteValue; int i, mSize, nSize, pSize; psych_bool isArgThere; double *pointList; double isConvex; int j,k; int flag; double z; combinerCacheSlot = 0; combinerCacheSize = 0; combinerCache = NULL; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(4)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord); //Get the color argument or use the default, then coerce to the form determened by the window depth. isArgThere=PsychCopyInColorArg(2, FALSE, &color); if(!isArgThere){ whiteValue=PsychGetWhiteValueFromWindow(windowRecord); PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other. } PsychCoerceColorMode( &color); //get the list of pairs and validate. PsychAllocInDoubleMatArg(3, kPsychArgRequired, &mSize, &nSize, &pSize, &pointList); if(nSize!=2) PsychErrorExitMsg(PsychError_user, "Width of pointList must be 2"); if(mSize<3) PsychErrorExitMsg(PsychError_user, "Polygons must consist of at least 3 points; M dimension of pointList was < 3!"); if(pSize>1) PsychErrorExitMsg(PsychError_user, "pointList must be a 2D matrix, not a 3D matrix!"); isConvex = -1; PsychCopyInDoubleArg(4, kPsychArgOptional, &isConvex); // On non-OpenGL1/2 we always force isConvex to zero, so the GLU tesselator is // always used. This because the tesselator only emits GL_TRIANGLES and GL_TRIANGLE_STRIP // and GL_TRIANGLE_FANS primitives which are supported on all current OpenGL API's, whereas // or "classic" fast-path needs GL_POLYGONS, which are only supported on classic OpenGL1/2: if (!PsychIsGLClassic(windowRecord)) isConvex = 0; // Enable this windowRecords framebuffer as current drawingtarget: PsychSetDrawingTarget(windowRecord); // Set default drawshader: PsychSetShader(windowRecord, -1); PsychUpdateAlphaBlendingFactorLazily(windowRecord); PsychSetGLColor(&color, windowRecord); ///////// Test for convexity //////// // This algorithm checks, if the polygon is definitely convex, or not. // We take the slow-path, if polygon is non-convex or if we can't prove // that it is convex. // // Algorithm adapted from: http://astronomy.swin.edu.au/~pbourke/geometry/clockwise/ // Which was written by Paul Bourke, 1998. // // -> This webpage explains the mathematical principle behind the test and provides // a C-Source file which has been adapted for use here. // if (isConvex == -1) { flag = 0; for (i=0; i < mSize; i++) { j = (i + 1) % mSize; k = (i + 2) % mSize; z = (pointList[j] - pointList[i]) * (pointList[k+mSize] - pointList[j+mSize]); z -= (pointList[j+mSize] - pointList[i+mSize]) * (pointList[k] - pointList[j]); if (z < 0) { flag |= 1; } else if (z > 0) { flag |= 2; } if (flag == 3) { // This is definitely a CONCAVE polygon --> not Convex --> Take slow but safe path. break; } } if (flag!=0 && flag!=3) { // This is a convex polygon --> Take fast path. isConvex = 1; } else { // This is a complex polygon --> can't determine if it is convex or not --> Take slow but safe path. isConvex = 0; } } ////// Switch between fast path and slow path, depending on convexity of polygon: if (isConvex > 0) { // Convex, non-self-intersecting polygon - Take the fast-path: glBegin(GL_POLYGON); for(i=0;i<mSize;i++) glVertex2d((GLdouble)pointList[i], (GLdouble)pointList[i+mSize]); glEnd(); } else { // Possibly concave and/or self-intersecting polygon - At least we couldn't prove it is convex. // Take the slow, but safe, path using GLU-Tesselators to break it up into a couple of convex, simple // polygons: // Create and initialize a new GLU-Tesselator object, if needed: if (NULL == tess) { // Create tesselator: tess = gluNewTess(); if (NULL == tess) PsychErrorExitMsg(PsychError_outofMemory, "Out of memory condition in Screen('FillPoly')! Not enough space."); // Assign our callback-functions: gluTessCallback(tess, GLU_TESS_BEGIN, GLUTESSCBCASTER PsychtcbBegin); gluTessCallback(tess, GLU_TESS_VERTEX, GLUTESSCBCASTER PsychtcbVertex); gluTessCallback(tess, GLU_TESS_END, GLUTESSCBCASTER PsychtcbEnd); gluTessCallback(tess, GLU_TESS_COMBINE, GLUTESSCBCASTER PsychtcbCombine); // Define all tesselated polygons to lie in the x-y plane: gluTessNormal(tess, 0, 0, 1); } // We need to hold the values in a temporary array: if (tempvsize < mSize) { tempvsize = ((mSize / 1000) + 1) * 1000; tempv = (double*) realloc((void*) tempv, sizeof(double) * 3 * tempvsize); if (NULL == tempv) PsychErrorExitMsg(PsychError_outofMemory, "Out of memory condition in Screen('FillPoly')! Not enough space."); } // Now submit our Polygon for tesselation: gluTessBeginPolygon(tess, NULL); gluTessBeginContour(tess); for(i=0; i < mSize; i++) { tempv[i*3]=(GLdouble) pointList[i]; tempv[i*3+1]=(GLdouble) pointList[i+mSize]; tempv[i*3+2]=0; gluTessVertex(tess, (GLdouble*) &(tempv[i*3]), (void*) &(tempv[i*3])); } // Process, finalize and render it by calling our callback-functions: gluTessEndContour(tess); gluTessEndPolygon (tess); // Done with drawing the filled polygon. (Slow-Path) } // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); // printf("CombinerCalls %i out of %i allocated.\n", combinerCacheSlot, combinerCacheSize); return(PsychError_none); }
PsychError SCREENLineStipple(void) { PsychWindowRecordType *winRec; static GLushort stipplePatternTemp; boolean isFactorThere, isPatternThere, isFlagThere, didChange; double *newFactor; boolean *newPatternArray; int numInputVectorElements; PsychNativeBooleanType *oldPatternArray; boolean *newEnableFlag; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(4)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(3)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &winRec); //return existing values PsychCopyOutFlagArg(1, kPsychArgOptional, winRec->stippleEnabled); PsychCopyOutDoubleArg(2, kPsychArgOptional, (double)winRec->stippleFactor); PsychAllocOutBooleanMatArg(3, kPsychArgOptional, 1, 16, 0, &oldPatternArray); ConvertShortToStippleArray(oldPatternArray, winRec->stipplePattern); //read in new values didChange=FALSE; isFlagThere=PsychAllocInFlagArg(2, kPsychArgOptional, &newEnableFlag); if(isFlagThere && *newEnableFlag != winRec->stippleEnabled){ didChange=TRUE; winRec->stippleEnabled=*newEnableFlag; } isFactorThere=PsychAllocInDoubleArg(3, kPsychArgOptional, &newFactor); if(isFactorThere && (GLint)(*newFactor) != winRec->stippleFactor){ didChange=TRUE; winRec->stippleFactor=(GLint)(*newFactor); } //NOTE: fix PsychAllocInFlagArgVector so that it limits to numInputVectorElements. isPatternThere=PsychAllocInFlagArgVector(4, kPsychArgOptional, &numInputVectorElements, &newPatternArray); if(isPatternThere){ if(numInputVectorElements != 16) PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Argument \"stipplePattern\" should be a vector of 16 elements in size"); ConvertStippleArrayToShort(newPatternArray, &stipplePatternTemp); if(stipplePatternTemp != winRec->stipplePattern){ didChange=TRUE; winRec->stipplePattern=stipplePatternTemp; } } //Update GL context according to new settings. if(didChange){ //avoids unnecessary context switches PsychSetGLContext(winRec); glLineStipple(winRec->stippleFactor, winRec->stipplePattern); if(winRec->stippleEnabled) glEnable(GL_LINE_STIPPLE); else glDisable(GL_LINE_STIPPLE); } //All psychfunctions require this. return(PsychError_none); }
PsychError PSYCHHIDReceiveReports(void) { long error=0; int deviceIndex; mxArray **mxErrPtr; const mxArray *mxOptions,*mx; PsychPushHelp(useString,synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(2)); PsychCopyInIntegerArg(1,TRUE,&deviceIndex); if(deviceIndex < 0 || deviceIndex >= MAXDEVICEINDEXS) PrintfExit("Sorry. Can't cope with deviceNumber %d (more than %d). Please tell [email protected]",deviceIndex, (int) MAXDEVICEINDEXS-1); /* "\"options.print\" =1 (default 0) enables diagnostic printing of a summary of each report when our callback routine receives it. " "\"options.printCrashers\" =1 (default 0) enables diagnostic printing of the creation of the callback source and its addition to the CFRunLoop. " "\"options.maxReports\" (default 10000) allocate space for at least this many reports, shared among all devices. " "\"options.maxReportSize\" (default 65) allocate this many bytes per report. " */ //optionsPrintReportSummary=0; // options.print //optionsPrintCrashers=0; // options.printCrashers //optionsMaxReports=10000; // options.maxReports //optionsMaxReportSize=65; // options.maxReportSize //optionsSecs=0.010; // options.secs mxOptions=PsychGetInArgMxPtr(2); if(mxOptions!=NULL){ mx=mxGetField(mxOptions,0,"print"); if(mx!=NULL)optionsPrintReportSummary=(psych_bool)mxGetScalar(mx); mx=mxGetField(mxOptions,0,"printCrashers"); if(mx!=NULL)optionsPrintCrashers=(psych_bool)mxGetScalar(mx); mx=mxGetField(mxOptions,0,"secs"); if(mx!=NULL)optionsSecs=mxGetScalar(mx); mx=mxGetField(mxOptions,0,"consistencyChecks"); if(mx!=NULL)optionsConsistencyChecks=(psych_bool)mxGetScalar(mx); // Changing maxReports or maxReportSize triggers a reallocation of // buffer memory: mx=mxGetField(mxOptions,0,"maxReports"); if(mx!=NULL) { oneShotRealloc = TRUE; optionsMaxReports = (int) mxGetScalar(mx); } mx=mxGetField(mxOptions,0,"maxReportSize"); if(mx!=NULL) { oneShotRealloc = TRUE; optionsMaxReportSize = (int) mxGetScalar(mx); } } // Sanity check: if(optionsMaxReports < 1) PsychErrorExitMsg(PsychError_user, "PsychHID ReceiveReports: Sorry, requested maxReports count must be at least 1!"); if(optionsMaxReportSize < 1) PsychErrorExitMsg(PsychError_user, "PsychHID ReceiveReports: Sorry, requested maxReportSize must be at least 1 byte!"); if(optionsMaxReportSize > MAXREPORTSIZE) { printf("PsychHID ReceiveReports: Sorry, requested maximum report size %d bytes exceeds built-in maximum of %d bytes.\n", optionsMaxReportSize, (int) MAXREPORTSIZE); PsychErrorExitMsg(PsychError_user, "Invalid option.maxReportSize provided!"); } // Start reception of reports: This will also allocate memory for the reports // on first invocation for this deviceIndex: error = ReceiveReports(deviceIndex); mxErrPtr=PsychGetOutArgMxPtr(1); if(mxErrPtr!=NULL){ const char *fieldNames[]={"n", "name", "description"}; char *name="",*description=""; mxArray *fieldValue; PsychHIDErrors(NULL, error,&name,&description); // Get error name and description, if available. *mxErrPtr=mxCreateStructMatrix(1,1,3,fieldNames); fieldValue=mxCreateString(name); if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\"."); mxSetField(*mxErrPtr,0,"name",fieldValue); fieldValue=mxCreateString(description); if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\"."); mxSetField(*mxErrPtr,0,"description",fieldValue); fieldValue=mxCreateDoubleMatrix(1,1,mxREAL); if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\"."); *mxGetPr(fieldValue)=(double)error; mxSetField(*mxErrPtr,0,"n",fieldValue); } return(PsychError_none); }
// Open a serial port on a serial port device: PsychError IOPORTOpenSerialPort(void) { static char useString[] = "[handle, errmsg] = IOPort('OpenSerialPort', port [, configString]);"; static char synopsisString[] = "Open a serial port device, return a 'handle' to it.\n" "If a port can't be opened, the function will abort with error, unless the " "level of verbosity is set to zero, in which case the function will silently " "fail, but return an invalid (negative) handle to signal the failure to the " "calling script. The optional return argument 'errmsg' contains a text string " "which is either empty on success, or contains a descriptive error message. \n" "'port' is usually a name string that defines the serial port device " "to open. On MS-Windows this could be, e.g., 'COM1' or 'COM2' etc. On " "Apple OS/X, it is the path to a BSD device file, e.g., '/dev/cu.usbserial-FT3Z95V5' " "for a serial-over-USB device with unique id FT3Z95V5. On GNU/Linux it could be " "'/dev/ttyS0' for the first real serial port, or '/dev/ttyUSB0' for the first " "serial-over-USB device.\n\n" "The optional string 'configString' is a string with pairs of paramName=paramValue " "tokens, separated by a delimiter, e.g., a space. It allows to specify specific " "values 'paramValue' to specific serial port parameters 'paramName'. Not all " "parameters are supported by all operating systems, and all settings have reasonable " "defaults. Settings unknown to a specific operating system are ignored.\n" "The following is a list of (possibly) supported parameters with their defaults:\n\n" "BaudRate=9600 -- The baud transmission rate of the connection. Standard baud rates include " "110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000 and " "256000 bits per second. Not all values may be supported by all operating systems and drivers.\n\n" "Parity=None -- Type of parity checking: None, Even, Odd.\n\n" "DataBits=8 -- Number of data bits per packet: 5,6,7 or 8, on Windows also 16.\n\n" "StopBits=1 -- Number of stop bits per packet: 1 or 2.\n\n" "FlowControl=None -- Type of flow control: None, Hardware (RTS/CTS lines), Software (XON/XOFF characters).\n\n" "Terminator=os default -- Type of terminator, given as ASCII character value, e.g., 13 for char(13) aka CR. Currently unused\n\n" "DTR=os default -- Setting for 'Data Terminal Ready' pin: 0 or 1.\n\n" "RTS=os default -- Setting for 'Request To Send' pin: 0 or 1.\n\n" "BreakBehaviour=Ignore -- Behaviour if a 'Break Condition' is detected on the line: Ignore, Flush, Zero. On Windows, this setting is ignored.\n\n" "OutputBufferSize=4096 -- Size of output buffer in bytes.\n\n" "InputBufferSize=4096 -- Size of input buffer in bytes. You can't read more than that amount per read command.\n\n" "HardwareBufferSizes=input,output -- Set size of the hardware driver internal input and output buffers in bytes. " "E.g., HardwareBufferSizes=32768,8192 would set the input buffer to 32768 bytes and the output buffer to 8192 bytes. " "This function is currently only supported on Windows, ignored on other systems. It is only a polite hint to the driver, " "the serial port driver is free to ignore the request and choose any buffer sizes or buffering strategy it finds appropriate. " "By default, this parameter is not set by IOPort and the hardware driver uses some built-in reasonable setting.\n\n" "The following timeout values are inter-byte timeouts. You specify how much time reception or " "transmission of a single byte is allowed to take. Timeout occurs if more than that time elapses " "between send/reception of two consecutive bytes or if the total amount of time exceeds the " "number of bytes, times the interbyte timeout value. A value of zero means not to use any timeout, " "in which case a blocking read or write may take forever. If a timeout occurs, the read or write " "operation will be aborted. \n" "Granularity of timeout settings is 100 msecs on OS/X and Linux, 1 msec on Windows, all values " "are rounded to the closest value matching that granularity. The minimal timeout is 100 msecs " "on OS/X and Linux, about 6 msecs on Windows.\n\n" "SendTimeout=1.0 -- Interbyte send timeout in seconds. Only used on Windows.\n\n" "ReceiveTimeout=1.0 -- Interbyte receive timeout in seconds.\n\n" "ReceiveLatency=0.000001 -- Latency in seconds for processing of new input bytes. Only used on OS/X.\n\n" "PollLatency=0.0005 (0.005 on Windows) -- Latency between polls in seconds for polling in some 'Read' operations.\n\n" "ProcessingMode=Raw -- Mode of input/output processing: Raw or Cooked. On Windows, only Raw (binary) mode is supported.\n\n" "StartBackgroundRead=readGranularity -- Enable asynchronous background read operations on the port. This is only supported " "on Linux and OS/X for now. A parallel background thread is started which tries to fetch 'readGranularity' bytes of data, " "polling the port every 'PollLatency' seconds for at least 'readGranularity' bytes of data. 'InputBufferSize' must be an " "integral multiple of 'readGranularity' for this to work. Later IOPort('Read') commands will pull collected data from " "the InputBuffer in quanta of at most 'readGranularity' bytes per invocation. This function is useful for background " "data collection from devices that stream some data at a constant rate. You set up background read, let the parallel " "thread do all data collection in the background and collect the data at the end of a session with a sequence of " "IOPort('Read') calls. This way, data collection doesn't clutter your main experiment script.\n\n" "StopBackgroundRead -- Stop running background read operation, discard all pending data.\n\n"; static char seeAlsoString[] = "'CloseAll'"; #if PSYCH_SYSTEM == PSYCH_WINDOWS // Difference to Unices: PollLatency defaults to 5 msecs instead of 0.5 msecs due to shoddy windows scheduler: static char defaultConfig[] = "BaudRate=9600 Parity=None DataBits=8 StopBits=1 FlowControl=None PollLatency=0.005 ReceiveLatency=0.000001 SendTimeout=1.0 ReceiveTimeout=1.0 ProcessingMode=Raw BreakBehaviour=Ignore OutputBufferSize=4096 InputBufferSize=4096"; #else static char defaultConfig[] = "BaudRate=9600 Parity=None DataBits=8 StopBits=1 FlowControl=None PollLatency=0.0005 ReceiveLatency=0.000001 SendTimeout=1.0 ReceiveTimeout=1.0 ProcessingMode=Raw BreakBehaviour=Ignore OutputBufferSize=4096 InputBufferSize=4096"; #endif char finalConfig[2000]; char errmsg[1024]; char* portSpec = NULL; char* configString = NULL; PsychSerialDeviceRecord* device = NULL; int handle; // Setup online help: PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none); }; PsychErrorExit(PsychCapNumInputArgs(2)); // The maximum number of inputs PsychErrorExit(PsychRequireNumInputArgs(1)); // The required number of inputs PsychErrorExit(PsychCapNumOutputArgs(2)); // The maximum number of outputs // Get required portSpec: PsychAllocInCharArg(1, kPsychArgRequired, &portSpec); // Get the optional configString: if (!PsychAllocInCharArg(2, kPsychArgOptional, &configString)) { // No config string specified: Assign default string: sprintf(finalConfig, "%s", defaultConfig); } else { // Config string provided: Prepend it to default string. That way, all settings // spec'd by usercode will override the defaultConfig, but non-specified settings // will be provided by defaultConfig: sprintf(finalConfig, "%s %s", configString, defaultConfig); } // Search for a free slot: if (portRecordCount >= PSYCH_MAX_IOPORTS) PsychErrorExitMsg(PsychError_user, "Maximum number of open Input/Output ports exceeded."); // Iterate until end or free slot: for (handle=0; (handle < PSYCH_MAX_IOPORTS) && (portRecordBank[handle].portType); handle++); if (portRecordBank[handle].portType) PsychErrorExitMsg(PsychError_user, "Maximum number of open Input/Output ports exceeded."); // handle is index into our port record... // Call OS specific open routine for serial port: device = PsychIOOSOpenSerialPort(portSpec, finalConfig, errmsg); // Copy out optional errmsg string: PsychCopyOutCharArg(2, kPsychArgOptional, errmsg); if (device == NULL) { // Special case: Could not open port, but verbosity level is zero, no conventional // error return possible as this would clutter the console with output. Simply cancel // open op and return a negative handle to signal failure to user code: PsychCopyOutDoubleArg(1, kPsychArgRequired, -1); return(PsychError_none); } // If we reach this point, then device is the pointer to the class specific device struct and the // open operation was successfull. Build port struct: portRecordBank[handle].portType = kPsychIOPortSerial; portRecordBank[handle].device = (void*) device; portRecordCount++; // Return handle to new serial port object: PsychCopyOutDoubleArg(1, kPsychArgRequired, (double) handle); return(PsychError_none); }
PsychError SCREENOpenWindow(void) { int screenNumber, numWindowBuffers, stereomode, multiSample, imagingmode; PsychRectType rect, screenrect; PsychColorType color; PsychColorModeType mode; boolean isArgThere, settingsMade, didWindowOpen, useAGL; PsychScreenSettingsType screenSettings; PsychWindowRecordType *windowRecord; double dVals[4]; PsychDepthType specifiedDepth, possibleDepths, currentDepth, useDepth; int dummy1; double dummy2, dummy3, dummy4; Boolean EmulateOldPTB = PsychPrefStateGet_EmulateOldPTB(); //just for debugging //if (PSYCH_DEBUG == PSYCH_ON) printf("Entering SCREENOpen\n"); //all sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the number of inputs PsychErrorExit(PsychCapNumInputArgs(8)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(2)); //The maximum number of outputs //get the screen number from the windowPtrOrScreenNumber. This also checks to make sure that the specified screen exists. PsychCopyInScreenNumberArg(kPsychUseDefaultArgPosition, TRUE, &screenNumber); if(screenNumber==-1) PsychErrorExitMsg(PsychError_user, "The specified onscreen window has no ancestral screen."); /* The depth checking is ugly because of this stupid depth structure stuff. Instead get a descriptor of the current video settings, change the depth field, and pass it to a validate function wich searches a list of valid video modes for the display. There seems to be no point in checking the depths alone because the legality of a particular depth depends on the other settings specified below. Its probably best to wait until we have digested all settings and then test the full mode, declarin an invalid mode and not an invalid pixel size. We could notice when the depth alone is specified and in that case issue an invalid depth value. */ //find the PixelSize first because the color specifier depends on the screen depth. PsychInitDepthStruct(¤tDepth); //get the current depth PsychGetScreenDepth(screenNumber, ¤tDepth); PsychInitDepthStruct(&possibleDepths); //get the possible depths PsychGetScreenDepths(screenNumber, &possibleDepths); #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_WINDOWS // MK Experimental Hack: Add the special depth values 64 and 128 to the depth struct. This should // allows for 16 bpc, 32 bpc floating point color buffers on the latest ATI and NVidia hardware. // "Should" means: It doesn't really work with any current driver, but we leave the testcode in // in the hope for future OS and driver releases ;-) // Unfortunately at this point of the init sequence, we are not able // to check if these formats are supported by the hardware. Ugly ugly ugly... PsychAddValueToDepthStruct(64, &possibleDepths); PsychAddValueToDepthStruct(128, &possibleDepths); #endif // #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX // On MacOS/X and Linux with ATI Radeon X1000/HD2000/HD3000 hardware and the special // kernel support driver installed, we should be able to configure the hardwares // framebuffers into ABGR2101010 mode, ie. 2 bits alpha, 10 bpc for red, green, blue. // This needs support from the imaging pipeline, or manually converted stimuli, as // the GPU doesn't format pixel data properly, only the CRTC scans out in that format. // Anyway, allow this setting on OS/X and Linux: // Update: Some FireGL cards (2008 and later) claim to support this on MS-Windows. Enable // this option on Windows as well, so it is at least testable: PsychAddValueToDepthStruct(30, &possibleDepths); // #endif PsychInitDepthStruct(&specifiedDepth); //get the requested depth and validate it. isArgThere = PsychCopyInSingleDepthArg(4, FALSE, &specifiedDepth); PsychInitDepthStruct(&useDepth); if(isArgThere){ //if the argument is there check that the screen supports it... if(!PsychIsMemberDepthStruct(&specifiedDepth, &possibleDepths)) PsychErrorExit(PsychError_invalidDepthArg); else PsychCopyDepthStruct(&useDepth, &specifiedDepth); }else //otherwise use the default PsychCopyDepthStruct(&useDepth, ¤tDepth); // Initialize the rect argument to the screen rectangle: PsychGetScreenRect(screenNumber, rect); //get the rect describing the screen bounds. This is the default Rect. // Override it with a user supplied rect, if one was supplied: isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect ); if (IsPsychRectEmpty(rect)) PsychErrorExitMsg(PsychError_user, "OpenWindow called with invalid (empty) rect argument."); if (PSYCH_SYSTEM == PSYCH_OSX) { // OS/X system: Need to decide if we use a Carbon window + AGL // or a fullscreen context with CGL: // Default to AGL, switch to CGL if below constraints are met: useAGL = TRUE; // Window rect provided which has a different size than screen? // We do not use windowed mode if the provided window rectangle either // matches the target screens rectangle (and therefore its exact size) // or its screens global rectangle. PsychGetScreenRect(screenNumber, screenrect); if (PsychMatchRect(screenrect, rect)) useAGL=FALSE; PsychGetGlobalScreenRect(screenNumber, screenrect); if (PsychMatchRect(screenrect, rect)) useAGL=FALSE; // Override for use on f$%#$Fd OS/X 10.5.3 - 10.5.6 with NVidia GF 8800 GPU's: if (PsychPrefStateGet_ConserveVRAM() & kPsychUseAGLCompositorForFullscreenWindows) useAGL = TRUE; } else { // Non OS/X system: Do not use AGL ;-) useAGL = FALSE; } //find the number of specified buffers. //OS X: The number of backbuffers is not a property of the display mode but an attribute of the pixel format. // Therefore the value is held by a window record and not a screen record. numWindowBuffers=2; PsychCopyInIntegerArg(5,FALSE,&numWindowBuffers); if(numWindowBuffers < 1 || numWindowBuffers > kPsychMaxNumberWindowBuffers) PsychErrorExit(PsychError_invalidNumberBuffersArg); // MK: Check for optional spec of stereoscopic display: 0 (the default) = monoscopic viewing. // 1 == Stereo output via OpenGL built-in stereo facilities: This will drive any kind of // stereo display hardware that is directly supported by MacOS-X. // 2/3 == Stereo output via compressed frame output: Only one backbuffer is used for both // views: The left view image is put into the top-half of the screen, the right view image // is put into the bottom half of the screen. External hardware demangles this combi-image // again into two separate images. CrystalEyes seems to be able to do this. One looses half // of the vertical resolution, but potentially gains refresh rate... // Future PTB version may include different stereo algorithms with an id > 1, e.g., // anaglyph stereo, interlaced stereo, ... stereomode=0; PsychCopyInIntegerArg(6,FALSE,&stereomode); if(stereomode < 0 || stereomode > 10) PsychErrorExitMsg(PsychError_user, "Invalid stereomode provided (Valid between 0 and 10)."); if (stereomode!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, stereo display functions are not supported in OS-9 PTB emulation mode."); multiSample=0; PsychCopyInIntegerArg(7,FALSE,&multiSample); if(multiSample < 0) PsychErrorExitMsg(PsychError_user, "Invalid multisample value provided (Valid are positive numbers >= 0)."); if (multiSample!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, anti-aliasing functions are not supported in OS-9 PTB emulation mode."); imagingmode=0; PsychCopyInIntegerArg(8,FALSE,&imagingmode); if(imagingmode < 0) PsychErrorExitMsg(PsychError_user, "Invalid imaging mode provided (See 'help PsychImagingMode' for usage info)."); if (imagingmode!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, imaging pipeline functions are not supported in OS-9 PTB emulation mode."); // We require use of the imaging pipeline if stereomode for dualwindow display is requested. // This makes heavy use of FBO's and blit operations, so imaging pipeline is needed. if (stereomode==kPsychDualWindowStereo) { // Dual window stereo requested, but imaging pipeline not enabled. Enable it: imagingmode|= kPsychNeedFastBackingStore; if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Trying to enable imaging pipeline for dual-window stereo display mode...\n"); } //set the video mode to change the pixel size. TO DO: Set the rect and the default color PsychGetScreenSettings(screenNumber, &screenSettings); PsychInitDepthStruct(&(screenSettings.depth)); PsychCopyDepthStruct(&(screenSettings.depth), &useDepth); // Here is where all the work goes on: // If the screen is not already captured then to that: if(!PsychIsScreenCaptured(screenNumber) && !useAGL) { PsychCaptureScreen(screenNumber); // We disable the call to PsychSetScreenSettings here: Its not useful, as it // could only change color depth - which is something we don't want to do anyway here. // If people want to change displays settings, they should use Screen('Resolution') instead, // which is a more clever interface to PsychSetScreenSettings(). // settingsMade=PsychSetScreenSettings(screenNumber, &screenSettings); //Capturing the screen and setting its settings always occur in conjunction //There should be a check above to see if the display is captured and openWindow is attempting to chang //the bit depth } #if PSYCH_SYSTEM == PSYCH_WINDOWS // On M$-Windows we currently only support - and therefore require >= 30 bpp color depth. if (PsychGetScreenDepthValue(screenNumber) < 30) { // Display running at less than 30 bpp. OpenWindow will fail on M$-Windows anyway, so let's abort // now. // Output warning text: printf("PTB-ERROR: Your display screen %i is not running at the required color depth of at least 30 bit.\n", screenNumber); printf("PTB-ERROR: The current setting is %i bit color depth..\n", PsychGetScreenDepthValue(screenNumber)); printf("PTB-ERROR: This will not work on Microsoft Windows operating systems.\n"); printf("PTB-ERROR: Please use the 'Display settings' control panel of Windows to change the color depth to\n"); printf("PTB-ERROR: 32 bits per pixel ('True color' or 'Highest' setting) and then retry. It may be neccessary\n"); printf("PTB-ERROR: to restart Matlab after applying the change...\n"); fflush(NULL); // Release the captured screen: PsychRestoreScreenSettings(screenNumber); PsychReleaseScreen(screenNumber); // Reset master assignment to prepare possible further dual-window config operations: sharedContextWindow = NULL; // Abort with Matlab error: PsychErrorExitMsg(PsychError_user, "Insufficient color depth setting for display device (smaller than 30 bpp)."); } #endif //if (PSYCH_DEBUG == PSYCH_ON) printf("Entering PsychOpenOnscreenWindow\n"); PsychCopyDepthStruct(&(screenSettings.depth), &useDepth); // Create the onscreen window and perform initialization of everything except // imaging pipeline and a few other special quirks. If sharedContextWindow is non-NULL, // the new window will share its OpenGL context ressources with sharedContextWindow. // This is typically used for dual-window stereo mode. Btw. If imaging pipeline is really // active, we force multiSample to zero: This way the system backbuffer / pixelformat // is enabled without multisampling support, as we do all the multisampling stuff ourselves // within the imaging pipeline with multisampled drawbuffer FBO's... didWindowOpen=PsychOpenOnscreenWindow(&screenSettings, &windowRecord, numWindowBuffers, stereomode, rect, ((imagingmode==0 || imagingmode==kPsychNeedFastOffscreenWindows) ? multiSample : 0), sharedContextWindow); if (!didWindowOpen) { if (!useAGL) { PsychRestoreScreenSettings(screenNumber); PsychReleaseScreen(screenNumber); } // Reset master assignment to prepare possible further dual-window config operations: sharedContextWindow = NULL; // We use this dirty hack to exit with an error, but without printing // an error message. The specific error message has been printed in // PsychOpenOnscreenWindow() already.. PsychErrMsgTxt(""); } // Sufficient display depth for full alpha-blending and such? if (PsychGetScreenDepthValue(screenNumber) < 24) { // Nope. Output a little warning. printf("PTB-WARNING: Your display screen %i is not running at 24 bit color depth or higher.\n", screenNumber); printf("PTB-WARNING: The current setting is %i bit color depth..\n", PsychGetScreenDepthValue(screenNumber)); printf("PTB-WARNING: This could cause failure to work correctly or visual artifacts in stimuli\n"); printf("PTB-WARNING: that involve Alpha-Blending. It can also cause drastically reduced color resolution\n"); printf("PTB-WARNING: for your stimuli! Please try to switch your display to 'True Color' (Windows)\n"); printf("PTB-WARNING: our 'Millions of Colors' (MacOS-X) to get rid of this warning and the visual artifacts.\n"); fflush(NULL); } // Define clear color: This depends on the color range of our onscreen window... isArgThere=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color); //get from user if(!isArgThere) PsychLoadColorStruct(&color, kPsychIndexColor, PsychGetWhiteValueFromWindow(windowRecord)); //or use the default PsychCoerceColorMode(&color); // Special setup code for dual window stereomode: if (stereomode == kPsychDualWindowStereo) { if (sharedContextWindow) { // This is creation & setup of the slave onscreen window, ie. the one // representing the right-eye view. This window doesn't do much. It // is not used or referenced in the users experiment script. It receives // its final image content during Screen('Flip') operation of the master // onscreen window, then gets flipped in sync with the master window. // Ok, we already have the slave window open and it shares its OpenGL context // with the master window. Reset its internal reference to the master: windowRecord->slaveWindow = NULL; // Reset imagingmode for this window prior to imaging pipeline setup. This // window is totally passive so it doesn't need the imaging pipeline. imagingmode = 0; // Assign this window to the master window as a slave: sharedContextWindow->slaveWindow = windowRecord; // Reset master assignment to prepare possible further dual-window config operations: sharedContextWindow = NULL; // Activate the IdentitiyBlitChain for the slave window and add a single identity blit // operation to it: This is needed in PsychPreFlipOperations() for final copy of stimulus // image into this slave window: PsychPipelineAddBuiltinFunctionToHook(windowRecord, "IdentityBlitChain", "Builtin:IdentityBlit", INT_MAX, ""); PsychPipelineEnableHook(windowRecord, "IdentityBlitChain"); if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Created master-slave window relationship for dual-window stereo display mode...\n"); // Special config finished. The master-slave combo should work from now on... } else { // This is initial setup & creation of the master onscreen window, ie. the one // representing the left-eye view and doing all the heavy work, acting as a // proxy for both windows. // Not much to do here. Just store its windowRecord as a reference for creation // of the slave window. We'll need it for that purpose... sharedContextWindow = windowRecord; } } // Set special half-width flag for window if we are either in a dual-display/dual-view stereo mode or if // if is requested as part of the imagingMode flag. This will cause PTB 2D drawing routines and window size // query routines etc. to return an effective window width or window rect only half the real width. if (windowRecord->stereomode==kPsychFreeFusionStereo || windowRecord->stereomode==kPsychFreeCrossFusionStereo || (imagingmode & kPsychHalfWidthWindow)) { windowRecord->specialflags = windowRecord->specialflags | kPsychHalfWidthWindow; imagingmode = imagingmode & (~kPsychHalfWidthWindow); } // Similar handling for windows of half the real height, except that none of our built-in stereo modes requires these, // so this is only done on request from external code via the imagingmode flag kPsychHalfHeightWindow. // One use of this is when using interleaved line stereo mode (PsychImaging(...'InterleavedLineStereo')) where windows // only have a useable net height of half their physical height: if (imagingmode & kPsychHalfHeightWindow) { windowRecord->specialflags = windowRecord->specialflags | kPsychHalfHeightWindow; imagingmode = imagingmode & (~kPsychHalfHeightWindow); } // Initialize internal image processing pipeline if requested: PsychInitializeImagingPipeline(windowRecord, imagingmode, multiSample); // On OS-X, if we are in quad-buffered frame sequential stereo mode, we automatically generate // blue-line-sync style sync lines for use with stereo shutter glasses. We don't do this // by default on Windows or Linux: These systems either don't have stereo capable hardware, // or they have some and its drivers already take care of sync signal generation. if ((PSYCH_SYSTEM == PSYCH_OSX) && (windowRecord->stereomode==kPsychOpenGLStereo)) { if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Enabling internal blue line sync renderer for quad-buffered stereo...\n"); PsychPipelineAddBuiltinFunctionToHook(windowRecord, "LeftFinalizerBlitChain", "Builtin:RenderStereoSyncLine", INT_MAX, ""); PsychPipelineEnableHook(windowRecord, "LeftFinalizerBlitChain"); PsychPipelineAddBuiltinFunctionToHook(windowRecord, "RightFinalizerBlitChain", "Builtin:RenderStereoSyncLine", INT_MAX, ""); PsychPipelineEnableHook(windowRecord, "RightFinalizerBlitChain"); } // Activate new onscreen window for userspace drawing: If imaging pipeline is active, this // will bind the correct rendertargets for the first time: PsychSetDrawingTarget(windowRecord); // Set the clear color and perform a backbuffer-clear: PsychConvertColorToDoubleVector(&color, windowRecord, windowRecord->clearColor); PsychGLClear(windowRecord); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); // Make sure no OpenGL errors happened up to this point: PsychTestForGLErrors(); // If we are in logo-startup mode (former blue-screen mode) and double-buffering // is enabled, then do an initial bufferswap & clear, so the display starts in // the user selected background color instead of staying at the blue screen or // logo display until the Matlab script first calls 'Flip'. if ((PsychPrefStateGet_VisualDebugLevel()>=4) && numWindowBuffers>=2) { // Do immediate bufferswap by an internal call to Screen('Flip'). This will also // take care of clearing the backbuffer in preparation of first userspace drawing // commands and such... PsychFlipWindowBuffers(windowRecord, 0, 0, 0, 0, &dummy1, &dummy2, &dummy3, &dummy4); // Display now shows background color, so user knows that PTB's 'OpenWindow' // procedure is successfully finished. } PsychTestForGLErrors(); //Return the window index and the rect argument. PsychCopyOutDoubleArg(1, FALSE, windowRecord->windowIndex); // rect argument needs special treatment in stereo mode: PsychMakeRect(&rect, windowRecord->rect[kPsychLeft], windowRecord->rect[kPsychTop], windowRecord->rect[kPsychLeft] + PsychGetWidthFromRect(windowRecord->rect)/((windowRecord->specialflags & kPsychHalfWidthWindow) ? 2 : 1), windowRecord->rect[kPsychTop] + PsychGetHeightFromRect(windowRecord->rect)/((windowRecord->specialflags & kPsychHalfHeightWindow) ? 2 : 1)); PsychCopyOutRectArg(2, FALSE, rect); return(PsychError_none); }
PsychError IOPORTWrite(void) { static char useString[] = "[nwritten, when, errmsg, prewritetime, postwritetime, lastchecktime] = IOPort('Write', handle, data [, blocking=1]);"; static char synopsisString[] = "Write data to device, specified by 'handle'.\n" "'data' must be a vector of data items to write, or a matrix (in which case data " "in the matrix will be transmitted in column-major order, ie., first the first " "column, then the 2nd column etc...), either with data elements of uint8 class " "or a (1 Byte per char) character string. The optional flag 'blocking' if " "set to 0 will ask the write function to not block, but return immediately, ie. " "data is sent/written in the background while your code continues to execute - There " "may be an arbitrary delay until data transmission is really finished. The default " "setting is 1, ie. blocking writes - The function waits until data transmission is really " "finished. You can also use blocking == 2 to request a different mode " "for blocking writes, where IOPort is polling for write-completion instead of " "a more cpu friendly wait. This may decrease latency for certain applications. " "Another even more agressive polling method is implemented via blocking == 3 on " "Linux systems with some limited set of hardware, e.g., real native serial ports.\n" "On systems without any support for specific polling modes, the 2 or 3 settings are treated " "as a standard blocking write.\n\n" "Optionally, the function returns the following return arguments:\n" "'nwritten' Number of bytes written -- Should match amount of data provided on success.\n" "'when' A timestamp of write completion: This is only meaningful in blocking mode!\n" "'errmsg' A system defined error message if something wen't wrong.\n" "The following three timestamps are for low-level debugging and special purpose:\n" "'prewritetime' A timestamp taken immediately before submitting the write request. " "'postwritetime' A timestamp taken immediately after submitting the write request. " "'lastchecktime' A timestamp taken at the time of last check for write completion if applicable. "; static char seeAlsoString[] = ""; char errmsg[1024]; int handle, blocking, m, n, p, nwritten; psych_uint8* inData = NULL; char* inChars = NULL; void* writedata = NULL; double timestamp[4] = {0, 0, 0, 0}; 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(2)); // The required number of inputs PsychErrorExit(PsychCapNumOutputArgs(6)); // The maximum number of outputs // Get required port handle: PsychCopyInIntegerArg(1, kPsychArgRequired, &handle); // Get the data: switch(PsychGetArgType(2)) { case PsychArgType_uint8: PsychAllocInUnsignedByteMatArg(2, kPsychArgRequired, &m, &n, &p, &inData); if (p!=1 || m * n == 0) PsychErrorExitMsg(PsychError_user, "'data' is not a vector or 2D matrix, but some higher dimensional matrix!"); n = m * n; writedata = (void*) inData; break; case PsychArgType_char: PsychAllocInCharArg(2, kPsychArgRequired, &inChars); n = strlen(inChars); writedata = (void*) inChars; break; default: PsychErrorExitMsg(PsychError_user, "Invalid type for 'data' vector: Must be an uint8 or char vector."); } // Get optional blocking flag: Defaults to one -- blocking. blocking = 1; PsychCopyInIntegerArg(3, kPsychArgOptional, &blocking); // Write data: nwritten = PsychWriteIOPort(handle, writedata, n, blocking, errmsg, ×tamp[0]); if (nwritten < 0 && verbosity > 0) printf("IOPort: Error: %s\n", errmsg); PsychCopyOutDoubleArg(1, kPsychArgOptional, nwritten); PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp[0]); PsychCopyOutCharArg(3, kPsychArgOptional, errmsg); PsychCopyOutDoubleArg(4, kPsychArgOptional, timestamp[1]); PsychCopyOutDoubleArg(5, kPsychArgOptional, timestamp[2]); PsychCopyOutDoubleArg(6, kPsychArgOptional, timestamp[3]); return(PsychError_none); }
PsychError SCREENFrameOval(void) { PsychRectType rect; double numSlices, outerRadius, xScale, yScale, xTranslate, yTranslate, rectY, rectX, penWidth, penHeight, penSize, innerRadius; PsychWindowRecordType *windowRecord; psych_bool isArgThere, isclassic; double *xy, *colors; unsigned char *bytecolors; double* penSizes; int numRects, i, nc, mc, nrsize; GLUquadricObj *diskQuadric; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);} //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(6)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); // Query, allocate and copy in all vectors... numRects = 4; nrsize = 0; colors = NULL; bytecolors = NULL; mc = nc = 0; // The negative position -3 means: xy coords are expected at position 3, but they are optional. // NULL means - don't want a size's vector. PsychPrepareRenderBatch(windowRecord, -3, &numRects, &xy, 2, &nc, &mc, &colors, &bytecolors, 4, &nrsize, &penSizes, FALSE); isclassic = PsychIsGLClassic(windowRecord); // Only up to one rect provided? if (numRects <= 1) { // Get the oval and draw it: PsychCopyRect(rect, windowRecord->clientrect); isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect); if (isArgThere && IsPsychRectEmpty(rect)) return(PsychError_none); numRects = 1; // Get the pen width and height arguments penWidth=1; penHeight=1; PsychCopyInDoubleArg(4, FALSE, &penWidth); PsychCopyInDoubleArg(5, FALSE, &penHeight); penSize = (penWidth > penHeight) ? penWidth : penHeight; } else { // Multiple ovals provided. Set up the first one: PsychCopyRect(rect, &xy[0]); penSize = penSizes[0]; } // Create quadric object: if (isclassic) diskQuadric = gluNewQuadric(); // Draw all ovals (one or multiple): for (i=0; i < numRects;) { // Per oval color provided? If so then set it up. If only one common color // was provided then PsychPrepareRenderBatch() has already set it up. if (nc>1) { // Yes. Set color for this specific item: PsychSetArrayColor(windowRecord, i, mc, colors, bytecolors); } // Per oval penSize provided? If so, set it up. Otherwise keep at default size // common for all ovals, set by code outside loop: if (nrsize > 1) penSize = penSizes[i]; // Compute drawing parameters for ellipse: if (!IsPsychRectEmpty(rect)) { //The glu disk object location and size with a center point and a radius, //whereas FrameOval accepts a bounding rect. Converting from one set of parameters //to the other we should careful what we do for rects size of even number of pixels in length. PsychGetCenterFromRectAbsolute(rect, &xTranslate, &yTranslate); rectY=PsychGetHeightFromRect(rect); rectX=PsychGetWidthFromRect(rect); if(rectX == rectY){ xScale=1; yScale=1; outerRadius=rectX/2; }else if(rectX > rectY){ xScale=1; yScale=rectY/rectX; outerRadius=rectX/2; }else { yScale=1; xScale=rectX/rectY; outerRadius=rectY/2; } numSlices = 3.14159265358979323846 * 2 * outerRadius; innerRadius = outerRadius - penSize; innerRadius = (innerRadius < 0) ? 0 : innerRadius; if (isclassic) { // Draw: Set up position, scale and size via matrix transform: glPushMatrix(); glTranslated(xTranslate, yTranslate, 0); glScaled(xScale, yScale, 1); // Compute disk quadric for given params: This is awfully slow and would // benefit a lot from shader magic on modern GPUs: gluDisk(diskQuadric, innerRadius, outerRadius, (int) numSlices, 1); glPopMatrix(); } else { PsychDrawDisc(windowRecord, (float) xTranslate, (float) yTranslate, (float) innerRadius, (float) outerRadius, (int) numSlices, (float) xScale, (float) yScale, 0, 360); } } // Done with this one. Set up the next one, if any... i++; if (i < numRects) { PsychCopyRect(rect, &xy[i*4]); } // Next oval. } // Release quadric object: if (isclassic) gluDeleteQuadric(diskQuadric); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); // All Psychfunctions require this. return(PsychError_none); }
PsychError SCREENDrawLines(void) { PsychWindowRecordType *windowRecord; int m,n,p, smooth; int nrsize, nrcolors, nrvertices, mc, nc, pc, i; boolean isArgThere, usecolorvector, isdoublecolors, isuint8colors; double *xy, *size, *center, *dot_type, *colors; unsigned char *bytecolors; float linesizerange[2]; double convfactor; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(6)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord); // Query, allocate and copy in all vectors... nrvertices = 2; nrsize = 1; colors = NULL; bytecolors = NULL; PsychPrepareRenderBatch(windowRecord, 2, &nrvertices, &xy, 4, &nc, &mc, &colors, &bytecolors, 3, &nrsize, &size); isdoublecolors = (colors) ? TRUE:FALSE; isuint8colors = (bytecolors) ? TRUE:FALSE; usecolorvector = (nc>1) ? TRUE:FALSE; // Get center argument isArgThere = PsychIsArgPresent(PsychArgIn, 5); if(!isArgThere){ center = (double *) PsychMallocTemp(2 * sizeof(double)); center[0] = 0; center[1] = 0; } else { PsychAllocInDoubleMatArg(5, TRUE, &m, &n, &p, ¢er); if(p!=1 || n!=2 || m!=1) PsychErrorExitMsg(PsychError_user, "center must be a 1-by-2 vector"); } // Get smooth argument isArgThere = PsychIsArgPresent(PsychArgIn, 6); if(!isArgThere){ smooth = 0; } else { PsychAllocInDoubleMatArg(6, TRUE, &m, &n, &p, &dot_type); smooth = (int) dot_type[0]; if(p!=1 || n!=1 || m!=1 || (smooth!=0 && smooth!=1)) PsychErrorExitMsg(PsychError_user, "smooth must be 0 or 1"); } // Child-protection: Alpha blending needs to be enabled for smoothing to work: if (smooth>0 && windowRecord->actualEnableBlending!=TRUE) { PsychErrorExitMsg(PsychError_user, "Line smoothing doesn't work with alpha-blending disabled! See Screen('BlendFunction') on how to enable it."); } // turn on antialiasing to draw anti-aliased lines: if(smooth) glEnable(GL_LINE_SMOOTH); // Set global width of lines: glLineWidth(size[0]); // Setup modelview matrix to perform translation by 'center': glMatrixMode(GL_MODELVIEW); // Make a backup copy of the matrix: glPushMatrix(); // Apply a global translation of (center(x,y)) pixels to all following lines: glTranslated(center[0], center[1],0); // Render the array of 2D-Lines - Efficient version: // This command sequence allows fast processing of whole arrays // of vertices (or lines, in this case). It saves the call overhead // associated with the original implementation below and is potentially // optimized in specific OpenGL implementations. // Pass a pointer to the start of the arrays: glVertexPointer(2, GL_DOUBLE, 0, &xy[0]); if (usecolorvector) { if (isdoublecolors) glColorPointer(mc, GL_DOUBLE, 0, colors); if (isuint8colors) glColorPointer(mc, GL_UNSIGNED_BYTE, 0, bytecolors); glEnableClientState(GL_COLOR_ARRAY); } // Enable fast rendering of arrays: glEnableClientState(GL_VERTEX_ARRAY); if (nrsize==1) { // Common line-width for all lines: Render all lines, starting at line 0: glDrawArrays(GL_LINES, 0, nrvertices); } else { // Different line-width per line: Need to manually loop through this mess: for (i=0; i < nrvertices/2; i++) { glLineWidth(size[i]); // Render line: glDrawArrays(GL_LINES, i * 2, 2); } } // Disable fast rendering of arrays: glDisableClientState(GL_VERTEX_ARRAY); if (usecolorvector) glDisableClientState(GL_COLOR_ARRAY); // Restore old matrix from backup copy, undoing the global translation: glPopMatrix(); // Turn off anti-aliasing: if(smooth) glDisable(GL_LINE_SMOOTH); // Reset line width to 1.0: glLineWidth(1); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); //All psychfunctions require this. return(PsychError_none); }
PsychError PSYCHHIDKbTriggerWait(void) { pRecDevice deviceRecord; int i, deviceIndex, numDeviceIndices; long KeysUsagePage=0x07; // This is the keyboard usage page long KeysUsage; // This will contain the key code of the trigger key long KbDeviceUsagePages[NUMDEVICEUSAGES]= {0x01,0x01}, KbDeviceUsages[NUMDEVICEUSAGES]={0x06,0x07}; // Generic Desktop page (0x01), keyboard (0x06), keypad (0x07) int numDeviceUsages=NUMDEVICEUSAGES; int deviceIndices[PSYCH_HID_MAX_KEYBOARD_DEVICES]; pRecDevice deviceRecords[PSYCH_HID_MAX_KEYBOARD_DEVICES]; psych_bool isDeviceSpecified, foundUserSpecifiedDevice; double *timeValueOutput; IOHIDQueueInterface **queue; HRESULT result; IOHIDDeviceInterface122** interface=NULL; // This requires Mac OS X 10.3 or higher IOReturn success; IOHIDElementCookie triggerCookie; PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(2)); //Specify trigger key code and the deviceNumber of the keyboard or keypad to scan. PsychHIDVerifyInit(); if(hidDataRef!=NULL) PsychErrorExitMsg(PsychError_user, "A queue is already running, you must call KbQueueRelease() before invoking KbTriggerWait."); //Identify the trigger { int KeysUsageInteger; if(!PsychCopyInIntegerArg(1, TRUE, &KeysUsageInteger)){ PsychErrorExitMsg(PsychError_user, "Keycode is required."); } KeysUsage=KeysUsageInteger; } //Choose the device index and its record PsychHIDGetDeviceListByUsages(numDeviceUsages, KbDeviceUsagePages, KbDeviceUsages, &numDeviceIndices, deviceIndices, deviceRecords); isDeviceSpecified=PsychCopyInIntegerArg(2, FALSE, &deviceIndex); if(isDeviceSpecified){ //make sure that the device number provided by the user is really a keyboard or keypad. 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. PsychAllocOutDoubleArg(1, FALSE, &timeValueOutput); if(!timeValueOutput) PsychErrorExitMsg(PsychError_system, "Failed to allocate memory for output."); interface=deviceRecord->interface; if(!interface) PsychErrorExitMsg(PsychError_system, "Could not get interface to device."); // The following bracketed clause will get a cookie corresponding to the // trigger. If multiple keys were of interest, the code could be modified // trivially to iterate over an array of KeysUsage to generate an array of // corresponding cookies { CFArrayRef elements; psych_bool usedDictionary=FALSE; { CFDictionaryRef dict=NULL; // The next few lines define a dictionary describing the key of interest // If they are omitted, the code will still work, but all elements will match // initially rather than just the one key of interest, so the code will be // slower since it will iterate through hundreds of keys CFStringRef keys[2] = {CFSTR("UsagePage"), CFSTR("Usage")}; CFNumberRef values[2]; values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &KeysUsagePage); values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &KeysUsage); if(values[0]!=NULL && values[1]!=NULL){ // Even if they are NULL, it will be ok since dict can be NULL at the expense of some loss of efficiency dict = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, 2, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } // copyMatchinfElements requires IOHIDDeviceInterface122, thus Mac OS X 10.3 or higher // elements would have to be obtained directly from IORegistry for 10.2 or earlier // If dict==NULL, all elements will match, leading to some inefficiency success = (*interface)->copyMatchingElements(interface, dict, &elements); if(dict){ usedDictionary=TRUE; CFRelease(dict); } if(values[0]) CFRelease(values[0]); if(values[1]) CFRelease(values[1]); if(!elements){ PsychErrorExitMsg(PsychError_user, "Specified key code not found on device."); } } { // elements will only contain one element in this implementation, but has the // advantage of generalizing to future derived implementations that listen // for multiple keys CFIndex i; for (i=0; i<CFArrayGetCount(elements); i++) { long number; CFDictionaryRef element= CFArrayGetValueAtIndex(elements, i); CFTypeRef object; if(!element) continue; if(!usedDictionary){ // Verify tht we are dealing with a keypad or keyboard object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementUsageKey)); if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue; if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,&number)) continue; if(number!=KeysUsage) continue; // See if element corresponds to the desired key object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementUsagePageKey)); if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue; if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number)) continue; if(number!=KeysUsagePage) continue; } // Get the cookie for this element object= (CFDictionaryGetValue(element, CFSTR(kIOHIDElementCookieKey))); if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue; if(!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number)) continue; triggerCookie = (IOHIDElementCookie) number; break; } if(CFArrayGetCount(elements)==i){ CFRelease(elements); PsychErrorExitMsg(PsychError_user, "Specified key code not found on device."); } CFRelease(elements); } } // Allocate for the queue queue=(*interface)->allocQueue(interface); if(!queue){ PsychErrorExitMsg(PsychError_system, "Failed to allocate event queue for detecting key press."); } // Create the queue result = (*queue)->create(queue, 0, 8); // 8 events can be stored before the earliest event is lost if (kIOReturnSuccess != result){ (*queue)->Release(queue); (*queue)=NULL; PsychErrorExitMsg(PsychError_system, "Failed to create event queue for detecting key press."); } // Add the trigger to the queue // If multiple keys were of interest, their cookies could be added in turn result = (*queue)->addElement(queue, triggerCookie, 0); if (kIOReturnSuccess != result){ result = (*queue)->dispose(queue); (*queue)->Release(queue); (*queue)=NULL; PsychErrorExitMsg(PsychError_system, "Failed to add trigger key to event queues."); } // Start the queue result = (*queue)->start(queue); if (kIOReturnSuccess != result){ result = (*queue)->dispose(queue); (*queue)->Release(queue); (*queue)=NULL; PsychErrorExitMsg(PsychError_system, "Failed to start event queues."); } // Watch for the trigger { IOHIDEventStruct event; while(1){ AbsoluteTime zeroTime = {0,0}; result = (*queue)->getNextEvent(queue, &event, zeroTime, 0); if(kIOReturnSuccess==result) break; PsychWaitIntervalSeconds((double)0.004); //surrender some time to other processes // If it were of interest to trigger selectively on key press or key release, // this could be evaluated by checking event.value (zero versus non-zero) // but this would put more code inside the loop // If multiple keys are registered via addElement (not the case here), the // cookie for the key responsible for the event can be obtained from // event.elementCookie } // If event.longValue is not NULL, the documentation indicates that it is up // to the caller to deallocate it. The circumstances under which a non-NULL // value would be generated are not specified. My guess is that some devices // might return a 64-bit value (e.g., a tracking device coordinate). // Keys, having only two states, shouldn't need this, but check and free to // be safe if ((event.longValueSize != 0) && (event.longValue != NULL)) free(event.longValue); // Set the time, using the same strategy as PsychTimeGlue's PsychGetPrecisionTimerSeconds // For code maintainability, it would be better if this conversion were performed // by a function in PsychTimeGlue { Nanoseconds timeNanoseconds=AbsoluteToNanoseconds(event.timestamp); UInt64 timeUInt64=UnsignedWideToUInt64(timeNanoseconds); double timeDouble=(double)timeUInt64; *timeValueOutput=timeDouble / 1000000000; } } // Clean up result = (*queue)->stop(queue); // Code from Apple sometimes removes elements from queue before disposing and sometimes does not // I can't see any reason to do so for a queue that's one line of code away from destruction // and therefore haven't result = (*queue)->dispose(queue); (*queue)->Release(queue); (*queue)=NULL; // Just in case queue is redefined as static in the future // PsychGetPrecisionTimerSeconds(timeValueOutput); // Less precise strategy than using event.timestamp return(PsychError_none); }
PsychError PsychModuleInit(void) { //register the project exit function PsychErrorExit(PsychRegisterExit(&PsychPortAudioExit)); // Register the project function which is called when the module // is invoked with no arguments: PsychErrorExit(PsychRegister(NULL, &PSYCHPORTAUDIODisplaySynopsis)); // Report the version #if PSYCH_SYSTEM == PSYCH_OSX PsychErrorExit(PsychRegister("Version", &MODULEVersion)); #endif // Register the module name PsychErrorExit(PsychRegister("PsychPortAudio", NULL)); // Register synopsis and named subfunctions. PsychErrorExit(PsychRegister("Verbosity", &PSYCHPORTAUDIOVerbosity)); PsychErrorExit(PsychRegister("Open", &PSYCHPORTAUDIOOpen)); PsychErrorExit(PsychRegister("Close", &PSYCHPORTAUDIOClose)); PsychErrorExit(PsychRegister("Start", &PSYCHPORTAUDIOStartAudioDevice)); PsychErrorExit(PsychRegister("RescheduleStart", &PSYCHPORTAUDIORescheduleStart)); PsychErrorExit(PsychRegister("Stop", &PSYCHPORTAUDIOStopAudioDevice)); PsychErrorExit(PsychRegister("FillBuffer", &PSYCHPORTAUDIOFillAudioBuffer)); PsychErrorExit(PsychRegister("RefillBuffer", &PSYCHPORTAUDIORefillBuffer)); PsychErrorExit(PsychRegister("GetDevices", &PSYCHPORTAUDIOGetDevices)); PsychErrorExit(PsychRegister("GetStatus", &PSYCHPORTAUDIOGetStatus)); PsychErrorExit(PsychRegister("LatencyBias", &PSYCHPORTAUDIOLatencyBias)); PsychErrorExit(PsychRegister("GetAudioData", &PSYCHPORTAUDIOGetAudioData)); PsychErrorExit(PsychRegister("RunMode", &PSYCHPORTAUDIORunMode)); PsychErrorExit(PsychRegister("SetLoop", &PSYCHPORTAUDIOSetLoop)); PsychErrorExit(PsychRegister("EngineTunables", &PSYCHPORTAUDIOEngineTunables)); PsychErrorExit(PsychRegister("GetOpenDeviceCount", &PSYCHPORTAUDIOGetOpenDeviceCount)); PsychErrorExit(PsychRegister("UseSchedule", &PSYCHPORTAUDIOUseSchedule)); PsychErrorExit(PsychRegister("AddToSchedule", &PSYCHPORTAUDIOAddToSchedule)); // Setup synopsis help strings: InitializeSynopsis(); //Scripting glue won't require this if the function takes no arguments. // Setup module author: PsychSetModuleAuthorByInitials("mk"); // Call wait-routine for 0.1 secs: This to initialize the time glue on MS-Windows, // so the first call to a timing function won't delay: PsychWaitIntervalSeconds(0.1); // Startup finished. return(PsychError_none); }
PsychError PSYCHHIDReceiveReports(void) { long error=0; int deviceIndex; mxArray **mxErrPtr; const mxArray *mxOptions,*mx; PsychPushHelp(useString,synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(2)); PsychCopyInIntegerArg(1,TRUE,&deviceIndex); /* "\"options.print\" =1 (default 0) enables diagnostic printing of a summary of each report when our callback routine receives it. " "\"options.printCrashers\" =1 (default 0) enables diagnostic printing of the creation of the callback source and its addition to the CFRunLoop. " "\"options.maxReports\" (default 10000) allocate space for at least this many reports, shared among all devices. " "\"options.maxReportSize\" (default 64) allocate this many bytes per report. " */ //optionsPrintReportSummary=0; // options.print //optionsPrintCrashers=0; // options.printCrashers //optionsMaxReports=10000; // options.maxReports //optionsMaxReportSize=64; // options.maxReportSize //optionsSecs=0.010; // options.secs mxOptions=PsychGetInArgMxPtr(2); if(mxOptions!=NULL){ mx=mxGetField(mxOptions,0,"print"); if(mx!=NULL)optionsPrintReportSummary=(psych_bool)mxGetScalar(mx); mx=mxGetField(mxOptions,0,"printCrashers"); if(mx!=NULL)optionsPrintCrashers=(psych_bool)mxGetScalar(mx); mx=mxGetField(mxOptions,0,"maxReports"); if(mx!=NULL)optionsMaxReports=(int)mxGetScalar(mx); mx=mxGetField(mxOptions,0,"maxReportSize"); if(mx!=NULL)optionsMaxReportSize=(int)mxGetScalar(mx); mx=mxGetField(mxOptions,0,"secs"); if(mx!=NULL)optionsSecs=mxGetScalar(mx); mx=mxGetField(mxOptions,0,"consistencyChecks"); if(mx!=NULL)optionsConsistencyChecks=(psych_bool)mxGetScalar(mx); } if(optionsMaxReports>MAXREPORTS)printf("PsychHID ReceiveReports: Sorry, maxReports is fixed at %d.\n",(int)MAXREPORTS); if(optionsMaxReportSize>MAXREPORTSIZE)printf("PsychHID ReceiveReports: Sorry, maxReportSize is fixed at %d.\n",(int)MAXREPORTSIZE); error=ReceiveReports(deviceIndex); mxErrPtr=PsychGetOutArgMxPtr(1); if(mxErrPtr!=NULL){ const char *fieldNames[]={"n", "name", "description"}; char *name="",*description=""; mxArray *fieldValue; PsychHIDErrors(NULL, error,&name,&description); // Get error name and description, if available. *mxErrPtr=mxCreateStructMatrix(1,1,3,fieldNames); fieldValue=mxCreateString(name); if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\"."); mxSetField(*mxErrPtr,0,"name",fieldValue); fieldValue=mxCreateString(description); if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\"."); mxSetField(*mxErrPtr,0,"description",fieldValue); fieldValue=mxCreateDoubleMatrix(1,1,mxREAL); if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\"."); *mxGetPr(fieldValue)=(double)error; mxSetField(*mxErrPtr,0,"n",fieldValue); } return(PsychError_none); }
PsychError SCREENPreloadTextures(void) { PsychWindowRecordType *windowRecord, *texwin; psych_bool isArgThere; int *texhandles; PsychWindowRecordType **windowRecordArray; int i, n, numWindows, myhandle; double *success; psych_bool* residency; GLuint* texids; GLboolean* texresident; psych_bool failed = false; GLclampf maxprio = 1.0f; GLenum target; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(2)); //The maximum number of inputs PsychErrorExit(PsychRequireNumInputArgs(1)); //The minimum number of inputs PsychErrorExit(PsychCapNumOutputArgs(2)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord); // Get optional texids vector: isArgThere = PsychIsArgPresent(PsychArgIn, 2); PsychAllocInIntegerListArg(2, FALSE, &n, &texhandles); if (n < 1) isArgThere=FALSE; // Enable this windowRecords framebuffer as current drawingtarget: PsychSetDrawingTarget(windowRecord); // Disable shader: PsychSetShader(windowRecord, 0); glDisable(GL_TEXTURE_2D); // Fetch global texturing mode: target=PsychGetTextureTarget(windowRecord); glEnable(target); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f(0, 0, 0, 0); // Setup identity modelview matrix: glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); PsychCreateVolatileWindowRecordPointerList(&numWindows, &windowRecordArray); // Process vector of all texids for all requested textures: if (!isArgThere) { // No handles provided: In this case, we preload all textures: n=0; for(i=0; i<numWindows; i++) { if (windowRecordArray[i]->windowType==kPsychTexture) { n++; // Prioritize this texture: glPrioritizeTextures(1, (GLuint*) &(windowRecordArray[i]->textureNumber), &maxprio); // Bind this texture: glBindTexture(target, windowRecordArray[i]->textureNumber); // Render a single textured point, thereby enforcing a texture upload: glBegin(GL_QUADS); glTexCoord2f(0,0); glVertex2i(10,10); glTexCoord2f(0,1); glVertex2i(10,11); glTexCoord2f(1,1); glVertex2i(11,11); glTexCoord2f(1,0); glVertex2i(11,10); glEnd(); } } texids = (GLuint*) PsychMallocTemp(sizeof(GLuint) * n); texresident = (GLboolean*) PsychMallocTemp(sizeof(GLboolean) * n); n=0; for(i=0; i<numWindows; i++) { if (windowRecordArray[i]->windowType==kPsychTexture) { texids[n] = (GLuint) windowRecordArray[i]->textureNumber; n++; } } } else { // Vector with texture handles provided: Just preload them. texids = (GLuint*) PsychMallocTemp(sizeof(GLuint) * n); texresident = (GLboolean*) PsychMallocTemp(sizeof(GLboolean) * n); myhandle=0; for (i=0; i<n; i++) { myhandle = texhandles[i]; texwin = NULL; if (IsWindowIndex(myhandle)) FindWindowRecord(myhandle, &texwin); if (texwin && texwin->windowType==kPsychTexture) { // Prioritize this texture: glPrioritizeTextures(1, (GLuint*) &(texwin->textureNumber), &maxprio); // Bind this texture: glBindTexture(target, texwin->textureNumber); // Render a single textured point, thereby enforcing a texture upload: glBegin(GL_QUADS); glTexCoord2f(0,0); glVertex2i(10,10); glTexCoord2f(0,1); glVertex2i(10,11); glTexCoord2f(1,1); glVertex2i(11,11); glTexCoord2f(1,0); glVertex2i(11,10); glEnd(); texids[i] = (GLuint) texwin->textureNumber; } else { // This handle is invalid or at least no texture handle: printf("PTB-ERROR! Screen('PreloadTextures'): Entry %i of texture handle vector (handle %i) is not a texture handle!\n", i, myhandle); failed = true; } } } // Restore old matrix from backup copy, undoing the global translation: glPopMatrix(); // Disable texture engine: glDisable(GL_TEXTURE_2D); glDisable(target); // Wait for prefetch completion: glFinish(); // We don't need these anymore: PsychDestroyVolatileWindowRecordPointerList(windowRecordArray); if (failed) { PsychErrorExitMsg(PsychError_user, "At least one texture handle in texids-vector was invalid! Aborted."); } // Query residency state of all preloaded textures: success = NULL; PsychAllocOutDoubleArg(1, FALSE, &success); *success = (double) glAreTexturesResident(n, texids, texresident); // Sync pipe again, just to be safe... glFinish(); // Count them and copy them into output vector: PsychAllocOutBooleanMatArg(2, FALSE, n, 1, 1, &residency); for (i=0; i<n; i++) { residency[i] = (psych_bool) ((*success) ? TRUE : texresident[i]); } PsychTestForGLErrors(); // Done. Our PsychMallocTemp'ed arrays will be auto-released... return(PsychError_none); }
PsychError SCREENEndOpenGL(void) { static char useString[] = "Screen('EndOpenGL', windowPtr);"; static char synopsisString[] = "Finish OpenGL rendering by external OpenGL code, prepare 2D drawing into window 'windowPtr'.\n" "This is the counterpart to Screen('BeginOpenGL'). Whenever you used Screen('BeginOpenGL') to enable " "external OpenGL drawing from Matlab, you *must* call Screen('EndOpenGL') when you're finished with a " "window, either because you want to draw into a different window, or you want to use a Screen command. " "Psychtoolbox will abort your script if you omit this command. "; static char seeAlsoString[] = "BeginOpenGL SetOpenGLTexture GetOpenGLTexture moglcore"; PsychWindowRecordType *windowRecord; GLenum error; GLint fboid; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(1)); // The maximum number of inputs PsychErrorExit(PsychRequireNumInputArgs(1)); // Number of required inputs. PsychErrorExit(PsychCapNumOutputArgs(0)); // The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); // In userspace mode? if (!PsychIsUserspaceRendering()) PsychErrorExitMsg(PsychError_user, "Tried to call Screen('EndOpenGL'), but userspace rendering is already disabled! Missing or mismatched Screen('BeginOpenGL')? Check your code."); // Check for OpenGL errors in external code: if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync) && ((error=glGetError())!=GL_NO_ERROR)) { printf("PTB-ERROR: Some of your external OpenGL code executed between last invocation of Screen('BeginOpenGL') and\n"); printf("PTB-ERROR: Screen('EndOpenGL') produced an OpenGL error condition. Please check your code. The reported GL\n"); printf("PTB-ERROR: error was: %s\n\n", (const char*) gluErrorString(error)); // Reset further error state for this context: while (glGetError()!=GL_NO_ERROR); // Abort script: PsychErrorExitMsg(PsychError_user, "Failure in external OpenGL code."); } // Reset userspace rendering flag: PsychSetUserspaceGLFlag(FALSE); // Switch to our windows own OpenGL context and enable it as drawingtarget: // MK: Note to self at 31. July 2012, after thinking about this for > 1 hour: // The fboid and preswitchWindowRecord code looks redundant and as if it could // get replaced by a simple PsychSetDrawingTarget(NULL) call in 'BeginOpenGL', to // let the PsychSetDrawingTarget(windowRecord); call below do all the work, but // *this is not the case* !!! Do not touch it! It is good as it is. // // Reason: The intermediate switching Voodoo is needed if PTB is used with the // imaging pipeline (partially) disabled for at least one of the participating // windows. We need the global currentRenderTarget to stay what it is aka // preswitchWindowRecord, and not turn to NULL while 3D rendering, because // we must not trigger the transition logic from NULL to windowRecord in the call to // PsychSetDrawingTarget(windowRecord); -- This would trigger a restore operation of // the framebuffer as it was pre-BeginOpenGL from the shadow framebuffer backup textures, // thereby undoing all the rendering work between BeginOpenGL and EndOpenGL. // // The only way we could get safely rid of this logic would be to always have the imaging // pipeline enabled on all windows -- to make OpenGL 2.1 with FBO extension and all other // required extensions mandatory for use of PTB. Iow., we would drop support for all GPU's // older than about OpenGL-3 / Direct3D-10 and would accept a significant memory overhead // for using all the FBO backing and FBO blitting even in use cases where enabling the // full pipeline has no benefit whatsoever. This would be a bad tradeoff, saving a fraction // of a millisecond here (potentially) while wasting dozens of MB VRAM+RAM and adding 1 // millisecond overhead and increased hardware spec requirements to any PTB script. // // Ok, we *can* skip it if sharecontext flag has been set to 1 to (ab)use the Screen() 2D internal // OpenGL rendering context for 3D userspace rendering. In that case no actual context switching // or change of framebuffer FBO bindings or any kind of framebuffer shadow backup-restore happens, // so the whole 'BeginOpenGL' -> 'EndOpenGL' call sequence is mostly a no-op. The only thing done // is setting the 2D drawingtarget at 'Begin/EndOpenGL' time if needed, to get the buffer bindings // and viewports / scissors / matrices et al. correct, setting of the userspace flag, and some // glGetError() error checking if not disabled by usercode. if (sharecontext == 0 || sharecontext == 2) { // Current state: Userspace context bound, possibly with a FBO binding active. // Internal drawingtarget is still set properly in our internal inactive context, // but possible FBO bindings are not set. // // Wanted intermediate state: Unbind FBO in userspace context, unbind userspace context. // Bind internal context, preserve its drawingtarget, but restore possible FBO // bindings for that drawingtarget. // Query current FBO binding in userspace context. We need to manually transfer this back to the PTB context, so // it can render into our window: if (glBindFramebufferEXT) { fboid = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fboid); } // Bind OpenGL context of pre-userspacerendering-switch-state: // This implicitely flushes the old context and unbinds any FBO's if neccessary // before the transition: PsychSetGLContext(preswitchWindowRecord); // Rebind possible old FBOs: if (glBindFramebufferEXT && (fboid > 0)) { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboid); } // Reset error state for our internal context: if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) while (glGetError()!=GL_NO_ERROR); } // Current intermediate state: Preswitch state restored, ie. the OpenGL context and // FBO bindings (and as part of context state all viewport/matrix/scissor setups etc.) // are set as it was before 'BeginOpenGL' was called. The drawingtarget is also the // same. // Avoid a no-op call to PsychSetDrawingTarget() as it would still perform a redundant no-op context-switch // causing a bit of overhead: if (windowRecord != preswitchWindowRecord) { // This call binds our internal OpenGL context for the requested 'windowRecord' and sets up the windowRecord for drawing: // It's a no-op if preswitch windowRecord == the requested windowRecord. Otherwise a standard context switch and drawing // target switch will occur: PsychSetDrawingTarget(windowRecord); // Reset error state for our internal context: if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) while (glGetError()!=GL_NO_ERROR); } // Reset preswitch record and state: preswitchWindowRecord = NULL; sharecontext = 0; // Ready for internal rendering: return(PsychError_none); }