/*
    PsychReadNormalizedGammaTable()
    
    TO DO: This should probably be changed so that the caller allocates the memory.
    TO DO: Adopt a naming convention which distinguishes between functions which allocate memory for return variables from those which do not.
            For example, PsychReadNormalizedGammaTable() vs. PsychGetNormalizedGammaTable().
    
*/
void PsychReadNormalizedGammaTable(int screenNumber, int *numEntries, float **redTable, float **greenTable, float **blueTable)
{
  CGDirectDisplayID	cgDisplayID;
  static  float localRed[256], localGreen[256], localBlue[256];
  
  // The X-Windows hardware LUT has 3 tables for R,G,B, 256 slots each.
  // Each entry is a 16-bit word with the n most significant bits used for an n-bit DAC.
  psych_uint16	RTable[256];
  psych_uint16	GTable[256];
  psych_uint16	BTable[256];
  int     i;        

  // Query OS for gamma table:
  PsychGetCGDisplayIDFromScreenNumber(&cgDisplayID, screenNumber);
  XF86VidModeGetGammaRamp(cgDisplayID, PsychGetXScreenIdForScreen(screenNumber), 256, (unsigned short*) RTable, (unsigned short*) GTable, (unsigned short*) BTable);

  // Convert tables:Map 16-bit values into 0-1 normalized floats:
  *redTable=localRed; *greenTable=localGreen; *blueTable=localBlue;
  for (i=0; i<256; i++) localRed[i]   = ((float) RTable[i]) / 65535.0f;
  for (i=0; i<256; i++) localGreen[i] = ((float) GTable[i]) / 65535.0f;
  for (i=0; i<256; i++) localBlue[i]  = ((float) BTable[i]) / 65535.0f;

  // The LUT's always have 256 slots for the 8-bit framebuffer:
  *numEntries= 256;
  return;
}
Esempio n. 2
0
PsychError ScreenExitFunction(void)
{
  CGDirectDisplayID dpy, last_dpy;
  int i;

	//The timing array holds time values set by Screen internal diagnostics.  It allocates memory with 
	//malloc to hold the array of times.  This call frees the memory prior to unloading Screen
	ClearTimingArray();
  
	// Close all open onscreen windows and release their resources,
	// -> Perform exactly the same cleanup that Screen('CloseAll') would do.
	ScreenCloseAllWindows();
	CloseWindowBank();

	#if PSYCH_SYSTEM == PSYCH_LINUX
	// Linux specific hack. Close display connection(s) to X-Server(s). This is a bit unclean.
	last_dpy = NULL;
	// Go trough full screen list:
	for (i=0; i < PsychGetNumDisplays(); i++) {
	  // Get display-ptr for this screen:
	  PsychGetCGDisplayIDFromScreenNumber(&dpy, i);
	  // Did we close this connection already (dpy==last_dpy)?
	  if (dpy != last_dpy) {
	    // Nope. Keep track of it...
	    last_dpy=dpy;
	    // ...and close display connection to X-Server:
	    XCloseDisplay(dpy);
	  }	  
	}

	// All connections should be closed now. We can't NULL-out the display list, but
	// Matlab will flush the Screen - Mexfile anyway...

	#endif

	return(PsychError_none);
}
void PsychLoadNormalizedGammaTable(int screenNumber, int numEntries, float *redTable, float *greenTable, float *blueTable)
{
  CGDirectDisplayID	cgDisplayID;
  int     i;        
  psych_uint16	RTable[256];
  psych_uint16	GTable[256];
  psych_uint16	BTable[256];

  // Table must have 256 slots!
  if (numEntries!=256) PsychErrorExitMsg(PsychError_user, "Loadable hardware gamma tables must have 256 slots!");    
  
  // The X-Windows hardware LUT has 3 tables for R,G,B, 256 slots each.
  // Each entry is a 16-bit word with the n most significant bits used for an n-bit DAC.

  // Convert input table to X11 specific gammaTable:
  for (i=0; i<256; i++) RTable[i] = (int)(redTable[i]   * 65535.0f + 0.5f);
  for (i=0; i<256; i++) GTable[i] = (int)(greenTable[i] * 65535.0f + 0.5f);
  for (i=0; i<256; i++) BTable[i] = (int)(blueTable[i]  * 65535.0f + 0.5f);
  
  // Set new gammaTable:
  PsychGetCGDisplayIDFromScreenNumber(&cgDisplayID, screenNumber);
  XF86VidModeSetGammaRamp(cgDisplayID, PsychGetXScreenIdForScreen(screenNumber), 256, (unsigned short*) RTable, (unsigned short*) GTable, (unsigned short*) BTable);
  return;
}
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 &timestamp is just a dummy assignment without any meaning.
		PsychCopyOutDoubleMatArg(5, kPsychArgOptional, (int) 1, (int) 0, (int) 1, &timestamp);
		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(&timestamp);

			// 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(&timestamp);

	      // 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 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);
}
/*  PsychWaitForBufferswapPendingOrFinished()
 *  Waits until a bufferswap for window windowRecord has either already happened or
 *  bufferswap is certain.
 *  Input values:
 *  windowRecord struct of onscreen window to monitor.
 *  timestamp    = Deadline for abortion of flip detection at input.
 *
 *  Return values:
 *  timestamp    = System time at polling loop exit.
 *  beamposition = Beamposition at polling loop exit.
 *
 *  Return value: FALSE if swap happened already, TRUE if swap is imminent.
 */
bool PsychWaitForBufferswapPendingOrFinished(PsychWindowRecordType* windowRecord, double* timestamp, int *beamposition)
{
#if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX
    CGDirectDisplayID displayID;
	unsigned int primarySurface, secondarySurface;
	unsigned int updateStatus;
	double deadline = *timestamp;

	// If we are called, we know that 'windowRecord' is an onscreen window.
	int screenId = windowRecord->screenNumber;

    // Retrieve display id and screen size spec that is needed later...
    PsychGetCGDisplayIDFromScreenNumber(&displayID, screenId);

#define RADEON_D1GRPH_UPDATE	0x6144
#define RADEON_D2GRPH_UPDATE	0x6944
#define RADEON_SURFACE_UPDATE_PENDING 4
#define RADEON_SURFACE_UPDATE_TAKEN   8

	// Just need to check if GPU low-level access is supported:
	if (!PsychOSIsKernelDriverAvailable(screenId)) return;
	
	// Driver is online. Enter polling loop:
	while (TRUE) {
		// Read surface address registers:
		primarySurface   = PsychOSKDReadRegister(screenId, (screenId <=0 ) ? RADEON_D1GRPH_PRIMARY_SURFACE_ADDRESS : RADEON_D2GRPH_PRIMARY_SURFACE_ADDRESS, NULL);
		secondarySurface = PsychOSKDReadRegister(screenId, (screenId <=0 ) ? RADEON_D1GRPH_SECONDARY_SURFACE_ADDRESS : RADEON_D2GRPH_SECONDARY_SURFACE_ADDRESS, NULL);

		// Read update status registers:
		updateStatus     = PsychOSKDReadRegister(screenId, (screenId <=0 ) ? RADEON_D1GRPH_UPDATE : RADEON_D2GRPH_UPDATE, NULL);

		PsychGetAdjustedPrecisionTimerSeconds(timestamp);

		if (primarySurface!=windowRecord->gpu_preflip_Surfaces[0] || secondarySurface!=windowRecord->gpu_preflip_Surfaces[1] || (updateStatus & (RADEON_SURFACE_UPDATE_PENDING | RADEON_SURFACE_UPDATE_TAKEN)) || (*timestamp > deadline)) {
			// Abort condition: Exit loop.
			break;
		}
		
		if (PsychPrefStateGet_Verbosity() > 9) {
			printf("PTB-DEBUG: Head %i: primarySurface=%p : secondarySurface=%p : updateStatus=%i\n", ((screenId <=0) ? 0:1), primarySurface, secondarySurface, updateStatus);
		}

		// Sleep 200 microseconds, then retry:
		PsychWaitIntervalSeconds(0.0002);
	};
	
	// Take timestamp and beamposition:
	*beamposition = PsychGetDisplayBeamPosition(&displayID, screenId);
	PsychGetAdjustedPrecisionTimerSeconds(timestamp);

	// Exit due to timeout?
	if (*timestamp > deadline) {
		// Mark timestamp as invalid due to timeout:
		*timestamp = -1;
	}
	
	// Return FALSE if bufferswap happened already, TRUE if swap is still pending:
	return((updateStatus & RADEON_SURFACE_UPDATE_PENDING) ? TRUE : FALSE);
#else
	// On Windows, we always return "swap happened":
	return(FALSE);
#endif
}
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 SCREENShowCursorHelper(void)
{
    // If you change the useString then also change the corresponding synopsis string in ScreenSynopsis.c
    static char useString[] = "Screen('ShowCursorHelper', screenIndex [, cursorshapeid][, mouseIndex]);";
    //                                                    1              2                3
    static char synopsisString[] =
    "This is a helper function called by ShowCursor.  Do not call Screen(\'ShowCursorHelper\'), use "
    "ShowCursor instead.\n"
    "Show the mouse pointer. If optional 'cursorshapeid' is given, select a specific cursor shape as well.\n"
    "If optional 'mouseIndex' is given, setup cursor for given master mouseIndex device (Linux only).\n";
    static char seeAlsoString[] = "HideCursorHelper";

    int	screenNumber, cursorid, mouseIdx;
    char *cursorName = NULL;

#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;
    if (!PsychCopyInIntegerArg(2, kPsychArgAnything, &cursorid)) {
        if (PsychAllocInCharArg(2, kPsychArgAnything, &cursorName)) cursorid = -2;
    }

    // Cursor change request?
    if (cursorid!=-1) {
        // Yes.
#if PSYCH_SYSTEM == PSYCH_OSX
        // OS/X:
        PsychCocoaSetThemeCursor(cursorid);
#endif

#if PSYCH_SYSTEM == PSYCH_LINUX

        #ifdef PTB_USE_WAYLAND
            // GNU/Linux with Wayland backend:

            // Map X11 id's to Wayland equivalents:
            switch (cursorid) {
                case 26: // SandClock
                    cursorName = "watch";
                break;

                case 58: // Hand
                    cursorName = "hand1";
                    break;

                case 34: // CrossHair
                    cursorName = "crosshair";
                    break;

                case 1: // IBeam
                    cursorName = "xterm";
                    break;

                case 0: // Arrow
                    cursorName = "left_ptr";
                    break;

                case 2: // Arrow
                    cursorName = "left_ptr";
                    break;

                case 5: // Arrow
                    cursorName = "left_ptr";
                    break;

                case -2: // Special case: Use provided cursorName string, if any:
                    if (cursorName) break;

                default:
                    cursorName = "left_ptr";
            }

            PsychOSDefineWaylandCursor(screenNumber, mouseIdx, cursorName);
        #else
            // 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);
        #endif
        // 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;

            case 8:
                // IBeam/text cursor:
                lpCursorName = IDC_IBEAM;
                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 SCREENReadNormalizedGammaTable(void)
{
    int		i, screenNumber, numEntries, reallutsize, physicalDisplay, outputId;
    float 	*redTable, *greenTable, *blueTable;
    double	*gammaTable;	

    //all subfunctions should have these two lines
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

    PsychErrorExit(PsychCapNumOutputArgs(3));
    PsychErrorExit(PsychCapNumInputArgs(2));

    // Get optional physicalDisplay argument - It defaults to zero:
    physicalDisplay = -1;
    PsychCopyInIntegerArg(2, FALSE, &physicalDisplay);

    // Read in the screen number:
    // On OS/X we also accept screen indices for physical displays (as opposed to active dispays).
    // This only makes a difference in mirror-mode, where there is only 1 active display, but that
    // corresponds to two physical displays which can have different gamma setting requirements:
    if ((PSYCH_SYSTEM == PSYCH_OSX) && (physicalDisplay > 0)) {
        PsychCopyInIntegerArg(1, TRUE, &screenNumber);
	if (screenNumber < 1) PsychErrorExitMsg(PsychError_user, "A 'screenNumber' that is smaller than one provided, although 'physicalDisplay' flag set. This is not allowed!");

	// Invert screenNumber as a sign its a physical display, not an active display:
	screenNumber = -1 * screenNumber;
    }
    else {
        PsychCopyInScreenNumberArg(1, TRUE, &screenNumber);
    }

    if ((PSYCH_SYSTEM == PSYCH_LINUX) && (physicalDisplay > -1)) {
	// Affect one specific display output for given screen:
	outputId = physicalDisplay;
    }
    else {
	// Other OS'es, and Linux with default setting: Affect all outputs
	// for a screen.
	outputId = -1;
    }

    // Retrieve gamma table:
    PsychReadNormalizedGammaTable(screenNumber, outputId, &numEntries, &redTable, &greenTable, &blueTable);
	
    // Copy it out to runtime:
    PsychAllocOutDoubleMatArg(1, FALSE, numEntries, 3, 0, &gammaTable);

    for(i=0;i<numEntries;i++){
        gammaTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 0, 0)]=(double)redTable[i];
        gammaTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 1, 0)]=(double)greenTable[i];
        gammaTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 2, 0)]=(double)blueTable[i];
    }

    // Copy out optional DAC resolution value:
    PsychCopyOutDoubleArg(2, FALSE, (double) PsychGetDacBitsFromDisplay(screenNumber));
	
    // We default to the assumption that the real size of the hardware LUT is identical to
    // the size of the returned LUT:
    reallutsize = numEntries;
	
    #if PSYCH_SYSTEM == PSYCH_OSX
		// On OS-X we query the real LUT size from the OS and return that value:
		CGDirectDisplayID	displayID;
		CFMutableDictionaryRef properties;
		CFNumberRef cfGammaLength;
		SInt32 lutslotcount;
		io_service_t displayService;
		kern_return_t kr;
		CFMutableArrayRef framebufferTimings0 = 0;
		CFDataRef framebufferTimings1 = 0;
		IODetailedTimingInformationV2 *framebufferTiming = NULL;
		
		// Retrieve display handle for screen:
		PsychGetCGDisplayIDFromScreenNumber(&displayID, screenNumber);
		
		if (PsychPrefStateGet_Verbosity()>5) printf("PTB-DEBUG: Screen %i has framebuffer address %p.\n", screenNumber, CGDisplayBaseAddress(displayID));

		// Retrieve low-level IOKit service port for this display:
		displayService = CGDisplayIOServicePort(displayID);
				
		// Obtain the properties from that service
		kr = IORegistryEntryCreateCFProperties(displayService, &properties, NULL, 0);
		if((kr == kIOReturnSuccess) && ((cfGammaLength = (CFNumberRef) CFDictionaryGetValue(properties, CFSTR(kIOFBGammaCountKey)))!=NULL))
		{
			CFNumberGetValue(cfGammaLength, kCFNumberSInt32Type, &lutslotcount);
			CFRelease(properties);
			reallutsize = (int) lutslotcount;
		}
		else {
			// Failed!
			if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to query real size of video LUT for screen %i! Will return safe default of %i slots.\n", screenNumber, reallutsize);
		}	

		if (PsychPrefStateGet_Verbosity()>9) {			
			CFDictionaryRef currentMode;
			CFNumberRef n;
			int modeId;
			currentMode = CGDisplayCurrentMode(displayID);
			n=CFDictionaryGetValue(currentMode, kCGDisplayMode);
			CFNumberGetValue(n, kCFNumberIntType, &modeId);
			printf("Current mode has id %i\n\n", modeId);
			kr = IORegistryEntryCreateCFProperties(displayService, &properties, NULL, 0);
			if((kr == kIOReturnSuccess) && ((framebufferTimings0 = (CFMutableArrayRef) CFDictionaryGetValue(properties, CFSTR(kIOFBDetailedTimingsKey) ) )!=NULL))
			{
				for (i=0; i<CFArrayGetCount(framebufferTimings0); i++) {
					if ((framebufferTimings1 = CFArrayGetValueAtIndex(framebufferTimings0, i)) != NULL) {
						if ((framebufferTiming = (IODetailedTimingInformationV2*) CFDataGetBytePtr(framebufferTimings1)) != NULL) {
							printf("[%i] : VActive =  %li, VBL = %li, VSYNC = %li, VSYNCWIDTH = %li , VBORDERBOT = %li, VTOTAL = %li \n", i, framebufferTiming->verticalActive, framebufferTiming->verticalBlanking, framebufferTiming->verticalSyncOffset, framebufferTiming->verticalSyncPulseWidth, framebufferTiming->verticalBorderBottom, framebufferTiming->verticalActive + framebufferTiming->verticalBlanking);
						}
					}
				}

				CFRelease(properties);
			}
			else {
				// Failed!
				if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to query STUFF for screen %i --> %p!\n", screenNumber, properties);
			}	
		}
		


    #endif
	
    // Copy out optional real LUT size (number of slots):
    PsychCopyOutDoubleArg(3, FALSE, (double) reallutsize);

    return(PsychError_none);
}
Esempio n. 10
0
PsychError SCREENWaitBlanking(void) 
{
    PsychWindowRecordType *windowRecord;
    int waitFrames, framesWaited;
    double tvbl, ifi;
    long screenwidth, screenheight;
    int vbl_startline, beampos, lastline;
    psych_uint64 vblCount, vblRefCount;
    CGDirectDisplayID	cgDisplayID;
    GLint read_buffer, draw_buffer;
    
    // All subfunctions should have these two lines.  
    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(1));    //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);
    
    if(!PsychIsOnscreenWindow(windowRecord))
        PsychErrorExitMsg(PsychError_user, "Tried to call 'WaitBlanking' on something else than an onscreen window!");
    
    // Get the number of frames to wait:
    waitFrames = 0;
    PsychCopyInIntegerArg(2, FALSE, &waitFrames);

	// We default to wait at least one interval if no argument supplied:
    waitFrames = (waitFrames < 1) ? 1 : waitFrames;
    
    // Enable this windowRecords framebuffer as current drawingtarget:
    // This is needed to make sure that Offscreen windows work propely.
    PsychSetDrawingTarget(windowRecord);
    
    // Retrieve display handle for beamposition queries:
    PsychGetCGDisplayIDFromScreenNumber(&cgDisplayID, windowRecord->screenNumber);
    
    // Retrieve final vbl_startline, aka physical height of the display in pixels:
    PsychGetScreenSize(windowRecord->screenNumber, &screenwidth, &screenheight);
    vbl_startline = (int) screenheight;
    
    // Query duration of a monitor refresh interval: We try to use the measured interval,
	// but fallback of the nominal value reported by the operating system if necessary:
    if ((ifi = windowRecord->VideoRefreshInterval)<=0) {
        if (PsychGetNominalFramerate(windowRecord->screenNumber) > 0) {
            // Valid nominal framerate returned by OS: Calculate nominal IFI from it.
            ifi = 1.0 / ((double) PsychGetNominalFramerate(windowRecord->screenNumber));        
        }
        else {
            // No reasonable value available! We fallback to an assumed 60 Hz refresh...
            ifi = 1.0 / 60.0;
        }
    }
    
    // Query vblcount to test if this method works correctly:
    PsychOSGetVBLTimeAndCount(windowRecord, &vblRefCount);
    
    // Check if beamposition queries are supported by this OS and working properly:
    if (-1 != PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber) && windowRecord->VBL_Endline >= 0) {
        // Beamposition queries supported and fine. We can wait for VBL without bufferswap-tricks:
        // This is the OS-X way of doing things. We query the rasterbeamposition and compare it
        // to the known values for the VBL area. If we enter VBL, we take a timestamp and return -
        // or wait for the next VBL if waitFrames>0
        
        // Query current beamposition when entering WaitBlanking:
        beampos = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber);
        // Are we in VBL when entering WaitBlanking? If so, we should wait for one additional frame,
        // because by definition, WaitBlanking should always wait for at least one monitor refresh
        // interval...
        if ((beampos<=windowRecord->VBL_Endline) && (beampos>=vbl_startline)) waitFrames++;
        
        while(waitFrames > 0) {
            // Enough time for a sleep? If the beam is far away from VBL area, we try to sleep to
            // yield some CPU time to other processes in the system -- we are nice citizens ;)
            beampos = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber);
            while (( ((float)(vbl_startline - beampos)) / (float) windowRecord->VBL_Endline * ifi) > 0.003) {
                // At least 3 milliseconds left until retrace. We sleep for 1 millisecond.
                PsychWaitIntervalSeconds(0.001);
                beampos = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber);
            }
            
            // Less than 3 ms away from retrace. Busy-Wait for retrace...
			lastline = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber);
			beampos = lastline;
			while ((beampos < vbl_startline) && (beampos >= lastline)) {
				lastline = beampos;
				beampos = (long) PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber);
			} 
			
            // Retrace! Take system timestamp of VBL onset:
            PsychGetAdjustedPrecisionTimerSeconds(&tvbl);
            
            // If this wasn't the last frame to wait, we need to wait for end of retrace before
            // repeating the loop, because otherwise we would detect the same VBL and skip frames.
            // If it is the last frame, we skip it and return as quickly as possible to save the
            // Matlab script some extra Millisecond for drawing...
            if (waitFrames>1) { 
                beampos = vbl_startline;
                while ((beampos<=windowRecord->VBL_Endline) && (beampos>=vbl_startline)) { beampos = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber); };
            }
            
            // Done with this refresh interval...
            // Decrement remaining number of frames to wait:
            waitFrames--;
        }
    }
    else if (vblRefCount > 0) {
        // Display beamposition queries unsupported, but vblank count queries seem to work. Try those.
        // Should work on Linux and OS/X:
        while(waitFrames > 0) {
            vblCount = vblRefCount;

            // Wait for next vblank counter increment - aka start of next frame (its vblank):
            while (vblCount == vblRefCount) {
                // Requery:
                PsychOSGetVBLTimeAndCount(windowRecord, &vblCount);
                // Yield at least 100 usecs. This is accurate as this code-path
                // only executes on OS/X and Linux, never on Windows (as of 01/06/2011):
                PsychYieldIntervalSeconds(0.000100);
            }

            vblRefCount = vblCount;
            
            // Done with this refresh interval...
            // Decrement remaining number of frames to wait:
            waitFrames--;
        }
    }
    else {            
        // Other methods unsupported. We use the doublebuffer swap method of waiting for retrace.
        //
        // Working principle: On each frame, we first copy the content of the (user visible) frontbuffer into the backbuffer.
        // Then we ask the OS to perform a front-backbuffer swap on next vertical retrace and go to sleep via glFinish() et al.
        // until the OS signals swap-completion. This way PTB's/Matlabs execution will stall until VBL, when swap happens and
        // we get woken up. We repeat this procedure 'waitFrames' times, then we take a high precision timestamp and exit the
        // Waitblanking loop. As backbuffer and frontbuffer are identical (due to the copy) at swap time, the visual display
        // won't change at all for the subject.
        // This method should work reliably, but it has one drawback: A random wakeup delay (scheduling jitter) is added after
        // retrace has been entered, so Waitblanking returns only after the beam has left retrace state on older hardware.
        // This means a bit less time (1 ms?) for stimulus drawing on Windows than on OS-X where Waitblanking returns faster. 
        
        // Child protection:
        if (windowRecord->windowType != kPsychDoubleBufferOnscreen) {
            PsychErrorExitMsg(PsychError_internal, "WaitBlanking tried to perform swap-waiting on a single buffered window!");
        }
        
        // Setup buffers for front->back copy op:
        
        // Backup old read- writebuffer assignments:
        glGetIntegerv(GL_READ_BUFFER, &read_buffer);
        glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
        
        // Set read- and writebuffer properly:
        glReadBuffer(GL_FRONT);
        glDrawBuffer(GL_BACK);
        
        // Reset viewport to full-screen default:
        glViewport(0, 0, screenwidth, screenheight);                
        glScissor(0, 0, screenwidth, screenheight);                
        
        // Reset color buffer writemask to "All enabled":
        glColorMask(TRUE, TRUE, TRUE, TRUE);
        glDisable(GL_BLEND);
        glPixelZoom(1,1);

		// Disable draw shader:
		PsychSetShader(windowRecord, 0);

        // Swap-Waiting loop for 'waitFrames' frames:
        while(waitFrames > 0) {
            // Copy current content of front buffer into backbuffer:
            glRasterPos2i(0, screenheight);
            glCopyPixels(0, 0, screenwidth, screenheight, GL_COLOR);            
            
            // Ok, front- and backbuffer are now identical, so a bufferswap
            // will be a visual no-op.
            
            // Enable beamsyncing of bufferswaps to VBL:
            PsychOSSetVBLSyncLevel(windowRecord, 1);                
            
            // Trigger bufferswap in sync with retrace:
            PsychOSFlipWindowBuffers(windowRecord);
            
            // Wait for swap-completion, aka beginning of VBL:
            PsychWaitPixelSyncToken(windowRecord);
            
            // VBL happened - Take system timestamp:
            PsychGetAdjustedPrecisionTimerSeconds(&tvbl);
            
            // This code-chunk is an alternate way of syncing, only used for debugging:
            if (false) {
                // Disable beamsyncing of bufferswaps to VBL:
                PsychOSSetVBLSyncLevel(windowRecord, 0);                
                // Swap buffers immediately without vsync:
                PsychOSFlipWindowBuffers(windowRecord);
            }
            
            // Decrement remaining number of frames to wait:
            waitFrames--;
        } // Do it again...
        
        // Enable beamsyncing of bufferswaps to VBL:
        PsychOSSetVBLSyncLevel(windowRecord, 1);                
        
        // Restore assignment of read- writebuffers and such:
        glEnable(GL_BLEND);
        glReadBuffer(read_buffer);
        glDrawBuffer(draw_buffer);        
        // Done with Windows waitblanking...
    }
    
    // Compute number of frames waited: It is timestamp of return of this waitblanking minus
    // timestamp of return of last waitblanking, divided by duration of a monitor refresh
    // interval, mathematically rounded to an integral number:
    framesWaited = (int) (((tvbl - windowRecord->time_at_last_vbl) / ifi) + 0.5f);
    
    // Update timestamp for next invocation of Waitblanking:
    windowRecord->time_at_last_vbl = tvbl;
    
    // Return optional number of frames waited:
    PsychCopyOutDoubleArg(1, FALSE, (double) framesWaited);
    
    // Done.
    return(PsychError_none);
}
PsychError SCREENGetWindowInfo(void) 
{
    const char *FieldNames[]={ "Beamposition", "LastVBLTimeOfFlip", "LastVBLTime", "VBLCount", "StereoMode", "ImagingMode", "MultiSampling", "MissedDeadlines", "StereoDrawBuffer",
							   "GuesstimatedMemoryUsageMB", "VBLStartline", "VBLEndline", "VideoRefreshFromBeamposition", "GLVendor", "GLRenderer", "GLVersion",
							   "GLSupportsFBOUpToBpc", "GLSupportsBlendingUpToBpc", "GLSupportsTexturesUpToBpc", "GLSupportsFilteringUpToBpc", "GLSupportsPrecisionColors",
							   "GLSupportsFP32Shading", "BitsPerColorComponent" };
							   
	const int  fieldCount = 23;
	PsychGenericScriptType	*s;
	
    PsychWindowRecordType *windowRecord;
    double beamposition, lastvbl;
	int beamposonly = 0;
	CGDirectDisplayID displayId;
	psych_uint64 postflip_vblcount;
	double vbl_startline;
	long scw, sch;
	bool onscreen;

    //all subfunctions should have these two lines.  
    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(1));    //The maximum number of outputs
    
    // Get the window record:
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
	onscreen = PsychIsOnscreenWindow(windowRecord);
    
    // Query beamposonly flag: Defaults to zero.
    PsychCopyInIntegerArg(2, FALSE, &beamposonly);

	if (onscreen) {
		// Query rasterbeam position: Will return -1 if unsupported.
		PsychGetCGDisplayIDFromScreenNumber(&displayId, windowRecord->screenNumber);
		beamposition = (double) PsychGetDisplayBeamPosition(displayId, windowRecord->screenNumber);
	}
	else {
		beamposition = -1;
	}
	
	if (beamposonly) {
		// Return the measured beamposition:
		PsychCopyOutDoubleArg(1, FALSE, beamposition);
	}
	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:
		PsychSetStructArrayDoubleElement("LastVBLTimeOfFlip", 0, windowRecord->time_at_last_vbl, s);

		// Try to determine system time of last VBL on display, independent of any
		// flips / bufferswaps.
		lastvbl = -1;
		postflip_vblcount = 0;
		
		#if PSYCH_SYSTEM == PSYCH_OSX
			// On OS/X 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->screenNumber, &postflip_vblcount); }
		#endif

		// 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) postflip_vblcount, s);

		// Misc. window parameters:
		PsychSetStructArrayDoubleElement("StereoMode", 0, windowRecord->stereomode, s);
		PsychSetStructArrayDoubleElement("ImagingMode", 0, windowRecord->imagingMode, s);
		PsychSetStructArrayDoubleElement("MultiSampling", 0, windowRecord->multiSample, s);
		PsychSetStructArrayDoubleElement("MissedDeadlines", 0, windowRecord->nr_missed_deadlines, 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);
		
		// Renderer information:
		PsychSetDrawingTarget(windowRecord);
		PsychSetStructArrayStringElement("GLVendor", 0, glGetString(GL_VENDOR), s);
		PsychSetStructArrayStringElement("GLRenderer", 0, glGetString(GL_RENDERER), s);
		PsychSetStructArrayStringElement("GLVersion", 0, glGetString(GL_VERSION), 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);
	}

    // Done.
    return(PsychError_none);
}
/*
    PsychOSOpenOnscreenWindow()
    
    Creates the pixel format and the context objects and then instantiates the context onto the screen.
    
    -The pixel format and the context are stored in the target specific field of the window recored.  Close
    should clean up by destroying both the pixel format and the context.
    
    -We mantain the context because it must be be made the current context by drawing functions to draw into 
    the specified window.
    
    -We maintain the pixel format object because there seems to be now way to retrieve that from the context.
    
    -To tell the caller to clean up PsychOSOpenOnscreenWindow returns FALSE if we fail to open the window. It 
    would be better to just issue an PsychErrorExit() and have that clean up everything allocated outside of
    PsychOpenOnscreenWindow().
*/
boolean PsychOSOpenOnscreenWindow(PsychScreenSettingsType *screenSettings, PsychWindowRecordType *windowRecord, int numBuffers, int stereomode, int conserveVRAM)
{
  RECT winRec;
  PsychRectType             screenrect;
  CGDirectDisplayID         cgDisplayID;
  int         pf;
  unsigned int nNumFormats;
  HDC         hDC;
  HWND        hWnd;
  WNDCLASS    wc;
  PIXELFORMATDESCRIPTOR pfd;
  int         attribs[48];
  int         attribcount;
  float       fattribs[2]={0,0};
  int x, y, width, height, i, bpc;
  GLenum      glerr;
  DWORD flags;
  boolean fullscreen = FALSE;
  DWORD windowStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
  // The WS_EX_NOACTIVATE flag prevents the window from grabbing keyboard focus. That way,
  // the new Java-GetChar can do its job.
  DWORD windowExtendedStyle = WS_EX_APPWINDOW | 0x08000000; // const int WS_EX_NOACTIVATE = 0x08000000;

	 // Init to safe default:
    windowRecord->targetSpecific.glusercontextObject = NULL;
    
    // Map the logical screen number to a device handle for the corresponding
    // physical display device: CGDirectDisplayID is currently typedef'd to a
    // HDC windows hardware device context handle.
    PsychGetCGDisplayIDFromScreenNumber(&cgDisplayID, screenSettings->screenNumber);

    // Check if this should be a fullscreen window:
    PsychGetScreenRect(screenSettings->screenNumber, screenrect);
    if (PsychMatchRect(screenrect, windowRecord->rect)) {
      // This is supposed to be a fullscreen window with the dimensions of
      // the current display/desktop:
      // Switch system to fullscreen-mode without changing any settings:
      fullscreen = ChangeScreenResolution(screenSettings->screenNumber, 0, 0, 0, 0);
    }
    else {
      // Window size different from current screen size:
      // A regular desktop window with borders and control icons is requested, e.g., for debugging:
      fullscreen = FALSE;
    }

	 // Special case for explicit multi-display setup under Windows when opening a window on
	 // screen zero. We enforce the fullscreen - flag, aka a borderless top level window. This way,
    // if anything of our automatic full-desktop window emulation code goes wrong on exotic setups,
    // the user can still enforce a suitably positioned and sized borderless toplevel window.
    if (PsychGetNumDisplays()>2 && screenSettings->screenNumber == 0) fullscreen = TRUE;

    if (fullscreen) {
      windowStyle = WS_POPUP;		      // Set The WindowStyle To WS_POPUP (Popup Window without borders)
      windowExtendedStyle |= WS_EX_TOPMOST;   // Set The Extended Window Style To WS_EX_TOPMOST
    }
    else {
      windowStyle |= WS_OVERLAPPEDWINDOW;
      windowExtendedStyle |= WS_EX_TOPMOST;   // Set The Extended Window Style To WS_EX_TOPMOST
    }

    // Define final position and size of window:
    x=windowRecord->rect[kPsychLeft];
    y=windowRecord->rect[kPsychTop];
    width=PsychGetWidthFromRect(windowRecord->rect);
    height=PsychGetHeightFromRect(windowRecord->rect);

    // Register our own window class for Psychtoolbox onscreen windows:
    // Only register the window class once - use hInstance as a flag.
    if (!hInstance) {
      hInstance = GetModuleHandle(NULL);
      wc.style         = CS_OWNDC;
      wc.lpfnWndProc   = WndProc;
      wc.cbClsExtra    = 0;
      wc.cbWndExtra    = 0;
      wc.hInstance     = hInstance;
      wc.hIcon         = LoadIcon(hInstance, IDI_WINLOGO);
      wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
      wc.hbrBackground = NULL;
      wc.lpszMenuName  = NULL;
      wc.lpszClassName = "PTB-OpenGL";

      if (!RegisterClass(&wc)) {
		  hInstance = 0;
        printf("\nPTB-ERROR[Register Windowclass failed]: Unknown error, Win32 specific.\n\n");
        return(FALSE);
      }
    }

    //if (PSYCH_DEBUG == PSYCH_ON) printf("Creating Window...\n");

    // Adjust window bounds to account for the window borders if we are in non-fullscreen mode:
    if (!fullscreen) {
      winRec.left=x; winRec.top=y; winRec.right=x+width; winRec.bottom=y+height;
      AdjustWindowRectEx(&winRec, windowStyle, 0, windowExtendedStyle);
      x=winRec.left; y=winRec.top; width=winRec.right - winRec.left; height=winRec.bottom - winRec.top;
    }

    // Window class registered: Create a window of this class with some specific properties:
    hWnd = CreateWindowEx(windowExtendedStyle,
			  "PTB-OpenGL",
			  "PTB Onscreen window",
			  windowStyle,
			  x, y, width, height, NULL, NULL, hInstance, NULL);

    if (hWnd == NULL) {
        printf("\nPTB-ERROR[CreateWindow() failed]: Unknown error, Win32 specific.\n\n");
        return(FALSE);
    }

    // Retrieve device context for the window:
    hDC = GetDC(hWnd);

    // Setup optional flags for pixelformat:
    flags = 0;
    // Init pfd to zero:
    memset(&pfd, 0, sizeof(pfd));
    attribcount = 0;

    attribs[attribcount++]=0x2001; // WGL_DRAW_TO_WINDOW_ARB
    attribs[attribcount++]=GL_TRUE;
    attribs[attribcount++]=0x2010; // WGL_SUPPORT_OPENGL_ARB
    attribs[attribcount++]=GL_TRUE;
    attribs[attribcount++]=0x2007; // WGL_SWAP_METHOD_ARB
    attribs[attribcount++]=0x2028; // WGL_SWAP_EXCHANGE_ARB
    attribs[attribcount++]=0x2013; // WGL_PIXEL_TYPE_ARB
    
    // Select either floating point or fixed point framebuffer:
    if (windowRecord->depth == 64 || windowRecord->depth == 128) {
      // Request a floating point drawable instead of a fixed-point one:
      attribs[attribcount++]=WGL_TYPE_RGBA_FLOAT_ARB;
    }
    else {
      // Request standard fixed point drawable:
      attribs[attribcount++]=0x202B; // WGL_TYPE_RGBA_ARB
    }
    
    // Select requested depth per color component 'bpc' for each channel:
    bpc = 8; // We default to 8 bpc == RGBA8
    if (windowRecord->depth == 30)  { bpc = 10; printf("PTB-INFO: Trying to enable at least 10 bpc fixed point framebuffer.\n"); }
    if (windowRecord->depth == 64)  { bpc = 16; printf("PTB-INFO: Trying to enable 16 bpc floating point framebuffer.\n"); }
    if (windowRecord->depth == 128) { bpc = 32; printf("PTB-INFO: Trying to enable 32 bpc floating point framebuffer.\n"); }
    
    // Set up color depth for each channel:
    attribs[attribcount++]=WGL_RED_BITS_ARB;
    attribs[attribcount++]=bpc;
    attribs[attribcount++]=WGL_GREEN_BITS_ARB;
    attribs[attribcount++]=bpc;
    attribs[attribcount++]=WGL_BLUE_BITS_ARB;
    attribs[attribcount++]=bpc;
    attribs[attribcount++]=WGL_ALPHA_BITS_ARB;
    // Alpha channel has only 2 bpc in the fixed point bpc=10 case, i.e. RGBA=8882.
    attribs[attribcount++]=(bpc == 10) ? 2 : bpc;
    
    
    // Stereo display support: If stereo display output is requested with OpenGL native stereo,
    // we request a stereo-enabled rendering context.
    if(stereomode==kPsychOpenGLStereo) {
      flags = flags | PFD_STEREO;
      attribs[attribcount++]=0x2012; // WGL_STEREO_ARB
      attribs[attribcount++]=GL_TRUE;
    }
    
    // Double buffering requested?
    if(numBuffers>=2) {
      // Enable double-buffering:
      flags = flags | PFD_DOUBLEBUFFER;
      attribs[attribcount++]=0x2011; // WGL_DOUBLE_BUFFER_ARB
      attribs[attribcount++]=GL_TRUE;
      
      // AUX buffers for Flip-Operations needed?
      if ((conserveVRAM & kPsychDisableAUXBuffers) == 0) {
	// Allocate one or two (mono vs. stereo) AUX buffers for new "don't clear" mode of Screen('Flip'):
	// Not clearing the framebuffer after "Flip" is implemented by storing a backup-copy of
	// the backbuffer to AUXs before flip and restoring the content from AUXs after flip.
	pfd.cAuxBuffers=(stereomode==kPsychOpenGLStereo || stereomode==kPsychCompressedTLBRStereo || stereomode==kPsychCompressedTRBLStereo) ? 2 : 1;
	attribs[attribcount++]=0x2024; // WGL_AUX_BUFFERS_ARB
	attribs[attribcount++]=pfd.cAuxBuffers;
      }
    }
    
    //if (PSYCH_DEBUG == PSYCH_ON) printf("Device context is %p\n", hDC);
    
    // Build pixelformat descriptor:
    pfd.nSize        = sizeof(pfd);
    pfd.nVersion     = 1;
    pfd.dwFlags      = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_SWAP_EXCHANGE |flags;  // Want OpenGL capable window with bufferswap via page-flipping...
    pfd.iPixelType   = PFD_TYPE_RGBA; // Want a RGBA pixel format.
    pfd.cColorBits   = 32;            // 32 bpp at least...
    pfd.cAlphaBits   = 8;             // Want a 8 bit alpha-buffer.
    
    // Support for OpenGL 3D rendering requested?
    if (PsychPrefStateGet_3DGfx()) {
      // Yes. Allocate and attach a 24bit depth buffer and 8 bit stencil buffer:
      pfd.cDepthBits = 24;
      pfd.cStencilBits = 8;
      attribs[attribcount++]=0x2022; // WGL_DEPTH_BITS_ARB
      attribs[attribcount++]=24;
      attribs[attribcount++]=0x2023; // WGL_STENCIL_BITS_ARB
      attribs[attribcount++]=8;
    }
    
    // Multisampled Anti-Aliasing requested?
    if (windowRecord->multiSample > 0) {
      // Request a multisample buffer:
      attribs[attribcount++]= 0x2041; // WGL_SAMPLE_BUFFERS_ARB
      attribs[attribcount++]= 1;
      // Request at least multiSample samples per pixel:
      attribs[attribcount++]= 0x2042; // WGL_SAMPLES_ARB
      attribs[attribcount++]= windowRecord->multiSample;
    }
    
    // Finalize attribs-array:
    attribs[attribcount++]= 0;
    
    //if (PSYCH_DEBUG == PSYCH_ON) printf("Choosing pixelformat\n");
    
    // Create pixelformat:
    // This is typical Microsoft brain-damage: We first need to create a window the
    // conventional old way just to be able to get a handle to the new wglChoosePixelFormat
    // method, which will us - after destroying and recreating the new window - allow to
    // select the pixelformat we actually want!
    
    // Step 1: Choose pixelformat old-style:
    pf = ChoosePixelFormat(hDC, &pfd);
    
    // Do we have a valid pixelformat?
    if (pf == 0) {
      // Nope. We give up!
      ReleaseDC(hDC, hWnd);
      DestroyWindow(hWnd);      
      printf("\nPTB-ERROR[ChoosePixelFormat() failed]: Unknown error, Win32 specific.\n\n");
      return(FALSE);
    }
    
    // Yes. Set it:
    if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
      ReleaseDC(hDC, hWnd);
      DestroyWindow(hWnd);      
      
      printf("\nPTB-ERROR[SetPixelFormat() failed]: Unknown error, Win32 specific.\n\n");
      return(FALSE);
    }
    
    // Ok, create and attach the rendering context.
    windowRecord->targetSpecific.contextObject = wglCreateContext(hDC);
    if (windowRecord->targetSpecific.contextObject == NULL) {
      ReleaseDC(hDC, hWnd);
      DestroyWindow(hWnd);
      
      printf("\nPTB-ERROR:[Context creation failed] Unknown, Win32 specific.\n\n");
      return(FALSE);
    }
    
    // Store the handles...
    windowRecord->targetSpecific.windowHandle = hWnd;
    windowRecord->targetSpecific.deviceContext = hDC;
    
    // Activate the rendering context:
    PsychOSSetGLContext(windowRecord);
    
    // Ok, the OpenGL rendering context is up and running. Auto-detect and bind all
    // available OpenGL extensions via GLEW:
    glerr = glewInit();
    if (GLEW_OK != glerr)
      {
	/* Problem: glewInit failed, something is seriously wrong. */
	printf("\nPTB-ERROR[GLEW init failed: %s]: Please report this to the forum. Will try to continue, but may crash soon!\n\n", glewGetErrorString(glerr));
	fflush(NULL);
      }
    else {
      if (PsychPrefStateGet_Verbosity()>4) printf("PTB-INFO: Using GLEW version %s for automatic detection of OpenGL extensions...\n", glewGetString(GLEW_VERSION));
    }
    
    DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
    
    if ((stereomode==kPsychOpenGLStereo) && ((pfd.dwFlags & PFD_STEREO)==0)) {
      // Ooops. Couldn't get the requested stereo-context from hardware :(
      ReleaseDC(hDC, hWnd);
      DestroyWindow(hWnd);
      
      printf("PTB-ERROR: OpenGL native stereo mode unavailable. Your hardware may not support it,\n"
	     "PTB-ERROR: or at least not on a flat-panel? Expect abortion of your script soon...");
      
      return(FALSE);
    }
    
	// Special debug override for faulty drivers with non-working extension:
	if (conserveVRAM & kPsychOverrideWglChoosePixelformat) wglChoosePixelFormatARB = NULL;

	// Step 2: Ok, we have an OpenGL rendering context with all known extensions bound:
	// Do we have (or want) support for wglChoosePixelFormatARB?
   // We skip use of it if we can do without it, i.e., when we don't need unusual framebuffer
   // configs (like non 8bpc fixed) and we don't need multisampling. This is a work-around
   // for hardware that has trouble (=driver bugs) with wglChoosePixelformat() in some modes, e.g., the NVidia
	// Quadro gfx card, which fails to enable quad-buffered stereo when using wglChoosePixelformat(),
	// but does so perfectly well when using the old-style ChoosePixelFormat(). 
	if ((wglChoosePixelFormatARB == NULL) || ((bpc==8) && (windowRecord->multiSample <= 0))) {
	  // Failed (==NULL) or disabled via override.
	  if ((wglChoosePixelFormatARB == NULL) && (PsychPrefStateGet_Verbosity() > 1)) printf("PTB-WARNING: Could not bind wglChoosePixelFormat - Extension. Some features will be unavailable, e.g., Anti-Aliasing and high precision framebuffers.\n");
	}
	else {
	  // Supported. We destroy the rendering context and window, then recreate it with
	  // the wglChoosePixelFormat - method...
	  wglMakeCurrent(NULL, NULL);

	  // Delete rendering context:
	  wglDeleteContext(windowRecord->targetSpecific.contextObject);
	  windowRecord->targetSpecific.contextObject=NULL;

      // Release device context:
      ReleaseDC(windowRecord->targetSpecific.deviceContext, windowRecord->targetSpecific.windowHandle);
      windowRecord->targetSpecific.deviceContext=NULL;

   	// Close & Destroy the window:
      DestroyWindow(windowRecord->targetSpecific.windowHandle);
      windowRecord->targetSpecific.windowHandle=NULL;

		// Ok, old window and stuff is dead. Create new window:
    	hWnd = CreateWindowEx(windowExtendedStyle, "PTB-OpenGL", "PTB Onscreen window", windowStyle,
			  						 x, y, width, height, NULL, NULL, hInstance, NULL);
    	if (hWnd == NULL) {
        printf("\nPTB-ERROR[CreateWindow() - II failed]: Unknown error, Win32 specific.\n\n");
        return(FALSE);
    	}

	   // Retrieve device context for the window:
    	hDC = GetDC(hWnd);

		pf = 0;
		nNumFormats=0;
		wglChoosePixelFormatARB(hDC, &attribs[0], NULL, 1, &pf, &nNumFormats);
      if (nNumFormats==0 && windowRecord->multiSample > 0) {
		 	// Failed. Probably due to too demanding multisample requirements: Lets lower them...
			for (i=0; i<attribcount && attribs[i]!=0x2042; i++);
			// Reduce requested number of samples/pixel and retry until we get one:
			while (nNumFormats==0 && windowRecord->multiSample > 0) {
				// printf("Failed for multisampling level %i nNum=%i\n", windowRecord->multiSample, nNumFormats);
				attribs[i+1]--;
	  			windowRecord->multiSample--;
				wglChoosePixelFormatARB(hDC, &attribs[0], NULL, 1, &pf, &nNumFormats);
			}
			// If we still do not get one with 0 samples per pixel, we can try to disable
			// multisampling completely:
			if (windowRecord->multiSample == 0 && nNumFormats==0) {
				// printf("Failed for multisampling level %i nNum=%i --> Disabling multisampling...\n", windowRecord->multiSample, nNumFormats);
				// We 0-Out all entries related to multi-sampling, including the
				// GL_SAMPLES_ARB and GL_SAMPLE_BUFFERS_ARB enums themselves:
				for (i=0; i<attribcount && attribs[i]!=0x2042; i++);
	  			attribs[i]=0;
	  			for (i=0; i<attribcount && attribs[i]!=0x2041; i++);
	  			attribs[i]=0;
	  			attribs[i+1]=0;
				wglChoosePixelFormatARB(hDC, &attribs[0], NULL, 1, &pf, &nNumFormats);
			}
      }

		if (nNumFormats==0 && numBuffers>=2) {
			// We still don't have a valid pixelformat, but double-buffering is enabled.
			// Let's try if we get one if we do not request any AUX-Buffers:
			for (i=0; i<attribcount && attribs[i]!=0x2024; i++);
			attribs[i+1] = 0; // Zero AUX-Buffers requested.
			wglChoosePixelFormatARB(hDC, &attribs[0], NULL, 1, &pf, &nNumFormats);
		}

		if (nNumFormats==0 && numBuffers>=2) {
			// We still don't have a valid pixelformat, but double-buffering is enabled.
			// Let's try if we get one if we do not request SWAP_EXCHANGED double buffering anymore:
			for (i=0; i<attribcount && attribs[i]!=0x2028; i++);
			attribs[i] = 0x202A; // WGL_SWAP_UNDEFINED_ARB
			wglChoosePixelFormatARB(hDC, &attribs[0], NULL, 1, &pf, &nNumFormats);
		}

		// Either we have a multisampled or non-multisampled format, or none. If we failed,
		// then we can not do anything anymore about it.

	   // Do we have a valid pixelformat?
      if (nNumFormats == 0) {
		   // Nope. We give up!
         ReleaseDC(hDC, hWnd);
         DestroyWindow(hWnd);      
         printf("\nPTB-ERROR[wglChoosePixelFormat() failed]: Unknown error, Win32 specific. Code: %i.\n\n", GetLastError());
         return(FALSE);
      }

      // Yes. Set it:
      if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
         ReleaseDC(hDC, hWnd);
         DestroyWindow(hWnd);      

         printf("\nPTB-ERROR[SetPixelFormat() - II failed]: Unknown error, Win32 specific.\n\n");
         return(FALSE);
      }

      // Ok, create and attach the rendering context.
      windowRecord->targetSpecific.contextObject = wglCreateContext(hDC);
      if (windowRecord->targetSpecific.contextObject == NULL) {
         ReleaseDC(hDC, hWnd);
         DestroyWindow(hWnd);

         printf("\nPTB-ERROR:[Context creation II failed] Unknown, Win32 specific.\n\n");
         return(FALSE);
      }
    
      // Store the handles...
      windowRecord->targetSpecific.windowHandle = hWnd;
      windowRecord->targetSpecific.deviceContext = hDC;

      // Activate the rendering context:
      PsychOSSetGLContext(windowRecord);

      DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);

      if ((stereomode==kPsychOpenGLStereo) && ((pfd.dwFlags & PFD_STEREO)==0)) {
         // Ooops. Couldn't get the requested stereo-context from hardware :(
         ReleaseDC(hDC, hWnd);
         DestroyWindow(hWnd);

         printf("PTB-ERROR: OpenGL native stereo mode unavailable. Your hardware may not support it,\n"
	             "PTB-ERROR: or at least not on a flat-panel? Expect abortion of your script soon...");

         return(FALSE);
      }		
		// Done with final window and OpenGL context setup. We've got our final context enabled.
	 }

	 // Enable multisampling if this was requested:
    if (windowRecord->multiSample > 0) glEnable(0x809D); // 0x809D == GL_MULTISAMPLE_ARB
	 // Throw away any error-state this could have created on old hardware...
	 glGetError();

    // External 3D graphics support enabled? Or OpenGL quad-buffered stereo enabled?
	 // For the former, we need this code for OpenGL state isolation. For the latter we
    // need this code as workaround for Windows brain-damage. For some reason it helps
    // to properly shutdown stereo contexts on Windows...
	 if (PsychPrefStateGet_3DGfx() || stereomode == kPsychOpenGLStereo) {
		// Yes. We need to create an extra OpenGL rendering context for the external
		// OpenGL code to provide optimal state-isolation. The context shares all
		// heavyweight ressources likes textures, FBOs, VBOs, PBOs, display lists and
		// starts off as an identical copy of PTB's context as of here.
      windowRecord->targetSpecific.glusercontextObject = wglCreateContext(hDC);
		if (windowRecord->targetSpecific.glusercontextObject == NULL) {
         ReleaseDC(hDC, hWnd);
         DestroyWindow(hWnd);
			printf("\nPTB-ERROR[UserContextCreation failed]: Creating a private OpenGL context for Matlab OpenGL failed for unknown reasons.\n\n");
			return(FALSE);
		}

		wglMakeCurrent(windowRecord->targetSpecific.deviceContext, windowRecord->targetSpecific.glusercontextObject);
		wglMakeCurrent(windowRecord->targetSpecific.deviceContext, windowRecord->targetSpecific.contextObject);

		// Copy full state from our main context:
		if(!wglCopyContext(windowRecord->targetSpecific.contextObject, windowRecord->targetSpecific.glusercontextObject, GL_ALL_ATTRIB_BITS)) {
			// This is ugly, but not fatal...
			if (PsychPrefStateGet_Verbosity()>1) {
				printf("\nPTB-WARNING[wglCopyContext for user context failed]: Copying state to private OpenGL context for Matlab OpenGL failed for unknown reasons.\n\n");
			}
		}

	   // Enable ressource sharing with master context for this window:
		if (!wglShareLists(windowRecord->targetSpecific.contextObject, windowRecord->targetSpecific.glusercontextObject)) {
			// This is ugly, but not fatal...
			if (PsychPrefStateGet_Verbosity()>1) {
				printf("\nPTB-WARNING[wglShareLists for user context failed]: Ressource sharing with private OpenGL context for Matlab OpenGL failed for unknown reasons.\n\n");
			}		
		}

	 }

    // Finally, show our new window:
    ShowWindow(hWnd, SW_SHOW);

    // Give it higher priority than other applications windows:
    // Disabled: Interferes with JAVA-GetChar: SetForegroundWindow(hWnd);

    // Set the focus on it:
    // Disabled: Interferes with JAVA-GetChar: SetFocus(hWnd);

    // Capture the window if it is a fullscreen one: This window will receive all
    // mouse move and mouse button press events. Important for GetMouse() to work
    // properly...
    if (fullscreen) SetCapture(hWnd);

    // Increase our own open window counter:
    win32_windowcount++;

    // Some info for the user regarding non-fullscreen and ATI hw:
    if (!fullscreen && (strstr(glGetString(GL_VENDOR), "ATI"))) {
      printf("PTB-INFO: Some ATI graphics cards may not support proper syncing to vertical retrace when\n");
      printf("PTB-INFO: running in windowed mode (non-fullscreen). If PTB aborts with 'Synchronization failure'\n");
      printf("PTB-INFO: you can disable the sync test via call to Screen('Preference', 'SkipSyncTests', 1); .\n");
      printf("PTB-INFO: You won't get proper stimulus onset timestamps though, so windowed mode may be of limited use.\n");
    }

    // Dynamically bind the VSYNC extension:
    //if (strstr(glGetString(GL_EXTENSIONS), "WGL_EXT_swap_control")) {
      // Bind it:
      // wglSwapIntervalEXT=(PFNWGLEXTSWAPCONTROLPROC) wglGetProcAddress("wglSwapIntervalEXT");
    //}
    //else {
	 if (wglSwapIntervalEXT == NULL) {
      wglSwapIntervalEXT = NULL;
      printf("PTB-WARNING: Your graphics driver doesn't allow me to control syncing wrt. vertical retrace!\n");
      printf("PTB-WARNING: Please update your display graphics driver as soon as possible to fix this.\n");
      printf("PTB-WARNING: Until then, you can manually enable syncing to VBL somewhere in the display settings\n");
      printf("PTB-WARNING: tab of your machine.\n");
    }

    // Ok, we should be ready for OS independent setup...

    //printf("\nPTB-INFO: Low-level (Windoze) setup of onscreen window finished!\n");
    //fflush(NULL);

    // Well Done!
    return(TRUE);
}
Esempio n. 13
0
/*
    PsychOSOpenOnscreenWindow()
    
    Creates the CGL pixel format and the CGL context objects and then instantiates the context onto the screen.
    
    -The pixel format and the context are stored in the target specific field of the window recored.  Close
    should clean up by destroying both the pixel format and the context.
    
    -We mantain the context because it must be be made the current context by drawing functions to draw into 
    the specified window.
    
    -We maintain the pixel format object because there seems to be now way to retrieve that from the context.
    
    -To tell the caller to clean up PsychOSOpenOnscreenWindow returns FALSE if we fail to open the window. It 
    would be better to just issue an PsychErrorExit() and have that clean up everything allocated outside of
    PsychOpenOnscreenWindow().
    
    MK: The new option 'stereomode' allows selection of stereo display instead of mono display:
    0 (default) ==  Old behaviour -> Monoscopic rendering context.
    >0          ==  Stereo display, where the number defines the type of stereo algorithm to use.
    =1          ==  Use OpenGL built-in stereo by creating a context/window with left- and right backbuffer. This is
                    the only mode of interest here, as it requires use of a stereo capable OpenGL pixelformat. All other
                    stereo modes are implemented by PTB itself in a platform independent manner on top of a standard mono
                    context.
*/
psych_bool PsychOSOpenOnscreenWindow(PsychScreenSettingsType *screenSettings, PsychWindowRecordType *windowRecord, int numBuffers, int stereomode, int conserveVRAM)
{
    CGOpenGLDisplayMask             displayMask;
    CGLError                        error;
    CGDirectDisplayID               cgDisplayID;
    CGLPixelFormatAttribute         attribs[40];
    int                             attribcount;
    GLint                           numVirtualScreens;
    GLenum                          glerr;
    PsychRectType                   screenrect;
    int                             i;
    int                             windowLevel;
    long                            scw, sch;
    void*                           cocoaWindow = NULL;

    // Map screen number to physical display handle cgDisplayID:
    PsychGetCGDisplayIDFromScreenNumber(&cgDisplayID, screenSettings->screenNumber);
    displayMask = CGDisplayIDToOpenGLDisplayMask(cgDisplayID);

    // NULL-out Cocoa window handle, so this is well-defined in case of error:
    windowRecord->targetSpecific.windowHandle = NULL;

    // Retrieve windowLevel, an indicator of where non-CGL / non-fullscreen windows should
    // be located wrt. to other windows. -2 = Allow regular window manager control of stacking
    // order, visibility etc., -1 = Invisible/hidden, 0 = Behind everything else, occluded by
    // everything else. 1 - 999 = At layer 'windowLevel' -> Occludes stuff on layers "below" it.
    // 1000 - 1999 = At highest level, but partially translucent / alpha channel allows to make
    // regions transparent. Range 1000 - 1499 = transparent for mouse and keyboard, alpha 0-99.99%,
    // 1500-1599 = opaque for mouse and keyboard, alpha 0-99.99%, 2000 or higher: Above everything,
    // fully opaque, occludes everything, a typical fullscreen onscreen window. 2000 is the default.
    windowLevel = PsychPrefStateGet_WindowShieldingLevel();

    // Window rect provided which has same size as 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. In such cases we use CGL for better
    // low level control and to exclude the desktop compositor from interfering:
    PsychGetScreenRect(screenSettings->screenNumber, screenrect);
    if (PsychMatchRect(screenrect, windowRecord->rect)) windowRecord->specialflags |= kPsychIsFullscreenWindow;

    PsychGetGlobalScreenRect(screenSettings->screenNumber, screenrect);
    if (PsychMatchRect(screenrect, windowRecord->rect)) windowRecord->specialflags |= kPsychIsFullscreenWindow;

    if ((windowRecord->specialflags & kPsychIsFullscreenWindow) && (PsychPrefStateGet_Verbosity() > 3)) {
        printf("PTB-INFO: Always using Cocoa for fullscreen windows to work around graphics driver bugs in OSX.\n");
        printf("PTB-INFO: Presentation timing precision is not yet known for this configuration on most machines. Check your results.\n");
    }

    // Display for fullscreen window not captured? Timing precision is unclear in this mode. In theory the compositor should disable
    // itself for fullscreen windows on modern OSX versions. If it really does that, who knows?
    if ((windowRecord->specialflags & kPsychIsFullscreenWindow) && (PsychPrefStateGet_ConserveVRAM() & kPsychUseAGLCompositorForFullscreenWindows)) {
        // Force a window rectangle that matches the global screen rectangle for that windows screen:
        PsychCopyRect(windowRecord->rect, screenrect);

        // Warn user about what's going on:
        if (PsychPrefStateGet_Verbosity()>1) printf("PTB-INFO: No display capture / compositor lockout for fullscreen window. Timing precision is unknown.\n");
    }

    if ((windowRecord->specialflags & kPsychGUIWindow) && (PsychPrefStateGet_Verbosity() > 3)) {
        printf("PTB-INFO: Onscreen window is configured as regular GUI window.\n");
    }

    // Create onscreen Cocoa window of requested position and size:
    if (PsychCocoaCreateWindow(windowRecord, windowLevel, &cocoaWindow)) {
        printf("\nPTB-ERROR[CreateNewWindow failed]: Failed to open Cocoa onscreen window\n\n");
        return(FALSE);
    }

    // Transparent window requested?
    if ((windowLevel >= 1000) && (windowLevel < 2000)) {
        // Setup of global window alpha value for transparency. This is premultiplied to
        // the individual per-pixel alpha values if transparency is enabled by Cocoa code.
        //
        // Levels 1000 - 1499 and 1500 to 1999 map to a master opacity level of 0.0 - 1.0:
        PsychCocoaSetWindowAlpha(cocoaWindow, ((float) (windowLevel % 500)) / 499.0);
    }

    // Show it! Unless a windowLevel of -1 requests hiding the window:
    if (windowLevel != -1) PsychCocoaShowWindow(cocoaWindow);

    // If usercode wants a black startup screen then we add a pause of 0.5 seconds here
    // before proceeding. This will avoid a white flash at window open time, which might
    // be something the user wanted to avoid. Why does this help or is needed at all?
    // Nobody knows, but this is Apples ridiculous toy OS, so why even ask such questions?
    if (PsychPrefStateGet_VisualDebugLevel() < 4)
        PsychYieldIntervalSeconds(0.5);

    // Level zero means: Place behind all other windows:
    if (windowLevel == 0) PsychCocoaSendBehind(cocoaWindow);

    // Levels 1 to 999 define window levels for the group of the window.
    // A level of -2 would leave this to the system:
    if (windowLevel > 0 && windowLevel < 1000) PsychCocoaSetWindowLevel(cocoaWindow, windowLevel);

    // Is the target display captured for a fullscreen window?
    if (PsychIsScreenCaptured(screenSettings->screenNumber)) {
        // Yes. Make sure our window is above the shielding window level:
        PsychCocoaSetWindowLevel(cocoaWindow, (int) CGShieldingWindowLevel());
    }

    // Store window handle in windowRecord:
    windowRecord->targetSpecific.windowHandle = cocoaWindow;

    // Store vblank startline aka true height of physical display screen in pixels:
    PsychGetScreenPixelSize(screenSettings->screenNumber, &scw, &sch);
    windowRecord->VBL_Startline = (int) sch;

    // Define pixelformat attributes for OpenGL contexts:

    // No pixelformat attribs to start with:
    attribcount = 0;

    attribs[attribcount++]=kCGLPFADisplayMask;
    attribs[attribcount++]=displayMask;

    // 10 bit per component integer framebuffer requested (10-10-10-2)?
    if (windowRecord->depth == 30) {
        // Request a 10 bit per color component framebuffer with 2 bit alpha channel:
        printf("PTB-INFO: Trying to enable 10 bpc, 30 bit integer framebuffer...\n");
        attribs[attribcount++]=kCGLPFANoRecovery;
        attribs[attribcount++]=kCGLPFAMinimumPolicy;
        attribs[attribcount++]=kCGLPFAColorSize;
        attribs[attribcount++]=10*3;
        attribs[attribcount++]=kCGLPFAAlphaSize;
        attribs[attribcount++]=2;
    }

    // 11 bit per component integer framebuffer requested (11-11-10-0)?
    if (windowRecord->depth == 33) {
        // Request a ~ 11 bit per color component framebuffer without alpha channel:
        printf("PTB-INFO: Trying to enable 11 bpc, 32 bit integer framebuffer...\n");
        attribs[attribcount++]=kCGLPFANoRecovery;
        attribs[attribcount++]=kCGLPFAMinimumPolicy;
        attribs[attribcount++]=kCGLPFAColorSize;
        attribs[attribcount++]=32;
        attribs[attribcount++]=kCGLPFAAlphaSize;
        attribs[attribcount++]=0;
    }

    // 16 bit per component integer framebuffer requested (16-16-16-16)?
    if (windowRecord->depth == 48) {
        // Request a 16 bit per color component framebuffer:
        printf("PTB-INFO: Trying to enable 16 bpc, 64 bit integer framebuffer...\n");
        attribs[attribcount++]=kCGLPFANoRecovery;
        attribs[attribcount++]=kCGLPFAMinimumPolicy;
        attribs[attribcount++]=kCGLPFAColorSize;
        attribs[attribcount++]=16*3;
        attribs[attribcount++]=kCGLPFAAlphaSize;
        attribs[attribcount++]=16;
    }

    // 16 bit per component, 64 bit framebuffer requested (16-16-16-16)?
    if (windowRecord->depth == 64) {
        // Request a floating point framebuffer in 16-bit half-float format, i.e., RGBA = 16 bits per component.
        printf("PTB-INFO: Trying to enable 16 bpc float framebuffer...\n");
        attribs[attribcount++]=kCGLPFAColorFloat;
        attribs[attribcount++]=kCGLPFAMinimumPolicy;
        attribs[attribcount++]=kCGLPFAColorSize;
        attribs[attribcount++]=16*3;
        attribs[attribcount++]=kCGLPFAAlphaSize;
        attribs[attribcount++]=16;
    }

    // 32 bit per component, 128 bit framebuffer requested (32-32-32-32)?
    if (windowRecord->depth == 128) {
        // Request a floating point framebuffer in 32-bit float format, i.e., RGBA = 32 bits per component.
        printf("PTB-INFO: Trying to enable 32 bpc float framebuffer...\n");
        attribs[attribcount++]=kCGLPFAColorFloat;
        attribs[attribcount++]=kCGLPFAMinimumPolicy;
        attribs[attribcount++]=kCGLPFAColorSize;
        attribs[attribcount++]=32*3;
        attribs[attribcount++]=kCGLPFAAlphaSize;
        attribs[attribcount++]=32;
    }

    // Possible to request use of the Apple floating point software renderer:
    if (conserveVRAM & kPsychUseSoftwareRenderer) {
        #ifndef kCGLRendererGenericFloatID
        #define kCGLRendererGenericFloatID    0x00020400
        #endif

        attribs[attribcount++]=AGL_RENDERER_ID;
        attribs[attribcount++]=kCGLRendererGenericFloatID;
    }

    // Support for 3D rendering requested?
    if (PsychPrefStateGet_3DGfx()) {
        // Yes. Allocate a 24-Bit depth and 8-Bit stencilbuffer for this purpose:
        attribs[attribcount++]=kCGLPFADepthSize;
        attribs[attribcount++]=24;
        attribs[attribcount++]=kCGLPFAStencilSize;
        attribs[attribcount++]=8;
        // Alloc an accumulation buffer as well?
        if (PsychPrefStateGet_3DGfx() & 2) {
            // Yes: Alloc accum buffer, request 64 bpp, aka 16 bits integer per color component if possible:
            attribs[attribcount++]=kCGLPFAAccumSize;
            attribs[attribcount++]=64;
        }
    }

    if(numBuffers>=2){
        // Enable double-buffering:
        attribs[attribcount++]=kCGLPFADoubleBuffer;
        if ((conserveVRAM & kPsychDisableAUXBuffers) == 0) {
            // Allocate one or two (for mono vs. stereo display) AUX buffers for "don't clear" mode of Screen('Flip'):
            // Not clearing the framebuffer after "Flip" is implemented by storing a backup-copy of
            // the backbuffer to AUXs before flip and restoring the content from AUXs after flip.
            // Unless the imaging pipeline is active, which doesn't need AUX buffers due to internal
            // storage of fb content in its drawbufferFBO's:
            attribs[attribcount++]=kCGLPFAAuxBuffers;
            attribs[attribcount++]=(stereomode==kPsychOpenGLStereo || stereomode==kPsychCompressedTLBRStereo || stereomode==kPsychCompressedTRBLStereo) ? 2 : 1;
        }
    }

    // If stereo display output is requested with OpenGL native stereo, request a stereo-enabled rendering context.
    // This is deprecated since 10.11 El Capitan, and in fact does no longer work - OpenGL quad buffered stereo is
    // dead on 10.11 on all tested GPU's from Intel, NVidia, AMD.
    if(stereomode==kPsychOpenGLStereo) {
        attribs[attribcount++] = kCGLPFAStereo;
    }

    // Multisampled Anti-Aliasing requested?
    if (windowRecord->multiSample > 0) {
        // Request a multisample buffer:
        attribs[attribcount++]= kCGLPFASampleBuffers;
        attribs[attribcount++]= 1;
        // Request at least multiSample samples per pixel:
        attribs[attribcount++]= kCGLPFASamples;
        attribs[attribcount++]= windowRecord->multiSample;
    }

    // Finalize attribute array with NULL.
    attribs[attribcount++]=(CGLPixelFormatAttribute)NULL;

    // Init to zero:
    windowRecord->targetSpecific.pixelFormatObject = NULL;
    windowRecord->targetSpecific.glusercontextObject = NULL;
    windowRecord->targetSpecific.glswapcontextObject = NULL;

    // Try to find matching pixelformat:
    error = CGLChoosePixelFormat(attribs, &(windowRecord->targetSpecific.pixelFormatObject), &numVirtualScreens);

    // No valid pixelformat found and stereo format requested?
    if ((error || (windowRecord->targetSpecific.pixelFormatObject == NULL)) && (stereomode == kPsychOpenGLStereo)) {
        // Yep: Stereo may be the culprit. Remove the stereo attribute by overwriting it with something
        // that is essentially a no-op, specifically kCGLPFAAccelerated which is supported by all real
        // renderers that might end up in this code-path:
        for (i = 0; i < attribcount && attribs[i] != kCGLPFAStereo; i++);
        attribs[i] = kCGLPFAAccelerated;
        
        // Retry query of pixelformat without request for native OpenGL quad-buffered stereo. If we succeed, we're
        // sort of ok, as the higher-level code will fallback to stereomode kPsychFrameSequentialStereo - our own
        // homegrown frame-sequential stereo support, which may be good enough.
        error = CGLChoosePixelFormat(attribs, &(windowRecord->targetSpecific.pixelFormatObject), &numVirtualScreens);
        if (error || (windowRecord->targetSpecific.pixelFormatObject == NULL)) {
            windowRecord->targetSpecific.pixelFormatObject = NULL;
            printf("\nPTB-ERROR[ChoosePixelFormat failed: %s]: Disabling OpenGL native quad-buffered stereo did not help. Moving on...\n\n", CGLErrorString(error));
        }
    }

    // Now try if choosing a matching format for a lower multisample mode helps to get unstuck:
    if (windowRecord->multiSample > 0) {
        if (windowRecord->targetSpecific.pixelFormatObject==NULL && windowRecord->multiSample > 0) {
            // Failed. Probably due to too demanding multisample requirements: Lets lower them...
            for (i = 0; i < attribcount && attribs[i] != kCGLPFASamples; i++);
            while (windowRecord->targetSpecific.pixelFormatObject == NULL && windowRecord->multiSample > 0) {
                attribs[i+1]--;
                windowRecord->multiSample--;
                error = CGLChoosePixelFormat(attribs, &(windowRecord->targetSpecific.pixelFormatObject), &numVirtualScreens);
            }

            if (windowRecord->multiSample == 0 && windowRecord->targetSpecific.pixelFormatObject == NULL) {
                // Ok, multisampling is now at zero and we still don't succeed. Disable multisampling completely:
                for (i=0; i<attribcount && attribs[i]!=kCGLPFASampleBuffers; i++);
                attribs[i+1] = 0;
                printf("\nPTB-ERROR[ChoosePixelFormat failed: %s]: Disabling multisample anti-aliasing did not help. Moving on...\n\n", CGLErrorString(error));
            }
        }
    }

    // Try choosing a matching display configuration again and create the window and rendering context:
    // If one of these two fails, then the installed gfx hardware is not good enough to satisfy our
    // requirements, or we have massive ressource shortage in the system. -> Screwed up anyway, so we abort.
    if (windowRecord->targetSpecific.pixelFormatObject == NULL) error = CGLChoosePixelFormat(attribs, &(windowRecord->targetSpecific.pixelFormatObject), &numVirtualScreens);
    if (error) {
        printf("\nPTB-ERROR[ChoosePixelFormat failed: %s]: Reason unknown. There could be insufficient video memory or a driver malfunction. Giving up.\n\n", CGLErrorString(error));
        return(FALSE);
    }

    // Create an OpenGL rendering context with the selected pixelformat: Share its ressources with 'slaveWindow's context, if slaveWindow is non-NULL.
    // If slaveWindow is non-NULL here, then slaveWindow is typically another onscreen window. Therefore this establishes OpenGL resource sharing across
    // different onscreen windows in a session, e.g., for multi-display operation:
    error=CGLCreateContext(windowRecord->targetSpecific.pixelFormatObject, ((windowRecord->slaveWindow) ? windowRecord->slaveWindow->targetSpecific.contextObject : NULL),
                           &(windowRecord->targetSpecific.contextObject));
    if (error) {
        printf("\nPTB-ERROR[ContextCreation failed: %s]: Could not create master OpenGL context for new onscreen window. Insufficient video memory?\n\n", CGLErrorString(error));
        return(FALSE);
    }

    // Enable the OpenGL rendering context associated with our window:
    error=CGLSetCurrentContext(windowRecord->targetSpecific.contextObject);
    if (error) {
        printf("\nPTB-ERROR[SetCurrentContext failed: %s]: Insufficient video memory\n\n", CGLErrorString(error));
        return(FALSE);
    }

    // NULL-out the AGL context field, just for safety...
    windowRecord->targetSpecific.deviceContext = NULL;

    // Ok, the master OpenGL rendering context for this new onscreen window is up and running.
    // Auto-detect and bind all available OpenGL extensions via GLEW:
    glerr = glewInit();
    if (GLEW_OK != glerr)
    {
        /* Problem: glewInit failed, something is seriously wrong. */
        printf("\nPTB-ERROR[GLEW init failed: %s]: Please report this to the forum. Will try to continue, but may crash soon!\n\n", glewGetErrorString(glerr));
        fflush(NULL);
    }
    else {
        if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Using GLEW version %s for automatic detection of OpenGL extensions...\n", glewGetString(GLEW_VERSION));
    }

    // Enable multisampling if it was requested:
    if (windowRecord->multiSample > 0) glEnable(GL_MULTISAMPLE);

    // External 3D graphics support enabled?
    if (PsychPrefStateGet_3DGfx()) {
        // Yes. We need to create an extra OpenGL rendering context for the external
        // OpenGL code to provide optimal state-isolation. The context shares all
        // heavyweight ressources likes textures, FBOs, VBOs, PBOs, shader, display lists and
        // starts off as an identical copy of PTB's context as of here.
        error=CGLCreateContext(windowRecord->targetSpecific.pixelFormatObject, windowRecord->targetSpecific.contextObject, &(windowRecord->targetSpecific.glusercontextObject));
        if (error) {
            printf("\nPTB-ERROR[UserContextCreation failed: %s]: Creating a private OpenGL context for userspace OpenGL failed.\n\n", CGLErrorString(error));
            return(FALSE);
        }
    }

    // Create glswapcontextObject - An OpenGL context for exclusive use by parallel background threads,
    // e.g., our thread for async flip operations and self-made frame-sequential stereo:
    error=CGLCreateContext(windowRecord->targetSpecific.pixelFormatObject, windowRecord->targetSpecific.contextObject, &(windowRecord->targetSpecific.glswapcontextObject));
    if (error) {
        printf("\nPTB-ERROR[SwapContextCreation failed: %s]: Creating a private OpenGL context for async-bufferswaps failed.\n\n", CGLErrorString(error));
        CGLSetCurrentContext(NULL);
        return(FALSE);
    }

    // Store Cocoa onscreen window handle:
    windowRecord->targetSpecific.windowHandle = cocoaWindow;

    // Objective-C setup path, using Cocoa + NSOpenGLContext wrapped around already
    // existing and setup CGLContext:
    if (PsychCocoaSetupAndAssignOpenGLContextsFromCGLContexts(cocoaWindow, windowRecord)) {
        printf("\nPTB-ERROR[Cocoa OpenGL setup failed]: Setup failed for unknown reasons.\n\n");
        PsychCocoaDisposeWindow(windowRecord);
        return(FALSE);
    }

    // Check for output display rotation enabled. Will impair timing/timestamping because
    // it uses the desktop compositor for a rotated copy blit, instead of via rotated crtc
    // scanout, as most crtc's don't support this in hardware:
    if ((((int) CGDisplayRotation(cgDisplayID)) != 0) && (PsychPrefStateGet_Verbosity() > 1)) {
        printf("PTB-WARNING: Your onscreen windows output display has rotation enabled. It is not displaying in upright orientation.\n");
        printf("PTB-WARNING: On most graphics cards this will cause unreliable stimulus presentation timing and timestamping.\n");
        printf("PTB-WARNING: If you want non-upright stimulus presentation, look at 'help PsychImaging' on how to achieve this in\n");
        printf("PTB-WARNING: a way that doesn't impair timing. The subfunctions 'FlipHorizontal' and 'FlipVertical' are what you probably need.\n");
    }

    // First reference to this screen by a window?
    if (screenRefCount[screenSettings->screenNumber] == 0) {
        // High precision timestamping enabled? If so, we need to setup the fallback
        // timestamping methods in case beamposition timestamping doesn't work:
        if (PsychPrefStateGet_VBLTimestampingMode() > 0) {
            // Use of CoreVideo is needed on 10.7 and later due to brokeness of the old method (thanks Apple!):
            if (PsychPrefStateGet_Verbosity() > 2) {
                printf("PTB-INFO: Will use fragile CoreVideo timestamping as fallback if beamposition timestamping doesn't work.\n");
                // Recommend use of kernel driver if it isn't installed already for all but Intel GPU's:
                if (!PsychOSIsKernelDriverAvailable(screenSettings->screenNumber) && !strstr((char*) glGetString(GL_VENDOR), "Intel")) {
                    printf("PTB-INFO: Installation of the PsychtoolboxKernelDriver is strongly recommended if you care about precise visual\n");
                    printf("PTB-INFO: onset timestamping or timing. See 'help PsychtoolboxKernelDriver' for installation instructions.\n");
                }
            }

            if (NULL == cvDisplayLink[screenSettings->screenNumber]) {
                // CoreVideo timestamping:
                //
                // Create and start a CVDisplayLink for this screen.
                if (kCVReturnSuccess != CVDisplayLinkCreateWithCGDisplay(cgDisplayID, &cvDisplayLink[screenSettings->screenNumber])) {
                    if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to create CVDisplayLink for screenId %i. This may impair VBL timestamping.\n", screenSettings->screenNumber);
                } else {
                    // Assign dummy output callback, as this is mandatory to get the link up and running:
                    CVDisplayLinkSetOutputCallback(cvDisplayLink[screenSettings->screenNumber], &PsychCVDisplayLinkOutputCallback, (void*) (long int) screenSettings->screenNumber);

                    // Setup shared data structure and mutex:
                    memset(&cvDisplayLinkData[screenSettings->screenNumber], 0, sizeof(cvDisplayLinkData[screenSettings->screenNumber]));
                    PsychInitMutex(&(cvDisplayLinkData[screenSettings->screenNumber].mutex));

                    // Start the link:
                    if (kCVReturnSuccess != CVDisplayLinkStart(cvDisplayLink[screenSettings->screenNumber])) {
                        // Failed to start: Release it again and report error:
                        CVDisplayLinkRelease(cvDisplayLink[screenSettings->screenNumber]);
                        cvDisplayLink[screenSettings->screenNumber] = NULL;

                        // Teardown shared data structure and mutex:
                        PsychDestroyMutex(&(cvDisplayLinkData[screenSettings->screenNumber].mutex));
                        if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to start CVDisplayLink for screenId %i. This may impair VBL timestamping.\n", screenSettings->screenNumber);
                    }
                    else {
                        // Display link started: Report some stuff for the fun of it...
                        if (PsychPrefStateGet_Verbosity() > 3) {
                            // Wait for 50 msecs before query of video refresh from display link to give it a chance to start up:
                            PsychWaitIntervalSeconds(0.050);

                            printf("PTB-INFO: CVDisplayLink for screen %i created to work around the brokenness of Apple Mac OS/X 10.7 and later:\n", screenSettings->screenNumber);
                            printf("PTB-INFO: Video refresh interval as measured by CoreVideo display link: %f msecs.\n", (float) CVDisplayLinkGetActualOutputVideoRefreshPeriod(cvDisplayLink[screenSettings->screenNumber]) * 1000.0);
                            CVTime outLatency = CVDisplayLinkGetOutputVideoLatency(cvDisplayLink[screenSettings->screenNumber]);
                            printf("PTB-INFO: Video display output delay as reported by CoreVideo display link: %f msecs.\n", screenSettings->screenNumber, (float) (((double) outLatency.timeValue / (double) outLatency.timeScale) * 1000.0));
                        }
                    }
                }
            }
        }
        else {
            // VBLtimestampingmode 0 or -1 -- No CoreVideo fallback for timestamping if beamposition timestamping is unavailable:
            // This is the new default as of Psychtoolbox 3.0.12 to avoid the buggy, crashy, unreliably CoreVideo fallback.

            // Recommend use of kernel driver if it isn't installed already for all but Intel GPU's:
            if (!PsychOSIsKernelDriverAvailable(screenSettings->screenNumber) && !strstr((char*) glGetString(GL_VENDOR), "Intel")) {
                printf("PTB-INFO: Installation of the PsychtoolboxKernelDriver is strongly recommended if you care about precise visual\n");
                printf("PTB-INFO: onset timestamping or timing. See 'help PsychtoolboxKernelDriver' for installation instructions.\n");
            }
        }
    }

    // Retain reference of this window to its screen:
    screenRefCount[screenSettings->screenNumber]++;

    // Done.
    return(TRUE);
}
PsychError SCREENGetMouseHelper(void) 
{
#if PSYCH_SYSTEM == PSYCH_OSX
	Point		mouseXY;
	UInt32		buttonState;
	double		numButtons, *buttonArray;
	int		i;
	boolean		doButtonArray;
	
	
	//all subfunctions should have these two lines.  
	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//cap the numbers of inputs and outputs
	PsychErrorExit(PsychCapNumInputArgs(1));   //The maximum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(3));  //The maximum number of outputs
	
	//Buttons.  
	// The only way I know to detect the  number number of mouse buttons is directly via HID.  The device reports
	//that information but OS X seems to ignore it above the level of the HID driver, that is, no OS X API above the HID driver
	//exposes it.  So GetMouse.m function calls PsychHID detect the number of buttons and then passes that value to GetMouseHelper 
	//which returns that number of button values in a vector.      
	PsychCopyInDoubleArg(1, kPsychArgRequired, &numButtons);
	if(numButtons > 32)
		PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "numButtons must not exceed 32");
	if(numButtons < 1) 
		PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "numButtons must exceed 1");
	doButtonArray=PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)numButtons, (int)1, &buttonArray);
	if(doButtonArray){
		buttonState=GetCurrentButtonState();
		for(i=0;i<numButtons;i++)
			buttonArray[i]=(double)(buttonState & (1<<i));
	}
			
	//cursor position
	GetMouse(&mouseXY);
	PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)mouseXY.h);
	PsychCopyOutDoubleArg(2, kPsychArgOptional, (double)mouseXY.v);
#endif

#if PSYCH_SYSTEM == PSYCH_WINDOWS
	static unsigned char disabledKeys[256];
	static unsigned char firsttime = 1;
	int keysdown, i;
	unsigned char keyState[256];
	double* buttonArray;
	double numButtons, timestamp;
	PsychNativeBooleanType* buttonStates;
	POINT		point;
	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

	// Retrieve optional number of mouse buttons:
	numButtons = 0;
	PsychCopyInDoubleArg(1, FALSE, &numButtons);

	// Are we operating in 'GetMouseHelper' mode? numButtons>=0 indicates this.
	if (numButtons>=0) {
		// GetMouse-Mode: Return mouse button states and mouse cursor position:

		PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)3, (int)1, &buttonArray);
		// Query and return mouse button state:
		PsychGetMouseButtonState(buttonArray);
		// Query and return cursor position in global coordinates:
		GetCursorPos(&point);
		PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) point.x);
		PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) point.y);
	}
	else {
	  // 'KeyboardHelper' mode: We implement either KbCheck() or KbWait() via X11.
	  // This is a hack to provide keyboard queries until a PsychHID() implementation
	  // for Microsoft Windows is available...

	  if (firsttime) {
			// First time init:
			firsttime = 0;
			memset(keyState, 0, sizeof(keyState));
			memset(disabledKeys, 0, sizeof(disabledKeys));
			// These keycodes are always disabled: 0, 255:
			disabledKeys[0]=1;
			disabledKeys[255]=1;
			// Mouse buttone (left, right, middle) are also disabled by default:
			disabledKeys[1]=1;
			disabledKeys[2]=1;
			disabledKeys[4]=1;
	  }

	  if (numButtons==-1 || numButtons==-2) {
	    // KbCheck()/KbWait() mode
	    do {
	      // Reset overall key state to "none pressed":
	      keysdown=0;

	      // Request current time of query:
	      PsychGetAdjustedPrecisionTimerSeconds(&timestamp);

			// Query state of all keys:
			for(i=1;i<255;i++){
				keyState[i] = (GetAsyncKeyState(i) & -32768) ? 1 : 0;
			}

	      // Disable all keys that are registered in disabledKeys. Check if
			// any non-disabled key is down.
	      for (i=0; i<256; i++) {
				if (disabledKeys[i]>0) keyState[i] = 0;
				keysdown+=(unsigned int) keyState[i];
	      }

	      // We repeat until any key pressed if in KbWait() mode, otherwise we
	      // exit the loop after first iteration in KbCheck mode.
	      if ((numButtons==-1) || ((numButtons==-2) && (keysdown>0))) break;

	      // Sleep for a millisecond before next KbWait loop iteration:
	      PsychWaitIntervalSeconds(0.001);

	    } while(1);

	    if (numButtons==-2) {
	      // KbWait mode: Copy out time value.
	      PsychCopyOutDoubleArg(1, kPsychArgOptional, timestamp);
	    }
	    else {
	      // KbCheck mode:
	      
	      // Copy out overall keystate:
	      PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown>0) ? 1 : 0);

	      // Copy out timestamp:
	      PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp);	      

	      // Copy out keyboard state:
	      PsychAllocOutBooleanMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates);

	      // Build 256 elements return vector:
	      for(i=0; i<255; i++) {
		  		buttonStates[i] = (PsychNativeBooleanType)((keyState[i+1]) ? 1 : 0);
	      }
			// Special case: Null out last element:
			buttonStates[255] = (PsychNativeBooleanType) 0;
	    }
	  }
	}
#endif
	
#if PSYCH_SYSTEM == PSYCH_LINUX
	unsigned char keys_return[32];
	char* keystring;
	PsychGenericScriptType *kbNames;
	CGDirectDisplayID dpy;
	Window rootwin, childwin;
	int i, j, mx, my, dx, dy;
	unsigned int mask_return;
	double numButtons, timestamp;
	double* buttonArray;
	PsychNativeBooleanType* buttonStates;
	int keysdown;
	XEvent event_return;
	XKeyPressedEvent keypressevent;
	int screenNumber;

	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

	PsychCopyInDoubleArg(1, kPsychArgRequired, &numButtons);

	// Retrieve optional screenNumber argument:
	screenNumber = 0;
	PsychCopyInScreenNumberArg(2, FALSE, &screenNumber);

	// Map screenNumber to X11 display handle and screenid:
	PsychGetCGDisplayIDFromScreenNumber(&dpy, screenNumber);

	// Are we operating in 'GetMouseHelper' mode? numButtons>=0 indicates this.
	if (numButtons>=0) {
	  // Mouse pointer query mode:
	  XQueryPointer(dpy, RootWindow(dpy, PsychGetXScreenIdForScreen(screenNumber)), &rootwin, &childwin, &mx, &my, &dx, &dy, &mask_return);
	  
	  // Copy out mouse x and y position:
	  PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) mx);
	  PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) my);
	  
	  // Copy out mouse button state:
	  PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)numButtons, (int)1, &buttonArray);

	  // Bits 8, 9 and 10 of mask_return seem to correspond to mouse buttons
	  // 1, 2 and 3 of a mouse for some weird reason. Bits 0-7 describe keyboard modifier keys
	  // like Alt, Ctrl, Shift, ScrollLock, NumLock, CapsLock...
	  // We remap here, so the first three returned entries correspond to the mouse buttons and
	  // the rest is attached behind, if requested...
	  
	  // Mouse buttons: Left, Middle, Right == 0, 1, 2, aka 1,2,3 in Matlab space...
	  for (i=0; i<numButtons && i<3; i++) {
	    buttonArray[i] = (mask_return & (1<<(i+8))) ? 1 : 0; 
	  }
	  // Modifier keys 0 to 7 appended:
	  for (i=3; i<numButtons && i<3+8; i++) {
	    buttonArray[i] = (mask_return & (1<<(i-3))) ? 1 : 0; 
	  }
	  // Everything else appended:
	  for (i=11; i<numButtons; i++) {
	    buttonArray[i] = (mask_return & (1<<i)) ? 1 : 0; 
	  }
	}
	else {
	  // 'KeyboardHelper' mode: We implement either KbCheck() or KbWait() via X11.
	  // This is a hack to provide keyboard queries until a PsychHID() implementation
	  // for Linux is available...

	  if (numButtons==-1 || numButtons==-2) {
	    // KbCheck()/KbWait() mode:

	    // Switch X-Server into synchronous mode: We need this to get
	    // a higher timing precision.
	    XSynchronize(dpy, TRUE);

	    do {
	      // Reset overall key state to "none pressed":
	      keysdown=0;

	      // Request current keyboard state from X-Server:
	      XQueryKeymap(dpy, keys_return);

	      // Request current time of query:
	      PsychGetAdjustedPrecisionTimerSeconds(&timestamp);

	      // Any key down?
	      for (i=0; i<32; i++) keysdown+=(unsigned int) keys_return[i];
	      
	      // We repeat until any key pressed if in KbWait() mode, otherwise we
	      // exit the loop after first iteration in KbCheck mode.
	      if ((numButtons==-1) || ((numButtons==-2) && (keysdown>0))) break;

	      // Sleep for a few milliseconds before next KbWait loop iteration:
	      PsychWaitIntervalSeconds(0.01);
	    } while(1);

	    if (numButtons==-2) {
	      // Copy out time:
	      PsychCopyOutDoubleArg(1, kPsychArgOptional, timestamp);
	    }
	    else {
	      // KbCheck mode:
	      
	      // Copy out overall keystate:
	      PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown>0) ? 1 : 0);
	      // copy out timestamp:
	      PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp);	      
	      // Copy keyboard state:
	      PsychAllocOutBooleanMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates);

	      // Map 32 times 8 bitvector to 256 element return vector:
	      for(i=0; i<32; i++) {
				for(j=0; j<8; j++) {
		  			buttonStates[i*8 + j] = (PsychNativeBooleanType)(keys_return[i] & (1<<j)) ? 1 : 0;
				}
	      }
	    }
	  }
	  else if (numButtons == -3) {
	    // numButtons == -3 --> KbName mapping mode:
	    // Return the full keyboard keycode to ASCII character code mapping table...
	    PsychAllocOutCellVector(1, kPsychArgOptional, 256, &kbNames);

	    for(i=0; i<256; i++) {
	      // Map keyboard scan code to KeySym:
	      keystring = XKeysymToString(XKeycodeToKeysym(dpy, i, 0));
	      if (keystring) {
		// Character found: Return its ASCII name string:
		PsychSetCellVectorStringElement(i, keystring, kbNames);
	      }
	      else {
		// No character for this keycode:
		PsychSetCellVectorStringElement(i, "", kbNames);
	      }
	    }
	  }
	  else if (numButtons == -4) {
	    // GetChar() emulation.

/* 	    do { */
/* 	      // Fetch next keypress event from queue, block if none is available... */
/* 	      keystring = NULL; */
/* 	      XNextEvent(dpy, &event_return); */
/* 	      // Check for valid keypress event and extract character: */
/* 	      if (event_return.type == KeyPress) { */
/* 		keypressevent = (XKeyPressedEvent) event_return; */
/* 		keystring = NULL; */
/* 		keystring = XKeysymToString(XKeycodeToKeysym(dpy, keypressevent.keycode, 0)); */
/* 	      } */
/* 	      // Repeat until a valid char is returned. */
/* 	    } while (keystring == NULL); */

/* 	    // Copy out character: */
/* 	    PsychCopyOutCharArg(1, kPsychArgOptional, (char) keystring); */
/* 	    // Copy out time: */
/* 	    PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) keypressevent.time); */
	  }
	}
#endif
	return(PsychError_none);	
}