diff --git a/PsychSourceGL/Source/Common/Eyelink/EyelinkShutdown.c b/PsychSourceGL/Source/Common/Eyelink/EyelinkShutdown.c index beb6b54a45..2a6f940fdf 100644 --- a/PsychSourceGL/Source/Common/Eyelink/EyelinkShutdown.c +++ b/PsychSourceGL/Source/Common/Eyelink/EyelinkShutdown.c @@ -1,37 +1,33 @@ /* - /osxptb/trunk/PsychSourceGL/Source/OSX/Eyelink/EyelinkShutdown.c - - PROJECTS: Eyelink - + /PsychSourceGL/Source/Common/Eyelink/EyelinkShutdown.c + + PROJECTS: Eyelink + AUTHORS: - cburns@berkeley.edu cdb - E.Peters@ai.rug.nl emp - f.w.cornelissen@med.rug.nl fwc - - PLATFORMS: Currently only OS X - + cburns@berkeley.edu cdb + E.Peters@ai.rug.nl emp + f.w.cornelissen@med.rug.nl fwc + + PLATFORMS: All. + HISTORY: 11/23/05 cdb Created. - TARGET LOCATION: - - Eyelink.mexmac resides in: - EyelinkToolbox */ #include "PsychEyelink.h" PsychError PsychEyelinkShutdown(void) { - int iStatus = -1; - char strMsg[256]; + int iStatus = -1; + char strMsg[256]; if (giSystemInitialized) { // Zero-out return string: memset(strMsg, 0, sizeof(strMsg)); - + // Disconnect if connected if (eyelink_is_connected()) { set_offline_mode(); @@ -41,16 +37,16 @@ PsychError PsychEyelinkShutdown(void) PsychErrorExitMsg(PsychError_internal, strMsg); } } - + // Detach all callback hook functions: PsychEyelink_uninit_core_graphics(); - + // Close down eyelink and reset global flag close_eyelink_system(); msec_delay(100); giSystemInitialized = 0; } - + return(PsychError_none); } @@ -62,7 +58,7 @@ PsychError EyelinkShutdown(void) // Add help strings PsychPushHelp(useString, synopsisString, seeAlsoString); - + // Output help if asked if(PsychIsGiveHelp()) { PsychGiveHelp(); @@ -73,7 +69,7 @@ PsychError EyelinkShutdown(void) PsychErrorExit(PsychCapNumInputArgs(0)); PsychErrorExit(PsychRequireNumInputArgs(0)); PsychErrorExit(PsychCapNumOutputArgs(0)); - + // Call actual shutdown routine: PsychEyelinkShutdown(); diff --git a/PsychSourceGL/Source/Common/Eyelink/PsychEyelink.c b/PsychSourceGL/Source/Common/Eyelink/PsychEyelink.c index b970414d6d..a478cd6dd7 100644 --- a/PsychSourceGL/Source/Common/Eyelink/PsychEyelink.c +++ b/PsychSourceGL/Source/Common/Eyelink/PsychEyelink.c @@ -1,1551 +1,1554 @@ -/* - - /osxptb/trunk/PsychSourceGL/Source/Common/Eyelink/PsychEyelink.c - - PROJECTS: Eyelink - - AUTHORS: - - cburns@berkeley.edu cdb - E.Peters@ai.rug.nl emp - f.w.cornelissen@med.rug.nl fwc - mario.kleiner@tuebingen.mpg.de mk - li@sr-research.com lj - - PLATFORMS: All. - - HISTORY: - - 11/22/05 cdb Created. - 29/06/06 fwc fixed EyelinkSystemIsConnected to allow dummy mode connections - 15/03/09 mk Added experimental support for eye camera image display. - 12/20/13 lj fixed PsychEyelinkParseToString to allow space between % ; - modified getMouseState to limit mouse cursor inside of camera image. - - TARGET LOCATION: - - Eyelink.mexmac resides in: - EyelinkToolbox -*/ - -#include "PsychEyelink.h" -#include - -///////////////////////////////////////////////////////////////////////// -// Global variables used throughout eyelink C files - -int giSystemInitialized = 0; -int verbosity = 2; - -// Callback string for eyelink display callback function: -char eyelinkDisplayCallbackFunc[1024]; - -// Memory pointer to malloc()'ed image pixel buffer that holds the -// image data for a RGBA8 texture with the most recent eye camera image: -static byte* eyeimage = NULL; - -// Width x Height of eye camera image in pixels: -static int eyewidth = 0; -static int eyeheight = 0; - -// Color remapping palette table: -static unsigned int palmap32[256]; -#define ERR_BUFF_LEN 1000 - -/* Declaration of callback functions defined later in this file: */ -static INT16 ELCALLBACK PsychEyelink_setup_image_display(INT16 width, INT16 height); -static void ELCALLBACK PsychEyelink_exit_image_display(void); -static void ELCALLBACK PsychEyelink_set_image_palette(INT16 ncolors, byte r[130], byte g[130], byte b[130]); -static void ELCALLBACK PsychEyelink_draw_image_line(INT16 width, INT16 line, INT16 totlines, byte *pixels); - -static INT16 ELCALLBACK PsychEyelink_setup_cal_display(void); -static void ELCALLBACK PsychEyelink_exit_cal_display(void); -static void ELCALLBACK PsychEyelink_clear_display(void); -static void ELCALLBACK PsychEyelink_draw_cal_target(INT16 x, INT16 y); -static void ELCALLBACK PsychEyelink_erase_cal_target(void); -static void ELCALLBACK PsychEyelink_image_title(INT16 threshold, char *title); -static INT16 ELCALLBACK PsychEyelink_get_input_key(InputEvent *keyinput); -static void ELCALLBACK PsychEyelink_alert_printf_hook(const char *msg); -static void ELCALLBACK PsychEyelink_noop(void); - -static void ELCALLBACK PsychEyelink_cal_target_beep_hook(void); -static void ELCALLBACK PsychEyelink_cal_done_beep_hook(INT16 error); -static void ELCALLBACK PsychEyelink_dc_done_beep_hook(INT16 error); -static void ELCALLBACK PsychEyelink_dc_target_beep_hook(void); - -///////////////////////////////////////////////////////////////////////// -// Check if system is initialized -// -PsychError EyelinkSystemIsConnected(void) -{ - int iStatus=-9999; - iStatus=eyelink_is_connected(); -// mexPrintf("EyelinkSystemIsConnected status %d ((iStatus==0)=%d)\n", iStatus, (iStatus==0) ); - if (iStatus==0) { - PsychErrorExitMsg(PsychError_user, "Eyelink system is not connected!\n"); - } -/* - if (eyelink_is_connected()==0) { - PsychErrorExitMsg(PsychError_user, "Eyelink system is not connected!\n"); - } - */ - return(PsychError_none); -} - -///////////////////////////////////////////////////////////////////////// -// Check is system is initialized -// -PsychError EyelinkSystemIsInitialized(void) -{ - if (giSystemInitialized != 1) { - PsychErrorExitMsg(PsychError_user, "Eyelink system is not initialized!\n"); - } - return(PsychError_none); -} - -/* Eyelink('Verbosity') - Set level of verbosity. - */ -PsychError EyelinkVerbosity(void) -{ - static char useString[] = "oldlevel = Eyelink('Verbosity' [,level]);"; - static char synopsisString[] = - "Set level of verbosity for error/warning/status messages. 'level' optional, new level " - "of verbosity. 'oldlevel' is the old level of verbosity. The following levels are " - "supported: 0 = Shut up. 1 = Print errors, 2 = Print also warnings, 3 = Print also some info, " - "4 = Print more useful info (default), >5 = Be very verbose (mostly for debugging the driver itself). "; - static char seeAlsoString[] = " "; - - int level= -1; - - // Setup online help: - PsychPushHelp(useString, synopsisString, seeAlsoString); - if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none); }; - - PsychErrorExit(PsychCapNumInputArgs(1)); // The maximum number of inputs - PsychErrorExit(PsychRequireNumInputArgs(0)); // The required number of inputs - PsychErrorExit(PsychCapNumOutputArgs(1)); // The maximum number of outputs - - PsychCopyInIntegerArg(1, kPsychArgOptional, &level); - if (level < -1) PsychErrorExitMsg(PsychError_user, "Invalid level of verbosity provided. Valid are levels of zero and greater."); - - // Return current/old level: - PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) verbosity); - - // Set new level, if one was provided: - if (level > -1) verbosity = level; - - return(PsychError_none); -} - -// Return level of verbosity: -int Verbosity(void) { - return(verbosity); -} - -// Parse printf() style format string and variable number of -// integer or string arguments into a printf() formatted -// string and return static pointer to the final string. -// Used, e.g., by Eyelink('Command') and Eyelink('Message'): -const char* PsychEyelinkParseToString(int startIdx) -{ - static char strCommand[256]; - int i = 0, j=0; - int iNumInArgs = 0; - PsychArgFormatType psychArgType = PsychArgType_none; - int iTempValue = 0; - char *pstrTemp = NULL; - char *pstrFormat = NULL; - char strFragment[256]; - char fSpec[256]; - int wIdx = 0; - int argIdx; - - // Alloc and grab the input format string - PsychAllocInCharArg(startIdx, TRUE, &pstrFormat); - iNumInArgs = PsychGetNumInputArgs(); - - // Define start index of variable argument list: - argIdx = startIdx + 1; - - // Clear strings - memset(strCommand, 0, sizeof(strCommand)); - - // Parse complete format string: - while ((*pstrFormat != 0) && (wIdx < 255)) { - // Special character % detected? - if ((*pstrFormat != '%') || (*(pstrFormat+1) == '%')) { - // Easy: Regular char or escaped %. Just copy into target command string: - - // Eat up the escape '%' character, if any: - if (pstrFormat == strstr(pstrFormat, "%%")) pstrFormat++; - - // Copy escaped single % or regular character: - strCommand[wIdx++] = *(pstrFormat++); - - // Next character... - continue; - } - - // Special % char detected, which is not escaped, therefore - // a datatype format specifier follows immediately: - - // Is there an argument available to match the format string spec? - if (iNumInArgs < argIdx) { - PsychErrorExitMsg(PsychError_user, "Number of supplied arguments does not match number of arguments required by format string!"); - } - - // Find end of actual parameter spec: - for (i = 0; (pstrFormat[i] > 0) && (pstrFormat[i] != '%'); i++) {}; - for (j = i+1; (pstrFormat[j] > 0) && (pstrFormat[j] != ' ') && (pstrFormat[j]!='%'); j++) {}; - - // Copy format substring to fSpec: - memset(fSpec, 0, sizeof(fSpec)); - strncpy(fSpec, pstrFormat, ((j-i) < 256) ? (j-i) : 255); - - // Prepare output substring for writing: - memset(strFragment, 0, sizeof(strFragment)); - - // Check if input argument type matches parameter spec string - // and assign, if so, abort otherwise: - psychArgType = PsychGetArgType(argIdx); - switch(psychArgType) { - case PsychArgType_double: - if ((PsychGetArgM(argIdx) == 1) && (PsychGetArgN(argIdx) == 1)) { - PsychCopyInIntegerArg(argIdx, TRUE, &iTempValue); - - // Got a int value. Was a int value expected? - if (strstr(fSpec, "d") || strstr(fSpec, "i")) { - // Yes: Print into output string fragment: - snprintf(strFragment, 255, fSpec, iTempValue); - } else { - // No: This is a mismatch - Game over: - PsychErrorExitMsg(PsychError_user, "Mismatch between provided scalar integer argument and expected argument!"); - } - } else { - PsychGiveHelp(); - PsychErrorExitMsg(PsychError_user, ""); - } - break; - - case PsychArgType_char: - PsychAllocInCharArg(argIdx, TRUE, &pstrTemp); - // Got a string. Was a string expected? - if (strstr(fSpec, "s")) { - // Yes: Print into output string fragment: - snprintf(strFragment, 255, fSpec, pstrTemp); - } else { - // No: This is a mismatch - Game over: - PsychErrorExitMsg(PsychError_user, "Mismatch between provided character string and expected argument!"); - } - break; - - default: - PsychGiveHelp(); - PsychErrorExitMsg(PsychError_user, ""); - break; - } - - // If we made it here, then the strFragment is ready for - // joining: - if ((strlen(strCommand) + strlen(strFragment)) < 256) { - strcat(strCommand, strFragment); - } else { - // Break out of parser - Need to truncate: - break; - } - - // Advance parse positions: - wIdx = strlen(strCommand); - pstrFormat += j; - argIdx++; - - // Next parse iteration. - } - - // Sanity check: - if (*pstrFormat != 0) printf("Eyelink-Warning:Final overall command truncated to '%s'!\nMaximum of 255 characters allowed.\n", strCommand); - - // Return pointer to internally statically allocated final character string: - return(strCommand); -} - -// Initialize all callback hook functions for use by Eyelink runtime, e.g., -// all the callbacks for eye camera image display: -void PsychEyelink_init_core_graphics(const char* callback) -{ - HOOKFCNS fcns; - - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_init_core_graphics()\n"); - - memset(&fcns, 0, sizeof(fcns)); - - // Setup cam image callbacks: - fcns.setup_image_display_hook = PsychEyelink_setup_image_display; - fcns.exit_image_display_hook= PsychEyelink_exit_image_display; - fcns.set_image_palette_hook = PsychEyelink_set_image_palette; - fcns.image_title_hook = PsychEyelink_image_title; - fcns.draw_image_line_hook = PsychEyelink_draw_image_line; - - // Setup calibration callbacks: - fcns.setup_cal_display_hook = PsychEyelink_setup_cal_display; - fcns.exit_cal_display_hook = PsychEyelink_exit_cal_display; - fcns.clear_cal_display_hook = PsychEyelink_clear_display; - fcns.draw_cal_target_hook = PsychEyelink_draw_cal_target; - fcns.erase_cal_target_hook = PsychEyelink_erase_cal_target; - - // Setup keyboard and I/O callbacks: - fcns.get_input_key_hook = PsychEyelink_get_input_key; - fcns.alert_printf_hook = PsychEyelink_alert_printf_hook; - - // Set auditory feedback callbacks: - fcns.cal_target_beep_hook = PsychEyelink_cal_target_beep_hook; - fcns.cal_done_beep_hook = PsychEyelink_cal_done_beep_hook; - fcns.dc_target_beep_hook = PsychEyelink_dc_target_beep_hook; - fcns.dc_done_beep_hook = PsychEyelink_dc_done_beep_hook; - - // Just set this to make eyelink-core happy: - fcns.record_abort_hide_hook = PsychEyelink_noop; - - // Assign runtime environment display callback function: - memset(eyelinkDisplayCallbackFunc, 0, sizeof(eyelinkDisplayCallbackFunc)); - - snprintf(eyelinkDisplayCallbackFunc, sizeof(eyelinkDisplayCallbackFunc) - 1, "%s", callback); -// #if PSYCH_SYSTEM != PSYCH_WINDOWS -// snprintf(eyelinkDisplayCallbackFunc, sizeof(eyelinkDisplayCallbackFunc) - 1, "%s", callback); -// #else -// _snprintf(eyelinkDisplayCallbackFunc, sizeof(eyelinkDisplayCallbackFunc) - 1, "%s", callback); -// #endif - - // Assign hooks to Eyelink runtime: - setup_graphic_hook_functions(&fcns); - - // Optionally dump the whole hookfunctions struct: - if (Verbosity() > 5) PsychEyelink_dumpHookfunctions(); - - return; -} - -// Disable all hook functions at shutdown time: -void PsychEyelink_uninit_core_graphics(void) -{ - HOOKFCNS fcns; - - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_uninit_core_graphics()\n"); - - memset(&fcns, 0, sizeof(fcns)); - setup_graphic_hook_functions(&fcns); - - // Optionally dump the whole hookfunctions struct: - if (Verbosity() > 5) PsychEyelink_dumpHookfunctions(); - - return; -} - -void PsychEyelink_dumpHookfunctions(void) -{ - HOOKFCNS* pfcns = get_all_hook_functions(); - int i; - - printf("PsychEyelink: Dump of current Eyelink HOOKFCNS struct as byte array:\n\n"); - for (i=0; i < sizeof(HOOKFCNS); i++) printf(" %02x", (int)(((unsigned char*) pfcns)[i])); - printf("\nPsychEyelink: Dump done\n\n"); - - return; -} - -void PsychEyelink_TestEyeImage(void) -{ - int i, x, y; - byte r[256], g[256], b[256]; - byte scanline[640]; - InputEvent keyinput; - - // Pseudo-Eyelink camera image test: - - // Setup pseudo eye-display of 640 x 480 pixels via setup callback: - PsychEyelink_setup_image_display(640, 480); - - // Build pseudo color LUT: - for (i=0; i < 256; i++) { - r[i]=i; - g[i]=255 - i; - b[i]=i * 2; - } - PsychEyelink_set_image_palette(256, r, g, b); - - // Set image title: - PsychEyelink_image_title(1, "Foobar-O-Matic:"); - - // Calibration beep: - PsychEyelink_cal_target_beep_hook(); - - // Run pseudo-display loop for 600 frames: - for (i = 0; i < 600; i++) { - // Draw calibration target: - PsychEyelink_draw_cal_target(i, 200); - - // Fill buffer with image pattern: - for (y=1; y <= 480; y++) { - // Build y'th scanline: - for (x=0; x < 640; x++) scanline[x] = (byte) ((x + y + i) % 256); - - // Submit y'th scanline: - PsychEyelink_draw_image_line(640, y, 480, (byte*) &scanline); - } - - // Check keyboard: - keyinput.key.key = 0; - if (PsychEyelink_get_input_key(&keyinput) > 0) { - PsychEyelink_alert_printf_hook("Eyelink: Key detected.\n"); - // Break out of loop on keycode 41 or 27 == ESCAPE on OS/X or Windows. - if (keyinput.key.key == 41 || keyinput.key.key == 27) break; - if (keyinput.key.key == TERMINATE_KEY) { - printf("Eyelink: TestSuite: WARNING: Abort code detected. Master abort.\n"); - break; - } - } - } - - // Calibration end beep hook: - PsychEyelink_cal_done_beep_hook(keyinput.key.key); - - // Tear down pseudo display: - PsychEyelink_exit_image_display(); - - // Test calibration display: - PsychEyelink_setup_cal_display(); - PsychEyelink_dc_target_beep_hook(); - PsychEyelink_clear_display(); - PsychEyelink_dc_done_beep_hook(keyinput.key.key); - - PsychEyelink_exit_cal_display(); - - return; -} - -int PsychEyelinkCallRuntime(int cmd, int x, int y, char* msg) -{ - PsychGenericScriptType *inputs[2]; - PsychGenericScriptType *outputs[1]; - double* callargs; - double rc; - int retc; - - // Callbacks forcefully disabled by error-handling? Return with error code if so: - if (0 == eyelinkDisplayCallbackFunc[0]) return(0xdeadbeef); - - // Create a Matlab double matrix with 4 elements: 1st is command code - // others are available for use specific to each command - outputs[0] = NULL; - inputs[0] = mxCreateDoubleMatrix(1, 4, mxREAL); - callargs = mxGetPr(inputs[0]); - - callargs[0] = (double) cmd; // Command code. - callargs[1] = (double) x; - callargs[2] = (double) y; - - if (msg != NULL) { - inputs[1] = mxCreateString(msg); - } - else { - inputs[1] = NULL; - } - - // Call the runtime environment: - if ((retc = Psych_mexCallMATLAB((cmd == 2) ? 1 : 0, outputs, (inputs[1]) ? 2 : 1, inputs, eyelinkDisplayCallbackFunc)) > 0) { - printf("EYELINK: WARNING! PsychEyelinkCallRuntime() Failed to call eyelink runtime callback function %s [rc = %i]!\n", eyelinkDisplayCallbackFunc, retc); - printf("EYELINK: WARNING! Make sure that function is on your Matlab/Octave path and properly initialized.\n"); - printf("EYELINK: WARNING! May also be an error during execution of that function. Type ple at command prompt for error messages.\n"); - printf("EYELINK: WARNING! Auto-Disabling all callbacks to the runtime environment for safety reasons.\n"); - eyelinkDisplayCallbackFunc[0] = 0; - } - - // Release our matrix again: - mxDestroyArray(inputs[0]); - if (msg != NULL) mxDestroyArray(inputs[1]); - - if (outputs[0]) { - rc = mxGetScalar(outputs[0]); - mxDestroyArray(outputs[0]); - } - else { - rc = 0; - } - - return((int) rc); -} - -// Callback functions, called by Eyelink runtime at various occassions, e.g, -// during tracker setup, drift correction/calibration etc.: -// ========================================================================= - -static void ELCALLBACK PsychEyelink_noop(void) -{ - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_noop()\n"); - - // Done. - return; -} - -// PsychEyelink_setup_image_display() tells the width and height of the camera -// image in pixels. -static INT16 ELCALLBACK PsychEyelink_setup_image_display(INT16 width, INT16 height) -{ - - - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_setup_image_display()\n"); - // Release any stale image buffer: - if (eyeimage != NULL) free(eyeimage); - - // Reset everything to startup default: - eyeimage = NULL; - eyewidth = 0; - eyeheight = 0; - - if (width < 1 || height < 1) { - printf("EYELINK: WARNING! Invalid image dimensions (smaller than 1 pixel!) received from eyelink: Aborting image setup.\n"); - return(-1); - } - - // Allocate an internal memory buffer of sufficient size to hold an image - // of size width x height pixels: - eyeimage = (byte*) malloc(sizeof(unsigned char) * 4 * width * height); - if (eyeimage != NULL) { - eyewidth = width; - eyeheight = height; - } - else { - // Failed: - return(-1); - } - - // Tell callback about image dimensions fwiw: - if (0xdeadbeef == PsychEyelinkCallRuntime(8, eyewidth, eyeheight, NULL)) { - // Error condition. Return error to eyelink runtime: - return(-1); - } - - if (Verbosity() > 5) printf("Eyelink: Leaving PsychEyelink_setup_image_display()\n"); - - // Done. - return(0); -} - -// PsychEyelink_exit_image_display() shuts down any camera image display: -static void ELCALLBACK PsychEyelink_exit_image_display(void) -{ - - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_exit_image_display()\n"); - - // Release any allocated image buffer: - if (eyeimage != NULL) free(eyeimage); - - // Reset everything to startup default: - eyeimage = NULL; - eyewidth = 0; - eyeheight = 0; - - // Tell runtime to exit display: Command code 9. - PsychEyelinkCallRuntime(9, 0, 0, NULL); - - // Done. - return; -} -// added by NJ @ SR Research Sept 2010 -#define UPSIDE 0 -#define LEFTSIDE 1 -#define RIGHTSIDE 2 -#define DOWNSIDE 3 -void drawSemiCircle(CrossHairInfo *chi, int left, int top, int dia, int side, int cindex) -{ - - - - unsigned char r =0; - unsigned char g =0; - unsigned char b =0; - int radius = dia/2; - int x = left - 1; - int y = top -1; - unsigned int *v0; - int x0,y0, ddF_x =1, ddF_y,f; - - - if (eyeimage == NULL){ - return; - } - - switch(cindex) - { - case CR_HAIR_COLOR: r=g=b =255; break;//255,255,255 - case PUPIL_HAIR_COLOR: r=g=b =255; break;//255,255,255 - case PUPIL_BOX_COLOR: g =255; break;//0,255,0 - case SEARCH_LIMIT_BOX_COLOR: - case MOUSE_CURSOR_COLOR: r = 255; break;//255,0,0 - } - - v0 = (unsigned int*) (eyeimage); - - // implement manual clipping to mimic behavior on host pc - - if(side == UPSIDE) - { - - x0 = left; - y0 = top; - radius = dia / 2; - y= radius; - f = 1 - radius; - ddF_y = -2 * radius; - x = 0; - y = radius; - x0 = x0 + dia/2; - y0 = y0 + dia/2; - - - if (y0 < eyeheight && y0 > 0 && y0-radius < eyeheight && y0-radius > 0 && y0-radius < eyeheight && x0+radius < eyewidth && x0-radius >0 && x0+radius > 0 && x0-radius < eyewidth){ - v0[(eyewidth*eyeheight) - (y0-radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - v0[(eyewidth*eyeheight) - y0*eyewidth+(x0+radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - v0[(eyewidth*eyeheight) - y0*eyewidth+(x0-radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - - while( x < y ){ - - if(f >= 0){ - y--; - ddF_y += 2; - f += ddF_y; - - } - x++; - ddF_x += 2; - f += ddF_x; - - - if (y0+y >0 && y0+y < y0 && y0+y < eyeheight) { - - if(x0+x < eyewidth) - v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if (x0-x > 0 ) - v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if (y0-y >0 && y0-y < y0 && y0-y < eyeheight){//if (y0-y < y0 && y0-y < eyeheight){ - - if (x0+x < eyewidth) - v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if (x0-x > 0 ) - v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if (y0+x >0 && y0+x < y0 && y0+x < eyeheight){//if (y0+x < y0 && y0+x < eyeheight){ - - if (x0+y < eyewidth) - v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if((x0-y) >0) - v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if (y0-x > 0 && y0-x < y0 && y0-x < eyeheight){//if (y0-x < y0 && y0-x < eyeheight){ - - if(x0+y < eyewidth) - v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if(x0-y > 0) - v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - } - }else if (side == DOWNSIDE){ - - radius = dia / 2; - y= radius; - f = 1 - radius; - ddF_y = -2 * radius; - ddF_x =1; - x = 0; - y = radius; - - x0 = left; - y0 = top; - x0 = x0 + dia/2; - - if (y0+radius < eyeheight && y0+radius > 0 && x0 > 0 && x0 < eyewidth) - v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if (y0 < eyeheight && y0 > 0 && x0+radius > 0 && x0+radius < eyewidth) - v0[(eyewidth*eyeheight) - y0*eyewidth+(x0+radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if (y0 < eyeheight && y0 > 0 && x0-radius > 0 && x0-radius < eyewidth) - v0[(eyewidth*eyeheight) - y0*eyewidth+(x0-radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - - - while( x < y ){ - - if(f >= 0){ - y--; - ddF_y += 2; - f += ddF_y; - - } - x++; - ddF_x += 2; - f += ddF_x; - - if (y0+y >0 && y0+y > y0 && y0+y < eyeheight) { - if ( x0+x < eyewidth) - v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if ( x0-x > 0 ) - v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if (y0-y > 0 && y0-y > y0 && y0-y < eyeheight){ - if ( x0+x < eyewidth) - v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if ( x0-x > 0 ) - v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if (y0+x >0 && y0+x > y0 && y0+x < eyeheight){ - if ( x0+y < eyewidth ) - v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if ( x0-y > 0) - v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if (y0-x >0 && y0-x > y0 && y0-x < eyeheight){ - if ( x0+y < eyewidth) - v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if (x0-y > 0) - v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - } - - - }else if (side == RIGHTSIDE){ - - radius = dia / 2; - y= radius; - f = 1 - radius; - ddF_y = -2 * radius; - ddF_x =1; - x = 0; - y = radius; - - x0 = left; - y0 = top; - y0 = y0 + dia/2; - - - if (x0 + radius < eyewidth && x0+radius > 0){ - v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - v0[(eyewidth*eyeheight) - y0*eyewidth+(x0+radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - - while( x < y ){ - - if(f >= 0){ - y--; - ddF_y += 2; - f += ddF_y; - - } - x++; - ddF_x += 2; - f += ddF_x; - - if (x0+x >0 && x0+x > x0 && x0+x < eyewidth) { - if(y0+x < eyeheight) - v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if(y0-x > 0) - v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - - } - if (x0-x > 0 && x0-x > x0 && x0-x < eyewidth){ - if(y0+y < eyeheight) - v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if(y0-y > 0) - v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if (x0+y > 0 && x0+y > x0 && x0+y < eyewidth){ - if(y0+x < eyeheight) - v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if(y0-x > 0) - v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if (x0-y > 0 && x0-y > x0 && x0-y < eyewidth){ - if(y0+x < eyeheight) - v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if(y0-x > 0) - v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - } - - }else if (side == LEFTSIDE){ - - radius = dia / 2; - y= radius; - f = 1 - radius; - ddF_y = -2 * radius; - ddF_x =1; - x = 0; - y = radius; - - x0 = left; - y0 = top; - y0 = y0 + dia/2; - x0 = x0 + dia/2; - - if (x0 - radius > 0 && x0-radius < eyewidth){ - v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - v0[(eyewidth*eyeheight) - y0*eyewidth+(x0-radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - - while( x < y ){ - - if(f >= 0){ - y--; - ddF_y += 2; - f += ddF_y; - - } - x++; - ddF_x += 2; - f += ddF_x; - - if (x0+x > 0 && x0+x < x0 && x0+x > 0) { - v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if (x0-x > 0 && x0-x < x0 && x0-x > 0){ - if(y0+x < eyeheight) - v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if(y0-x > 0) - v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if (x0+y > 0 && x0+y < x0 && x0+y > 0){ - if(y0+x < eyeheight) - v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if(y0-x > 0) - v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if (x0-y > 0 && x0-y < x0 && x0-y > 0){ - if(y0+x < eyeheight) - v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if(y0-x > 0) - v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - } - - } - return; -} - -// lack of graphics functions use primitive algorithm to draw circle by coloring in rgb in image memory -void drawCircle(CrossHairInfo *chi, int x0, int y0, int width, int height, int cindex) -{ - - - - unsigned char r =0; - unsigned char g =0; - unsigned char b =0; - int x = 0, y, f; - unsigned int *v0; - int radius, ddF_x =1, ddF_y; - - if (eyeimage == NULL) return; - - switch(cindex){ - case CR_HAIR_COLOR: r=g=b = 255; break;//255,255,255 - case PUPIL_HAIR_COLOR: r=g=b = 255; break;//255,255,255 - case PUPIL_BOX_COLOR: g = 255; break;//0,255,0 - case SEARCH_LIMIT_BOX_COLOR: - case MOUSE_CURSOR_COLOR: r = 255; break;//255,0,0 - } - - - v0 = (unsigned int*) (eyeimage); - - radius = width / 2; - y= radius; - f = 1 - radius; - - ddF_y = -2 * radius; - x = 0; - y = radius; - x0 = x0 + width/2; - y0 = y0 + width/2; - - if (x0 - radius > 0 && x0+radius < eyewidth && y0+radius < eyeheight && y0-radius > 0){ - v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - v0[(eyewidth*eyeheight) - (y0-radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - v0[(eyewidth*eyeheight) - y0*eyewidth+(x0+radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - v0[(eyewidth*eyeheight) - y0*eyewidth+(x0-radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - - while( x < y ){ - - if(f >= 0){ - y--; - ddF_y += 2; - f += ddF_y; - } - x++; - ddF_x += 2; - f += ddF_x; - if (y0+y < eyeheight && y0+y > 0){ - if(x0+x < eyewidth) - v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if(x0-x > 0) - v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if (y0-y < eyeheight && y0-y > 0){ - if(x0+x < eyewidth) - v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if(x0-x > 0) - v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if(y0+x < eyeheight && y0+x > 0){ - if(x0+y < eyewidth) - v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if(x0-y >0) - v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - if(y0-x < eyeheight && y0-x > 0){ - if(x0+y < eyewidth) - v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if(x0-y >0) - v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - } - return; -} -// added by NJ @ SR Research Sept 2010 -void drawLozenge(CrossHairInfo *chi, int x0, int y0, int width, int height, int cindex) -{ - - unsigned char r =0; - unsigned char g =0; - unsigned char b =0; - int x = 0, y; - int y2, y1; - unsigned int *v0; - - if (eyeimage == NULL) return; - // clip to prevent memory issues and wrap around - - switch(cindex) { - - case CR_HAIR_COLOR: r=g=b = 255; break;//255,255,255 - case PUPIL_HAIR_COLOR: r=g=b = 255; break;//255,255,255 - case PUPIL_BOX_COLOR: g = 255; break;//0,255,0 - case SEARCH_LIMIT_BOX_COLOR: - case MOUSE_CURSOR_COLOR: r = 255; break;//255,0,0 - } - -if(eyeimage != NULL) { - - // Retrieve v0 as pointer to pixel row in output buffer: - v0 = (unsigned int*) (eyeimage); - - // is it a circle? - if(abs(width - height) < 4) - { - drawCircle(chi, x0, y0, width, height, cindex); - - }else { // non. ligne - - int minwidth = width * (width < height) + height * (width >= height); //min(width,height); - if (width == minwidth) // width was smaller - { - y1 = y0+width/2; - x = x0; - y2 = y1+(height-width) + 1; - // only draw vertical lines - for (y=y1 ;y< y2;y++) { - // be careful to clip or memory error occurs - if (y < eyeheight && y > 0 && x > 0 && x < eyewidth) - v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - if (y < eyeheight && y > 0 && (x+ width) > 0 && (x+width) < eyewidth) - v0[(eyewidth*eyeheight) - y*eyewidth+(x+width)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - - } // Now draw the semi circles - drawSemiCircle(chi,x0,y0,width,UPSIDE,cindex); - drawSemiCircle(chi,x0,(y0+width/2+(height-width)),width,DOWNSIDE,cindex); - - - }else{ // height smaller - - int x1 = x0+height/2; - int x2 = x1+(width-height); - y=y0; - //horizontal - for (x=x1 ;x<= x2;x++) { - if ( x < eyewidth && x > 0 && y > 0 && y < eyeheight) - v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - - y = y0+height; - for (x=x1 ;x<= x2;x++) { - if ( x < eyewidth && x > 0 && y < eyeheight && y > 0) - v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - - drawSemiCircle(chi,(x0+height/2+(width-height)),y0,height,RIGHTSIDE,cindex); - drawSemiCircle(chi,x0,y0,height,LEFTSIDE,cindex); - - } - } -} - return; -} - -// added by NJ @ SR Research LTD -void drawLine(CrossHairInfo *chi, int x1, int y1, int x2, int y2, int cindex) -{ - - unsigned char r =0; - unsigned char g =0; - unsigned char b =0; - int dx, dy; - int x, y, ch; - INT16 xc[4],yc[4], enabled; - unsigned int *v0; - int xx1, xx2, yy1, yy2; - - if (eyeimage == NULL) return; - // get camera channel. 2 = head. - ch = get_image_xhair_data(xc, yc, &enabled); - - // clip if fail - if (ch == 2){ - - if (x1<0) x1=0; - if (x2<0) x2=0; - if (y1<0) y1=0; - if (y2<0) y2=0; - - if (x1>eyewidth-1) x1=eyewidth-1; - if (x2>eyewidth-1) x2=eyewidth-1; - if (y1>eyeheight-1) y1=eyeheight-1; - if (y2>eyeheight-1) y2=eyeheight-1; - } - - - switch(cindex) - { - case CR_HAIR_COLOR: r=g=b = 255; break;//255,255,255 - case PUPIL_HAIR_COLOR: r=g=b = 255; break;//255,255,255 - case PUPIL_BOX_COLOR: g = 255; break;//0,255,0 - case SEARCH_LIMIT_BOX_COLOR: - case MOUSE_CURSOR_COLOR: r = 255; break;//255,0,0 - } - // Memory pointer to malloc()'ed image pixel buffer that holds the - // image data for a RGBA8 texture with the most recent eye camera image: - // eyeimage is a global variable - if(eyeimage != NULL) { - - // Retrieve v0 as pointer to pixel row in output buffer: image is upside down - v0 = (unsigned int*) ( eyeimage); - - dx = x2 - x1; - dy = y2 - y1; - - - if(ch != 2){ - // never diagonal here y1 is always < y2 - for (y=y1 ;y< y2;y++) { - x = x1 + (dx) * (y - y1)/(dy); - if (y < eyeheight && x < eyewidth && y >0 && x >0 && (eyewidth*eyeheight) - y*eyewidth+x > 0) - v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - } - - //account for diagonal lines in binocular mode ( x1 and y1 may be > x2 and y2) - - // vertical - if (dx == 0 ){ - if(y1>y2){ - yy1 = y2; - yy2 = y1; - }else { - yy1 = y1; - yy2 = y2; - } - - for (y=yy1 ;y< yy2;y++) { - x = x1 + (dx) * (y - y1)/(dy); - if (y < eyeheight && x < eyewidth && y >0 && x >0 && (eyewidth*eyeheight) - y*eyewidth+x > 0) - v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - } - else { - if(x1>x2){ - xx1 = x2; - xx2 = x1; - }else { - xx1 = x1; - xx2 = x2; - } - - for (x=xx1 ;x< xx2;x++) { - y = y1 + (dy) * (x - x1)/(dx); - if (y < eyeheight && x < eyewidth && y >0 && x >0 && (eyewidth*eyeheight) - y*eyewidth+x > 0) - v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); - } - } - } - return; -} - - -typedef void (*GET_MOUSE_LOC)(CrossHairInfo *dt, int *x, int *y, int *state); -GET_MOUSE_LOC mouseLoc = NULL; - -void ELCALLTYPE set_mouse_loc_callback(GET_MOUSE_LOC get_mouse_loc) -{ - mouseLoc = get_mouse_loc; -} - - -void getMouseState(CrossHairInfo *chi, int *rx, int *ry, int *rstate) -{ - float x =0; - float y =0; - PsychGenericScriptType *inputs[1]; - PsychGenericScriptType *outputs[1]; - double* callargs; - double* outputargs; - float ar[7]; - float w,h; - int i; - - inputs[0] = mxCreateDoubleMatrix(1, 4, mxREAL); - callargs = mxGetPr(inputs[0]); - - callargs[0] = 16; // 16 == Command code for mouse button event - - Psych_mexCallMATLAB(1, outputs, 1, inputs, eyelinkDisplayCallbackFunc); - - outputargs = mxGetData(outputs[0]); - for (i=0;i<7;i++){ - ar[i] = (int) outputargs[i]; - } - - // Release our matrix again: - mxDestroyArray(inputs[0]); - mxDestroyArray(outputs[0]); - - w = ar[0]; - h = ar[1]; - x = floor((ar[2] - ((w/2) - ar[4]/2)) * ((float)eyewidth/ar[4])); - y = floor((ar[3] - ((h/2) - ar[5]/2)) * ((float)eyeheight/ar[5])); - - if(x>0 && y >0 && x <= eyewidth && y <= eyeheight) - { - *rx = (int)x; - *ry = (int)y; - *rstate = (int)ar[6]; - }else - { - if(x<=0 && y<=0) - { - - *rx = 1; - *ry = 1; - } - else if(x<0 && y>eyeheight) - { - - *rx = 1; - *ry = eyeheight; - } - else if(x>eyewidth && y>eyeheight) - { - - *rx = eyewidth; - *ry = eyeheight; - } - else if(x>eyewidth && y<0) - { - - *rx = eyewidth; - *ry = 1; - } - else if(x>eyewidth && y>0 && y0 && y<=eyeheight) - { - - *rx = 1; - *ry = y; - } - else if(y<0 && x>0 && x<=eyewidth) - { - - *rx = x; - *ry = 1; - } - else if(y>eyeheight && x>0 && x<=eyewidth) - { - - *rx = x; - *ry = eyeheight; - } - } - - return; -} - - -// PsychEyelink_draw_image_line() retrieves exactly one scanline worth of eye camera -// image data. Once a full image has been received, it has to trigger the actual image -// display: -static void ELCALLBACK PsychEyelink_draw_image_line(INT16 width, INT16 line, INT16 totlines, byte *pixels) -{ - PsychGenericScriptType *inputs[1]; - PsychGenericScriptType *outputs[1]; - double* callargs; - double teximage; - static INT16 lastline = -1; - static int wrapcount = 0; - static double tlastwrap = 0.0; - double tnow; - int rc; - byte* p; - unsigned int *v0; - short i; - CrossHairInfo crossHairInfo; - - if (Verbosity() > 8) printf("Eyelink: Entering PsychEyelink_draw_image_line()\n"); - - // Callbacks forcefully disabled by error-handling? Simply return with no-op, if so: - if (0 == eyelinkDisplayCallbackFunc[0]) return; - - // width, line, totlines within valid range? - if (width < 1 || width > eyewidth || line < 1 || line > eyeheight || totlines < 1 || totlines > eyeheight) { - printf("EYELINK: WARNING! Eye camera image with invalid parameters received! (width = %i, line = %i, totlines = %i out of sane range %i x %i)!\n", - width, line, totlines, eyewidth, eyeheight); - printf("EYELINK: WARNING! Will try to clamp to valid values, but results may be junk.\n"); - width = eyewidth; - line = (line < 1) ? 1 : line; - line = (line > eyeheight) ? line : eyeheight; - totlines = (totlines < 1) ? 1 : totlines; - totlines = (totlines > eyeheight) ? totlines : eyeheight; - } - - - - - // Data structures properly initialized? - if(eyeimage != NULL) { - // Retrieve p as pointer to input pixel index color buffer: - p = pixels; - - // Retrieve v0 as pointer to pixel row in output buffer: - v0 = (unsigned int*) (( eyeimage + ( (totlines - line) * width * 4 ) )); - - // Copy one row of pixels from input- to output buffer: - // This is a bit optimized, but we could do more if we're really bored with life ;-) - for(i=0; i < width; i++) { - // Decode pixel index value in *p via palette color lookup table and store to output buffer: - *(v0++) = palmap32[*p++]; - } - - if (Verbosity() > 8) printf("Eyelink: PsychEyelink_draw_image_line(): Scanline %i received.\n", (int) line); - - // Premature wraparound? - if (line < lastline) { - // Premature wraparound due to too slow processing. Increase wrapcounter: - wrapcount++; - } - - // More than some threshold? - if (wrapcount > 10) { - // Spill a warning? - PsychGetAdjustedPrecisionTimerSeconds(&tnow); - if (tnow - tlastwrap > 2.0) { - // Last invocation longer than 2 seconds away: - // Output some warning to console... - if (Verbosity() > 1) { - printf("Eyelink: Warning: Skipped videoframes from eye camera detected within last seconds (count=%i)\n", wrapcount); - printf("Eyelink: Warning: Timing problems on your machine or network problems on tracker connection?!?\n\n"); - } - - // Update / Reset detector: - tlastwrap = tnow; - wrapcount = 0; - } - } - - // Update skip detector: - lastline = line; - - // Complete new eye image received? - if (line == totlines) { - // Yes. Our eyeimage buffer contains a new image. - - // Reset skip detector: - lastline = -1; - - crossHairInfo.w = eyewidth; - crossHairInfo.h = eyeheight; - crossHairInfo.drawLozenge = drawLozenge; - crossHairInfo.drawLine = drawLine; - crossHairInfo.getMouseState = mouseLoc?mouseLoc:getMouseState; - crossHairInfo.userdata = eyeimage; - - eyelink_draw_cross_hair(&crossHairInfo); - - - // Compute double-encoded Matlab/Octave compatible memory pointer to image buffer: - teximage = PsychPtrToDouble((void*) eyeimage); - - - - - // Ok, teximage is a memory pointer to our image buffer, encoded as a double. - // Now we need to call our Matlab callback function which actually converts - // the data in our internal image buffer into a PTB texture, then draws that - // texture etc. to display the new eye camera image. - if (Verbosity() > 6) printf("Eyelink: PsychEyelink_draw_image_line(): All %i Scanlines received. Calling Runtime!\n", (int) line); - - // Create a Matlab double matrix with 4 elements: 1st is command code '1' - // 2nd is the double pointer, 3r//d is image width, 4th is image height: - outputs[0] = NULL; - inputs[0] = mxCreateDoubleMatrix(1, 4, mxREAL); - callargs = mxGetPr(inputs[0]); - - callargs[0] = 1; // 1 == Command code for "Show eye image". - callargs[1] = teximage; - callargs[2] = eyewidth; - callargs[3] = eyeheight; - - rc = Psych_mexCallMATLAB(0, outputs, 1, inputs, eyelinkDisplayCallbackFunc); - if(rc) { - printf("EYELINK: WARNING! Failed to call eyelink camera image display callback function %s [rc=%i]!\n", eyelinkDisplayCallbackFunc, rc); - printf("EYELINK: WARNING! Make sure that function is on your Matlab/Octave path and properly initialized.\n"); - printf("EYELINK: WARNING! May also be an error during execution of that function. Type ple at command prompt for error messages.\n"); - printf("EYELINK: WARNING! Auto-Disabling all callbacks to the runtime environment for safety reasons.\n"); - eyelinkDisplayCallbackFunc[0] = 0; - } - - // Release our matrix again: - mxDestroyArray(inputs[0]); - } - } - - - - - // Done. - return; -} - -// PsychEyelink_set_image_palette() sets the color palette for decoding 1-byte color index -// values in an eyelink camera image into RGB8 1-byte-per-color-component color values: -static void ELCALLBACK PsychEyelink_set_image_palette(INT16 ncolors, byte r[], byte g[], byte b[]) -{ - short i; - - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_set_image_palette()\n"); - - if (ncolors > 256) { - printf("EYELINK: WARNING! Invalid color palette size %i (> 256 colors!) received from eyelink: Clamping to 256 colors.\n", (int) ncolors); - ncolors = 256; - } - - // Copy given r,g,b color arrays into internal remapping table palmap32: - for(i=0; i < ncolors; i++) { - // Format is ABGR - palmap32[i] = 0xFF000000 | ((unsigned int) b[i] << 16) | ((unsigned int) g[i] << 8) | ((unsigned int) r[i]); - } - - return; -} - -static INT16 ELCALLBACK PsychEyelink_setup_cal_display(void) -{ - //nj added "hack" to disable flashing instructions in drift correction and to enable sending cal and val results - int mode = -1; - - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_setup_cal_display()\n"); - - mode = eyelink_tracker_mode(); - - if (mode == 1 || mode ==9 ) //EL_DRIFT_CORR_MODE) - if (0xdeadbeef == PsychEyelinkCallRuntime(17, 0, 0, NULL)) { - // Error condition. Return error to eyelink runtime: - return(-1); - } - - // Tell runtime to setup calibration display: Command code 7. - if (0xdeadbeef == PsychEyelinkCallRuntime(7, 0, 0, NULL)) { - // Error condition. Return error to eyelink runtime: - return(-1); - } - - // Return success: - return(0); -} - -static void ELCALLBACK PsychEyelink_exit_cal_display(void) -{ - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_exit_cal_display()\n"); - - // Tell runtime to exit calibration display: Command code 11. - PsychEyelinkCallRuntime(11, 0, 0, NULL); - return; -} - -static void ELCALLBACK PsychEyelink_clear_display(void) -{ - //NJ modified to add msg to call back 6 with cal and val result - char strMessage[256]; - int result =-1; - // Clear strings - memset(strMessage, 0, sizeof(strMessage)); - - - result = eyelink_cal_message(strMessage); - - - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_clear_display()\n"); - - // Tell runtime to clear display: Command code 6. - PsychEyelinkCallRuntime(6, 0, 0, strMessage);//NULL); - - - return; -} - -static void ELCALLBACK PsychEyelink_draw_cal_target(INT16 x, INT16 y) -{ - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_draw_cal_target(): x=%i y=%i.\n", (int) x, (int) y); - - // Tell runtime about where to draw calibration target: Command code 5. - PsychEyelinkCallRuntime(5, (int) x, (int) y, NULL); - - return; -} - -static void ELCALLBACK PsychEyelink_erase_cal_target(void) -{ - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_erase_cal_target():\n"); - - // Tell runtime about clear calibration target: Command code 10. - PsychEyelinkCallRuntime(10, 0, 0, NULL); - - return; -} - -static void ELCALLBACK PsychEyelink_image_title(INT16 threshold, char *title) -{ - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_image_title(): threshold = %i : Title = %s\n", (int) threshold, title); - - //mexPrintf("C code: %s ... %d\n", title, threshold); - //fflush(stdout); - - // Tell runtime about image title: Command code 4. - PsychEyelinkCallRuntime(4, (int) threshold, 0, title); - - return; -} - -#ifndef ELKEY_DOWN -#define ELKEY_DOWN 1 //temporary while we wait for sr-research's lib to get updated with this -#endif - -static INT16 ELCALLBACK PsychEyelink_get_input_key(InputEvent *keyinput) -{ - int ky = 0; - double tnow; - static double tlastquery = 0; - const double tmininterval = 0.1; // Allow one query every 0.1 seconds. - InputEvent *key_input = keyinput; - - // Throttling routine: - // We don't want key queries to call out to the runtime too often, as this - // creates a quite significant overhead, e.g., approx. 1 msec for a KbCheck - // for a fast 2009'ish machine on OS/X! - PsychGetAdjustedPrecisionTimerSeconds(&tnow); - if (tnow - tlastquery < tmininterval) { - // Last invocation less than tmininterval seconds away. Throttle this, - // we just return "no key pressed". - if (Verbosity() > 9) printf("Eyelink: In PsychEyelink_get_input_key(): Throttling...\n"); - return(0); - } - else { - // Last invocation longer than tmininterval seconds away. Accept this - // query and update timestamp: - tlastquery = tnow; - } - - if (Verbosity() > 7) printf("Eyelink: Entering PsychEyelink_get_input_key()\n"); - - // Call runtime for keycode of pressed key (command code 2): - if ((ky = PsychEyelinkCallRuntime(2, 0, 0, NULL)) == 0xdeadbeef) { - // Error condition in runtime callback! Can't progress. We try to - // shutdown the current eyelink runtime operation by sending a fake - // keycode corresponding to the terminate key: - if (Verbosity() > 0) printf("Eyelink: In PsychEyelink_get_input_key(): Error condition detected: Trying to send TERMINATE_KEY abort keycode!\n"); - ky = TERMINATE_KEY; - } - - if (ky > 0) { - // Fill Eyelinks InputEvent struct: - memset(key_input, 0, sizeof(InputEvent)); - key_input->key.key = ky; - key_input->key.state = ELKEY_DOWN; - key_input->key.type = KEYINPUT_EVENT; - key_input->key.modifier = 0; //event.key.keysym.mod; - key_input->key.unicode = 0;//event.key.keysym.unicode; - - // One key pressed: - return(1); - } - else { - // No key pressed: - return(0); - } -} - -static void ELCALLBACK PsychEyelink_alert_printf_hook(const char *msg) -{ - // Print error message to runtime console if error output is allowed: - if (Verbosity() > 3) printf("Eyelink: Alert! Eyelink says: %s.\n\n", msg); - - // Tell runtime about alert condition: Command code 3. - PsychEyelinkCallRuntime(3, 0, 0, (char*) msg); - - return; -} - -static void ELCALLBACK PsychEyelink_cal_target_beep_hook(void) -{ - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_cal_target_beep_hook():\n"); - - PsychEyelinkCallRuntime(12, 0, 0, NULL); - return; -} - -static void ELCALLBACK PsychEyelink_dc_target_beep_hook(void) -{ - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_dc_target_beep_hook():\n"); - - PsychEyelinkCallRuntime(13, 0, 0, NULL); - return; -} - -static void ELCALLBACK PsychEyelink_cal_done_beep_hook(INT16 error) -{ - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_cal_done_beep_hook():\n"); - - PsychEyelinkCallRuntime(14, (int) error, 0, NULL); - return; -} - -static void ELCALLBACK PsychEyelink_dc_done_beep_hook(INT16 error) -{ - if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_dc_done_beep_hook():\n"); - - PsychEyelinkCallRuntime(15, (int) error, 0, NULL); - return; -} +/* + + /osxptb/trunk/PsychSourceGL/Source/Common/Eyelink/PsychEyelink.c + + PROJECTS: Eyelink + + AUTHORS: + + cburns@berkeley.edu cdb + E.Peters@ai.rug.nl emp + f.w.cornelissen@med.rug.nl fwc + mario.kleiner@tuebingen.mpg.de mk + li@sr-research.com lj + + PLATFORMS: All. + + HISTORY: + + 11/22/05 cdb Created. + 29/06/06 fwc Fixed EyelinkSystemIsConnected to allow dummy mode connections + 15/03/09 mk Added experimental support for eye camera image display. + 12/20/13 lj Fixed PsychEyelinkParseToString to allow space between % ; + modified getMouseState to limit mouse cursor inside of camera image. + +*/ + +#include "PsychEyelink.h" +#include + +///////////////////////////////////////////////////////////////////////// +// Global variables used throughout eyelink C files + +int giSystemInitialized = 0; + +// Level of verbosity: Do only query via Verbosity() accessor function outside this file! +static int verbosity = 2; + +// Callback string for eyelink display callback function: +static char eyelinkDisplayCallbackFunc[1024]; + +// Memory pointer to malloc()'ed image pixel buffer that holds the +// image data for a RGBA8 texture with the most recent eye camera image: +static byte* eyeimage = NULL; + +// Width x Height of eye camera image in pixels: +static int eyewidth = 0; +static int eyeheight = 0; + +// Color remapping palette table: +static unsigned int palmap32[256]; +#define ERR_BUFF_LEN 1000 + +/* Declaration of callback functions defined later in this file: */ +static INT16 ELCALLBACK PsychEyelink_setup_image_display(INT16 width, INT16 height); +static void ELCALLBACK PsychEyelink_exit_image_display(void); +static void ELCALLBACK PsychEyelink_set_image_palette(INT16 ncolors, byte r[130], byte g[130], byte b[130]); +static void ELCALLBACK PsychEyelink_draw_image_line(INT16 width, INT16 line, INT16 totlines, byte *pixels); + +static INT16 ELCALLBACK PsychEyelink_setup_cal_display(void); +static void ELCALLBACK PsychEyelink_exit_cal_display(void); +static void ELCALLBACK PsychEyelink_clear_display(void); +static void ELCALLBACK PsychEyelink_draw_cal_target(INT16 x, INT16 y); +static void ELCALLBACK PsychEyelink_erase_cal_target(void); +static void ELCALLBACK PsychEyelink_image_title(INT16 threshold, char *title); +static INT16 ELCALLBACK PsychEyelink_get_input_key(InputEvent *keyinput); +static void ELCALLBACK PsychEyelink_alert_printf_hook(const char *msg); +static void ELCALLBACK PsychEyelink_noop(void); + +static void ELCALLBACK PsychEyelink_cal_target_beep_hook(void); +static void ELCALLBACK PsychEyelink_cal_done_beep_hook(INT16 error); +static void ELCALLBACK PsychEyelink_dc_done_beep_hook(INT16 error); +static void ELCALLBACK PsychEyelink_dc_target_beep_hook(void); + +///////////////////////////////////////////////////////////////////////// +// Check if system is initialized +// +PsychError EyelinkSystemIsConnected(void) +{ + int iStatus=-9999; + iStatus=eyelink_is_connected(); +// mexPrintf("EyelinkSystemIsConnected status %d ((iStatus==0)=%d)\n", iStatus, (iStatus==0) ); + if (iStatus==0) { + PsychErrorExitMsg(PsychError_user, "Eyelink system is not connected!\n"); + } +/* + if (eyelink_is_connected()==0) { + PsychErrorExitMsg(PsychError_user, "Eyelink system is not connected!\n"); + } + */ + return(PsychError_none); +} + +///////////////////////////////////////////////////////////////////////// +// Check is system is initialized +// +PsychError EyelinkSystemIsInitialized(void) +{ + if (giSystemInitialized != 1) { + PsychErrorExitMsg(PsychError_user, "Eyelink system is not initialized!\n"); + } + return(PsychError_none); +} + +/* Eyelink('Verbosity') - Set level of verbosity. + */ +PsychError EyelinkVerbosity(void) +{ + static char useString[] = "oldlevel = Eyelink('Verbosity' [,level]);"; + static char synopsisString[] = + "Set level of verbosity for error/warning/status messages. 'level' optional, new level " + "of verbosity. 'oldlevel' is the old level of verbosity. The following levels are " + "supported: 0 = Shut up. 1 = Print errors, 2 = Print also warnings, 3 = Print also some info, " + "4 = Print more useful info (default), >5 = Be very verbose (mostly for debugging the driver itself). "; + static char seeAlsoString[] = " "; + + int level= -1; + + // Setup online help: + PsychPushHelp(useString, synopsisString, seeAlsoString); + if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none); }; + + PsychErrorExit(PsychCapNumInputArgs(1)); // The maximum number of inputs + PsychErrorExit(PsychRequireNumInputArgs(0)); // The required number of inputs + PsychErrorExit(PsychCapNumOutputArgs(1)); // The maximum number of outputs + + PsychCopyInIntegerArg(1, kPsychArgOptional, &level); + if (level < -1) PsychErrorExitMsg(PsychError_user, "Invalid level of verbosity provided. Valid are levels of zero and greater."); + + // Return current/old level: + PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) verbosity); + + // Set new level, if one was provided: + if (level > -1) verbosity = level; + + return(PsychError_none); +} + +// Return level of verbosity: +int Verbosity(void) { + return(verbosity); +} + +// Parse printf() style format string and variable number of +// integer or string arguments into a printf() formatted +// string and return static pointer to the final string. +// Used, e.g., by Eyelink('Command') and Eyelink('Message'): +const char* PsychEyelinkParseToString(int startIdx) +{ + static char strCommand[256]; + int i = 0, j=0; + int iNumInArgs = 0; + PsychArgFormatType psychArgType = PsychArgType_none; + int iTempValue = 0; + char *pstrTemp = NULL; + char *pstrFormat = NULL; + char strFragment[256]; + char fSpec[256]; + int wIdx = 0; + int argIdx; + + // Alloc and grab the input format string + PsychAllocInCharArg(startIdx, TRUE, &pstrFormat); + iNumInArgs = PsychGetNumInputArgs(); + + // Define start index of variable argument list: + argIdx = startIdx + 1; + + // Clear strings + memset(strCommand, 0, sizeof(strCommand)); + + // Parse complete format string: + while ((*pstrFormat != 0) && (wIdx < 255)) { + // Special character % detected? + if ((*pstrFormat != '%') || (*(pstrFormat+1) == '%')) { + // Easy: Regular char or escaped %. Just copy into target command string: + + // Eat up the escape '%' character, if any: + if (pstrFormat == strstr(pstrFormat, "%%")) pstrFormat++; + + // Copy escaped single % or regular character: + strCommand[wIdx++] = *(pstrFormat++); + + // Next character... + continue; + } + + // Special % char detected, which is not escaped, therefore + // a datatype format specifier follows immediately: + + // Is there an argument available to match the format string spec? + if (iNumInArgs < argIdx) { + PsychErrorExitMsg(PsychError_user, "Number of supplied arguments does not match number of arguments required by format string!"); + } + + // Find end of actual parameter spec: + for (i = 0; (pstrFormat[i] > 0) && (pstrFormat[i] != '%'); i++) {}; + for (j = i+1; (pstrFormat[j] > 0) && (pstrFormat[j] != ' ') && (pstrFormat[j]!='%'); j++) {}; + + // Copy format substring to fSpec: + memset(fSpec, 0, sizeof(fSpec)); + strncpy(fSpec, pstrFormat, ((j-i) < 256) ? (j-i) : 255); + + // Prepare output substring for writing: + memset(strFragment, 0, sizeof(strFragment)); + + // Check if input argument type matches parameter spec string + // and assign, if so, abort otherwise: + psychArgType = PsychGetArgType(argIdx); + switch(psychArgType) { + case PsychArgType_double: + if ((PsychGetArgM(argIdx) == 1) && (PsychGetArgN(argIdx) == 1)) { + PsychCopyInIntegerArg(argIdx, TRUE, &iTempValue); + + // Got a int value. Was a int value expected? + if (strstr(fSpec, "d") || strstr(fSpec, "i")) { + // Yes: Print into output string fragment: + snprintf(strFragment, 255, fSpec, iTempValue); + } else { + // No: This is a mismatch - Game over: + PsychErrorExitMsg(PsychError_user, "Mismatch between provided scalar integer argument and expected argument!"); + } + } else { + PsychGiveHelp(); + PsychErrorExitMsg(PsychError_user, ""); + } + break; + + case PsychArgType_char: + PsychAllocInCharArg(argIdx, TRUE, &pstrTemp); + // Got a string. Was a string expected? + if (strstr(fSpec, "s")) { + // Yes: Print into output string fragment: + snprintf(strFragment, 255, fSpec, pstrTemp); + } else { + // No: This is a mismatch - Game over: + PsychErrorExitMsg(PsychError_user, "Mismatch between provided character string and expected argument!"); + } + break; + + default: + PsychGiveHelp(); + PsychErrorExitMsg(PsychError_user, ""); + break; + } + + // If we made it here, then the strFragment is ready for + // joining: + if ((strlen(strCommand) + strlen(strFragment)) < 256) { + strcat(strCommand, strFragment); + } else { + // Break out of parser - Need to truncate: + break; + } + + // Advance parse positions: + wIdx = strlen(strCommand); + pstrFormat += j; + argIdx++; + + // Next parse iteration. + } + + // Sanity check: + if (*pstrFormat != 0) printf("Eyelink-Warning:Final overall command truncated to '%s'!\nMaximum of 255 characters allowed.\n", strCommand); + + // Return pointer to internally statically allocated final character string: + return(strCommand); +} + +// Initialize all callback hook functions for use by Eyelink runtime, e.g., +// all the callbacks for eye camera image display: +void PsychEyelink_init_core_graphics(const char* callback) +{ + HOOKFCNS fcns; + + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_init_core_graphics()\n"); + + memset(&fcns, 0, sizeof(fcns)); + + // Setup cam image callbacks: + fcns.setup_image_display_hook = PsychEyelink_setup_image_display; + fcns.exit_image_display_hook = PsychEyelink_exit_image_display; + fcns.set_image_palette_hook = PsychEyelink_set_image_palette; + fcns.image_title_hook = PsychEyelink_image_title; + fcns.draw_image_line_hook = PsychEyelink_draw_image_line; + + // Setup calibration callbacks: + fcns.setup_cal_display_hook = PsychEyelink_setup_cal_display; + fcns.exit_cal_display_hook = PsychEyelink_exit_cal_display; + fcns.clear_cal_display_hook = PsychEyelink_clear_display; + fcns.draw_cal_target_hook = PsychEyelink_draw_cal_target; + fcns.erase_cal_target_hook = PsychEyelink_erase_cal_target; + + // Setup keyboard and I/O callbacks: + fcns.get_input_key_hook = PsychEyelink_get_input_key; + fcns.alert_printf_hook = PsychEyelink_alert_printf_hook; + + // Set auditory feedback callbacks: + fcns.cal_target_beep_hook = PsychEyelink_cal_target_beep_hook; + fcns.cal_done_beep_hook = PsychEyelink_cal_done_beep_hook; + fcns.dc_target_beep_hook = PsychEyelink_dc_target_beep_hook; + fcns.dc_done_beep_hook = PsychEyelink_dc_done_beep_hook; + + // Just set this to make eyelink-core happy: + fcns.record_abort_hide_hook = PsychEyelink_noop; + + // Assign runtime environment display callback function: + memset(eyelinkDisplayCallbackFunc, 0, sizeof(eyelinkDisplayCallbackFunc)); + + snprintf(eyelinkDisplayCallbackFunc, sizeof(eyelinkDisplayCallbackFunc) - 1, "%s", callback); +// #if PSYCH_SYSTEM != PSYCH_WINDOWS +// snprintf(eyelinkDisplayCallbackFunc, sizeof(eyelinkDisplayCallbackFunc) - 1, "%s", callback); +// #else +// _snprintf(eyelinkDisplayCallbackFunc, sizeof(eyelinkDisplayCallbackFunc) - 1, "%s", callback); +// #endif + + // Assign hooks to Eyelink runtime: + setup_graphic_hook_functions(&fcns); + + // Optionally dump the whole hookfunctions struct: + if (Verbosity() > 5) PsychEyelink_dumpHookfunctions(); + + return; +} + +// Disable all hook functions at shutdown time: +void PsychEyelink_uninit_core_graphics(void) +{ + HOOKFCNS fcns; + + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_uninit_core_graphics()\n"); + + memset(&fcns, 0, sizeof(fcns)); + setup_graphic_hook_functions(&fcns); + + + // Fake callback to cleanup runtime + PsychEyelinkCallRuntime(-1, 0, 0, ""); + + // Optionally dump the whole hookfunctions struct: + if (Verbosity() > 5) PsychEyelink_dumpHookfunctions(); + + return; +} + +void PsychEyelink_dumpHookfunctions(void) +{ + HOOKFCNS* pfcns = get_all_hook_functions(); + int i; + + printf("PsychEyelink: Dump of current Eyelink HOOKFCNS struct as byte array:\n\n"); + for (i=0; i < sizeof(HOOKFCNS); i++) printf(" %02x", (int)(((unsigned char*) pfcns)[i])); + printf("\nPsychEyelink: Dump done\n\n"); + + return; +} + +void PsychEyelink_TestEyeImage(void) +{ + int i, x, y; + byte r[256], g[256], b[256]; + byte scanline[640]; + InputEvent keyinput; + + // Pseudo-Eyelink camera image test: + + // Setup pseudo eye-display of 640 x 480 pixels via setup callback: + PsychEyelink_setup_image_display(640, 480); + + // Build pseudo color LUT: + for (i=0; i < 256; i++) { + r[i]=i; + g[i]=255 - i; + b[i]=i * 2; + } + PsychEyelink_set_image_palette(256, r, g, b); + + // Set image title: + PsychEyelink_image_title(1, "Foobar-O-Matic:"); + + // Calibration beep: + PsychEyelink_cal_target_beep_hook(); + + // Run pseudo-display loop for 600 frames: + for (i = 0; i < 600; i++) { + // Draw calibration target: + PsychEyelink_draw_cal_target(i, 200); + + // Fill buffer with image pattern: + for (y=1; y <= 480; y++) { + // Build y'th scanline: + for (x=0; x < 640; x++) scanline[x] = (byte) ((x + y + i) % 256); + + // Submit y'th scanline: + PsychEyelink_draw_image_line(640, y, 480, (byte*) &scanline); + } + + // Check keyboard: + keyinput.key.key = 0; + if (PsychEyelink_get_input_key(&keyinput) > 0) { + PsychEyelink_alert_printf_hook("Eyelink: Key detected.\n"); + // Break out of loop on keycode 41 or 27 == ESCAPE on OS/X or Windows. + if (keyinput.key.key == 41 || keyinput.key.key == 27) break; + if (keyinput.key.key == TERMINATE_KEY) { + printf("Eyelink: TestSuite: WARNING: Abort code detected. Master abort.\n"); + break; + } + } + } + + // Calibration end beep hook: + PsychEyelink_cal_done_beep_hook(keyinput.key.key); + + // Tear down pseudo display: + PsychEyelink_exit_image_display(); + + // Test calibration display: + PsychEyelink_setup_cal_display(); + PsychEyelink_dc_target_beep_hook(); + PsychEyelink_clear_display(); + PsychEyelink_dc_done_beep_hook(keyinput.key.key); + + PsychEyelink_exit_cal_display(); + + return; +} + +int PsychEyelinkCallRuntime(int cmd, int x, int y, char* msg) +{ + PsychGenericScriptType *inputs[2]; + PsychGenericScriptType *outputs[1]; + double* callargs; + double rc; + int retc; + + // Callbacks forcefully disabled by error-handling? Return with error code if so: + if (0 == eyelinkDisplayCallbackFunc[0]) return(0xdeadbeef); + + // Create a Matlab double matrix with 4 elements: 1st is command code + // others are available for use specific to each command + outputs[0] = NULL; + inputs[0] = mxCreateDoubleMatrix(1, 4, mxREAL); + callargs = mxGetPr(inputs[0]); + + callargs[0] = (double) cmd; // Command code. + callargs[1] = (double) x; + callargs[2] = (double) y; + + if (msg != NULL) { + inputs[1] = mxCreateString(msg); + } + else { + inputs[1] = NULL; + } + + // Call the runtime environment: + if ((retc = Psych_mexCallMATLAB((cmd == 2) ? 1 : 0, outputs, (inputs[1]) ? 2 : 1, inputs, eyelinkDisplayCallbackFunc)) > 0) { + printf("EYELINK: WARNING! PsychEyelinkCallRuntime() Failed to call eyelink runtime callback function %s [rc = %i]!\n", eyelinkDisplayCallbackFunc, retc); + printf("EYELINK: WARNING! Make sure that function is on your Matlab/Octave path and properly initialized.\n"); + printf("EYELINK: WARNING! May also be an error during execution of that function. Type ple at command prompt for error messages.\n"); + printf("EYELINK: WARNING! Auto-Disabling all callbacks to the runtime environment for safety reasons.\n"); + eyelinkDisplayCallbackFunc[0] = 0; + } + + // Release our matrix again: + mxDestroyArray(inputs[0]); + if (msg != NULL) mxDestroyArray(inputs[1]); + + if (outputs[0]) { + rc = mxGetScalar(outputs[0]); + mxDestroyArray(outputs[0]); + } + else { + rc = 0; + } + + return((int) rc); +} + +// Callback functions, called by Eyelink runtime at various occassions, e.g, +// during tracker setup, drift correction/calibration etc.: +// ========================================================================= + +static void ELCALLBACK PsychEyelink_noop(void) +{ + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_noop()\n"); + + // Done. + return; +} + +// PsychEyelink_setup_image_display() tells the width and height of the camera +// image in pixels. +static INT16 ELCALLBACK PsychEyelink_setup_image_display(INT16 width, INT16 height) +{ + + + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_setup_image_display()\n"); + // Release any stale image buffer: + if (eyeimage != NULL) free(eyeimage); + + // Reset everything to startup default: + eyeimage = NULL; + eyewidth = 0; + eyeheight = 0; + + if (width < 1 || height < 1) { + printf("EYELINK: WARNING! Invalid image dimensions (smaller than 1 pixel!) received from eyelink: Aborting image setup.\n"); + return(-1); + } + + // Allocate an internal memory buffer of sufficient size to hold an image + // of size width x height pixels: + eyeimage = (byte*) malloc(sizeof(unsigned char) * 4 * width * height); + if (eyeimage != NULL) { + eyewidth = width; + eyeheight = height; + } + else { + // Failed: + return(-1); + } + + // Tell callback about image dimensions fwiw: + if (0xdeadbeef == PsychEyelinkCallRuntime(8, eyewidth, eyeheight, NULL)) { + // Error condition. Return error to eyelink runtime: + return(-1); + } + + if (Verbosity() > 5) printf("Eyelink: Leaving PsychEyelink_setup_image_display()\n"); + + // Done. + return(0); +} + +// PsychEyelink_exit_image_display() shuts down any camera image display: +static void ELCALLBACK PsychEyelink_exit_image_display(void) +{ + + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_exit_image_display()\n"); + + // Release any allocated image buffer: + if (eyeimage != NULL) free(eyeimage); + + // Reset everything to startup default: + eyeimage = NULL; + eyewidth = 0; + eyeheight = 0; + + // Tell runtime to exit display: Command code 9. + PsychEyelinkCallRuntime(9, 0, 0, NULL); + + // Done. + return; +} +// added by NJ @ SR Research Sept 2010 +#define UPSIDE 0 +#define LEFTSIDE 1 +#define RIGHTSIDE 2 +#define DOWNSIDE 3 +void drawSemiCircle(CrossHairInfo *chi, int left, int top, int dia, int side, int cindex) +{ + + + + unsigned char r =0; + unsigned char g =0; + unsigned char b =0; + int radius = dia/2; + int x = left - 1; + int y = top -1; + unsigned int *v0; + int x0,y0, ddF_x =1, ddF_y,f; + + + if (eyeimage == NULL){ + return; + } + + switch(cindex) + { + case CR_HAIR_COLOR: r=g=b =255; break;//255,255,255 + case PUPIL_HAIR_COLOR: r=g=b =255; break;//255,255,255 + case PUPIL_BOX_COLOR: g =255; break;//0,255,0 + case SEARCH_LIMIT_BOX_COLOR: + case MOUSE_CURSOR_COLOR: r = 255; break;//255,0,0 + } + + v0 = (unsigned int*) (eyeimage); + + // implement manual clipping to mimic behavior on host pc + + if(side == UPSIDE) + { + + x0 = left; + y0 = top; + radius = dia / 2; + y= radius; + f = 1 - radius; + ddF_y = -2 * radius; + x = 0; + y = radius; + x0 = x0 + dia/2; + y0 = y0 + dia/2; + + + if (y0 < eyeheight && y0 > 0 && y0-radius < eyeheight && y0-radius > 0 && y0-radius < eyeheight && x0+radius < eyewidth && x0-radius >0 && x0+radius > 0 && x0-radius < eyewidth){ + v0[(eyewidth*eyeheight) - (y0-radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + v0[(eyewidth*eyeheight) - y0*eyewidth+(x0+radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + v0[(eyewidth*eyeheight) - y0*eyewidth+(x0-radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + + while( x < y ){ + + if(f >= 0){ + y--; + ddF_y += 2; + f += ddF_y; + + } + x++; + ddF_x += 2; + f += ddF_x; + + + if (y0+y >0 && y0+y < y0 && y0+y < eyeheight) { + + if(x0+x < eyewidth) + v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if (x0-x > 0 ) + v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if (y0-y >0 && y0-y < y0 && y0-y < eyeheight){//if (y0-y < y0 && y0-y < eyeheight){ + + if (x0+x < eyewidth) + v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if (x0-x > 0 ) + v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if (y0+x >0 && y0+x < y0 && y0+x < eyeheight){//if (y0+x < y0 && y0+x < eyeheight){ + + if (x0+y < eyewidth) + v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if((x0-y) >0) + v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if (y0-x > 0 && y0-x < y0 && y0-x < eyeheight){//if (y0-x < y0 && y0-x < eyeheight){ + + if(x0+y < eyewidth) + v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if(x0-y > 0) + v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + } + }else if (side == DOWNSIDE){ + + radius = dia / 2; + y= radius; + f = 1 - radius; + ddF_y = -2 * radius; + ddF_x =1; + x = 0; + y = radius; + + x0 = left; + y0 = top; + x0 = x0 + dia/2; + + if (y0+radius < eyeheight && y0+radius > 0 && x0 > 0 && x0 < eyewidth) + v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if (y0 < eyeheight && y0 > 0 && x0+radius > 0 && x0+radius < eyewidth) + v0[(eyewidth*eyeheight) - y0*eyewidth+(x0+radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if (y0 < eyeheight && y0 > 0 && x0-radius > 0 && x0-radius < eyewidth) + v0[(eyewidth*eyeheight) - y0*eyewidth+(x0-radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + + + while( x < y ){ + + if(f >= 0){ + y--; + ddF_y += 2; + f += ddF_y; + + } + x++; + ddF_x += 2; + f += ddF_x; + + if (y0+y >0 && y0+y > y0 && y0+y < eyeheight) { + if ( x0+x < eyewidth) + v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if ( x0-x > 0 ) + v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if (y0-y > 0 && y0-y > y0 && y0-y < eyeheight){ + if ( x0+x < eyewidth) + v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if ( x0-x > 0 ) + v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if (y0+x >0 && y0+x > y0 && y0+x < eyeheight){ + if ( x0+y < eyewidth ) + v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if ( x0-y > 0) + v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if (y0-x >0 && y0-x > y0 && y0-x < eyeheight){ + if ( x0+y < eyewidth) + v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if (x0-y > 0) + v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + } + + + }else if (side == RIGHTSIDE){ + + radius = dia / 2; + y= radius; + f = 1 - radius; + ddF_y = -2 * radius; + ddF_x =1; + x = 0; + y = radius; + + x0 = left; + y0 = top; + y0 = y0 + dia/2; + + + if (x0 + radius < eyewidth && x0+radius > 0){ + v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + v0[(eyewidth*eyeheight) - y0*eyewidth+(x0+radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + + while( x < y ){ + + if(f >= 0){ + y--; + ddF_y += 2; + f += ddF_y; + + } + x++; + ddF_x += 2; + f += ddF_x; + + if (x0+x >0 && x0+x > x0 && x0+x < eyewidth) { + if(y0+x < eyeheight) + v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if(y0-x > 0) + v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + + } + if (x0-x > 0 && x0-x > x0 && x0-x < eyewidth){ + if(y0+y < eyeheight) + v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if(y0-y > 0) + v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if (x0+y > 0 && x0+y > x0 && x0+y < eyewidth){ + if(y0+x < eyeheight) + v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if(y0-x > 0) + v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if (x0-y > 0 && x0-y > x0 && x0-y < eyewidth){ + if(y0+x < eyeheight) + v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if(y0-x > 0) + v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + } + + }else if (side == LEFTSIDE){ + + radius = dia / 2; + y= radius; + f = 1 - radius; + ddF_y = -2 * radius; + ddF_x =1; + x = 0; + y = radius; + + x0 = left; + y0 = top; + y0 = y0 + dia/2; + x0 = x0 + dia/2; + + if (x0 - radius > 0 && x0-radius < eyewidth){ + v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + v0[(eyewidth*eyeheight) - y0*eyewidth+(x0-radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + + while( x < y ){ + + if(f >= 0){ + y--; + ddF_y += 2; + f += ddF_y; + + } + x++; + ddF_x += 2; + f += ddF_x; + + if (x0+x > 0 && x0+x < x0 && x0+x > 0) { + v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if (x0-x > 0 && x0-x < x0 && x0-x > 0){ + if(y0+x < eyeheight) + v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if(y0-x > 0) + v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if (x0+y > 0 && x0+y < x0 && x0+y > 0){ + if(y0+x < eyeheight) + v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if(y0-x > 0) + v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if (x0-y > 0 && x0-y < x0 && x0-y > 0){ + if(y0+x < eyeheight) + v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if(y0-x > 0) + v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + } + + } + return; +} + +// lack of graphics functions use primitive algorithm to draw circle by coloring in rgb in image memory +void drawCircle(CrossHairInfo *chi, int x0, int y0, int width, int height, int cindex) +{ + + + + unsigned char r =0; + unsigned char g =0; + unsigned char b =0; + int x = 0, y, f; + unsigned int *v0; + int radius, ddF_x =1, ddF_y; + + if (eyeimage == NULL) return; + + switch(cindex){ + case CR_HAIR_COLOR: r=g=b = 255; break;//255,255,255 + case PUPIL_HAIR_COLOR: r=g=b = 255; break;//255,255,255 + case PUPIL_BOX_COLOR: g = 255; break;//0,255,0 + case SEARCH_LIMIT_BOX_COLOR: + case MOUSE_CURSOR_COLOR: r = 255; break;//255,0,0 + } + + + v0 = (unsigned int*) (eyeimage); + + radius = width / 2; + y= radius; + f = 1 - radius; + + ddF_y = -2 * radius; + x = 0; + y = radius; + x0 = x0 + width/2; + y0 = y0 + width/2; + + if (x0 - radius > 0 && x0+radius < eyewidth && y0+radius < eyeheight && y0-radius > 0){ + v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + v0[(eyewidth*eyeheight) - (y0-radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + v0[(eyewidth*eyeheight) - y0*eyewidth+(x0+radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + v0[(eyewidth*eyeheight) - y0*eyewidth+(x0-radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + + while( x < y ){ + + if(f >= 0){ + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + if (y0+y < eyeheight && y0+y > 0){ + if(x0+x < eyewidth) + v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if(x0-x > 0) + v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if (y0-y < eyeheight && y0-y > 0){ + if(x0+x < eyewidth) + v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if(x0-x > 0) + v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if(y0+x < eyeheight && y0+x > 0){ + if(x0+y < eyewidth) + v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if(x0-y >0) + v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + if(y0-x < eyeheight && y0-x > 0){ + if(x0+y < eyewidth) + v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if(x0-y >0) + v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + } + return; +} +// added by NJ @ SR Research Sept 2010 +void drawLozenge(CrossHairInfo *chi, int x0, int y0, int width, int height, int cindex) +{ + + unsigned char r =0; + unsigned char g =0; + unsigned char b =0; + int x = 0, y; + int y2, y1; + unsigned int *v0; + + if (eyeimage == NULL) return; + // clip to prevent memory issues and wrap around + + switch(cindex) { + + case CR_HAIR_COLOR: r=g=b = 255; break;//255,255,255 + case PUPIL_HAIR_COLOR: r=g=b = 255; break;//255,255,255 + case PUPIL_BOX_COLOR: g = 255; break;//0,255,0 + case SEARCH_LIMIT_BOX_COLOR: + case MOUSE_CURSOR_COLOR: r = 255; break;//255,0,0 + } + +if(eyeimage != NULL) { + + // Retrieve v0 as pointer to pixel row in output buffer: + v0 = (unsigned int*) (eyeimage); + + // is it a circle? + if(abs(width - height) < 4) + { + drawCircle(chi, x0, y0, width, height, cindex); + + }else { // non. ligne + + int minwidth = width * (width < height) + height * (width >= height); //min(width,height); + if (width == minwidth) // width was smaller + { + y1 = y0+width/2; + x = x0; + y2 = y1+(height-width) + 1; + // only draw vertical lines + for (y=y1 ;y< y2;y++) { + // be careful to clip or memory error occurs + if (y < eyeheight && y > 0 && x > 0 && x < eyewidth) + v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + if (y < eyeheight && y > 0 && (x+ width) > 0 && (x+width) < eyewidth) + v0[(eyewidth*eyeheight) - y*eyewidth+(x+width)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + + } // Now draw the semi circles + drawSemiCircle(chi,x0,y0,width,UPSIDE,cindex); + drawSemiCircle(chi,x0,(y0+width/2+(height-width)),width,DOWNSIDE,cindex); + + + }else{ // height smaller + + int x1 = x0+height/2; + int x2 = x1+(width-height); + y=y0; + //horizontal + for (x=x1 ;x<= x2;x++) { + if ( x < eyewidth && x > 0 && y > 0 && y < eyeheight) + v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + + y = y0+height; + for (x=x1 ;x<= x2;x++) { + if ( x < eyewidth && x > 0 && y < eyeheight && y > 0) + v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + + drawSemiCircle(chi,(x0+height/2+(width-height)),y0,height,RIGHTSIDE,cindex); + drawSemiCircle(chi,x0,y0,height,LEFTSIDE,cindex); + + } + } +} + return; +} + +// added by NJ @ SR Research LTD +void drawLine(CrossHairInfo *chi, int x1, int y1, int x2, int y2, int cindex) +{ + + unsigned char r =0; + unsigned char g =0; + unsigned char b =0; + int dx, dy; + int x, y, ch; + INT16 xc[4],yc[4], enabled; + unsigned int *v0; + int xx1, xx2, yy1, yy2; + + if (eyeimage == NULL) return; + // get camera channel. 2 = head. + ch = get_image_xhair_data(xc, yc, &enabled); + + // clip if fail + if (ch == 2){ + + if (x1<0) x1=0; + if (x2<0) x2=0; + if (y1<0) y1=0; + if (y2<0) y2=0; + + if (x1>eyewidth-1) x1=eyewidth-1; + if (x2>eyewidth-1) x2=eyewidth-1; + if (y1>eyeheight-1) y1=eyeheight-1; + if (y2>eyeheight-1) y2=eyeheight-1; + } + + + switch(cindex) + { + case CR_HAIR_COLOR: r=g=b = 255; break;//255,255,255 + case PUPIL_HAIR_COLOR: r=g=b = 255; break;//255,255,255 + case PUPIL_BOX_COLOR: g = 255; break;//0,255,0 + case SEARCH_LIMIT_BOX_COLOR: + case MOUSE_CURSOR_COLOR: r = 255; break;//255,0,0 + } + // Memory pointer to malloc()'ed image pixel buffer that holds the + // image data for a RGBA8 texture with the most recent eye camera image: + // eyeimage is a global variable + if(eyeimage != NULL) { + + // Retrieve v0 as pointer to pixel row in output buffer: image is upside down + v0 = (unsigned int*) ( eyeimage); + + dx = x2 - x1; + dy = y2 - y1; + + + if(ch != 2){ + // never diagonal here y1 is always < y2 + for (y=y1 ;y< y2;y++) { + x = x1 + (dx) * (y - y1)/(dy); + if (y < eyeheight && x < eyewidth && y >0 && x >0 && (eyewidth*eyeheight) - y*eyewidth+x > 0) + v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + } + + //account for diagonal lines in binocular mode ( x1 and y1 may be > x2 and y2) + + // vertical + if (dx == 0 ){ + if(y1>y2){ + yy1 = y2; + yy2 = y1; + }else { + yy1 = y1; + yy2 = y2; + } + + for (y=yy1 ;y< yy2;y++) { + x = x1 + (dx) * (y - y1)/(dy); + if (y < eyeheight && x < eyewidth && y >0 && x >0 && (eyewidth*eyeheight) - y*eyewidth+x > 0) + v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + } + else { + if(x1>x2){ + xx1 = x2; + xx2 = x1; + }else { + xx1 = x1; + xx2 = x2; + } + + for (x=xx1 ;x< xx2;x++) { + y = y1 + (dy) * (x - x1)/(dx); + if (y < eyeheight && x < eyewidth && y >0 && x >0 && (eyewidth*eyeheight) - y*eyewidth+x > 0) + v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r); + } + } + } + return; +} + + +typedef void (*GET_MOUSE_LOC)(CrossHairInfo *dt, int *x, int *y, int *state); +GET_MOUSE_LOC mouseLoc = NULL; + +void ELCALLTYPE set_mouse_loc_callback(GET_MOUSE_LOC get_mouse_loc) +{ + mouseLoc = get_mouse_loc; +} + + +void getMouseState(CrossHairInfo *chi, int *rx, int *ry, int *rstate) +{ + float x =0; + float y =0; + PsychGenericScriptType *inputs[1]; + PsychGenericScriptType *outputs[1]; + double* callargs; + double* outputargs; + float ar[7]; + float w,h; + int i; + + inputs[0] = mxCreateDoubleMatrix(1, 4, mxREAL); + callargs = mxGetPr(inputs[0]); + + callargs[0] = 16; // 16 == Command code for mouse button event + + Psych_mexCallMATLAB(1, outputs, 1, inputs, eyelinkDisplayCallbackFunc); + + outputargs = mxGetData(outputs[0]); + for (i=0;i<7;i++){ + ar[i] = (int) outputargs[i]; + } + + // Release our matrix again: + mxDestroyArray(inputs[0]); + mxDestroyArray(outputs[0]); + + w = ar[0]; + h = ar[1]; + x = floor((ar[2] - ((w/2) - ar[4]/2)) * ((float)eyewidth/ar[4])); + y = floor((ar[3] - ((h/2) - ar[5]/2)) * ((float)eyeheight/ar[5])); + + if(x>0 && y >0 && x <= eyewidth && y <= eyeheight) + { + *rx = (int)x; + *ry = (int)y; + *rstate = (int)ar[6]; + }else + { + if(x<=0 && y<=0) + { + + *rx = 1; + *ry = 1; + } + else if(x<0 && y>eyeheight) + { + + *rx = 1; + *ry = eyeheight; + } + else if(x>eyewidth && y>eyeheight) + { + + *rx = eyewidth; + *ry = eyeheight; + } + else if(x>eyewidth && y<0) + { + + *rx = eyewidth; + *ry = 1; + } + else if(x>eyewidth && y>0 && y0 && y<=eyeheight) + { + + *rx = 1; + *ry = y; + } + else if(y<0 && x>0 && x<=eyewidth) + { + + *rx = x; + *ry = 1; + } + else if(y>eyeheight && x>0 && x<=eyewidth) + { + + *rx = x; + *ry = eyeheight; + } + } + + return; +} + + +// PsychEyelink_draw_image_line() retrieves exactly one scanline worth of eye camera +// image data. Once a full image has been received, it has to trigger the actual image +// display: +static void ELCALLBACK PsychEyelink_draw_image_line(INT16 width, INT16 line, INT16 totlines, byte *pixels) +{ + PsychGenericScriptType *inputs[1]; + PsychGenericScriptType *outputs[1]; + double* callargs; + double teximage; + static INT16 lastline = -1; + static int wrapcount = 0; + static double tlastwrap = 0.0; + double tnow; + int rc; + byte* p; + unsigned int *v0; + short i; + CrossHairInfo crossHairInfo; + + if (Verbosity() > 8) printf("Eyelink: Entering PsychEyelink_draw_image_line()\n"); + + // Callbacks forcefully disabled by error-handling? Simply return with no-op, if so: + if (0 == eyelinkDisplayCallbackFunc[0]) return; + + // width, line, totlines within valid range? + if (width < 1 || width > eyewidth || line < 1 || line > eyeheight || totlines < 1 || totlines > eyeheight) { + printf("EYELINK: WARNING! Eye camera image with invalid parameters received! (width = %i, line = %i, totlines = %i out of sane range %i x %i)!\n", + width, line, totlines, eyewidth, eyeheight); + printf("EYELINK: WARNING! Will try to clamp to valid values, but results may be junk.\n"); + width = eyewidth; + line = (line < 1) ? 1 : line; + line = (line > eyeheight) ? line : eyeheight; + totlines = (totlines < 1) ? 1 : totlines; + totlines = (totlines > eyeheight) ? totlines : eyeheight; + } + + + + + // Data structures properly initialized? + if(eyeimage != NULL) { + // Retrieve p as pointer to input pixel index color buffer: + p = pixels; + + // Retrieve v0 as pointer to pixel row in output buffer: + v0 = (unsigned int*) (( eyeimage + ( (totlines - line) * width * 4 ) )); + + // Copy one row of pixels from input- to output buffer: + // This is a bit optimized, but we could do more if we're really bored with life ;-) + for(i=0; i < width; i++) { + // Decode pixel index value in *p via palette color lookup table and store to output buffer: + *(v0++) = palmap32[*p++]; + } + + if (Verbosity() > 8) printf("Eyelink: PsychEyelink_draw_image_line(): Scanline %i received.\n", (int) line); + + // Premature wraparound? + if (line < lastline) { + // Premature wraparound due to too slow processing. Increase wrapcounter: + wrapcount++; + } + + // More than some threshold? + if (wrapcount > 10) { + // Spill a warning? + PsychGetAdjustedPrecisionTimerSeconds(&tnow); + if (tnow - tlastwrap > 2.0) { + // Last invocation longer than 2 seconds away: + // Output some warning to console... + if (Verbosity() > 1) { + printf("Eyelink: Warning: Skipped videoframes from eye camera detected within last seconds (count=%i)\n", wrapcount); + printf("Eyelink: Warning: Timing problems on your machine or network problems on tracker connection?!?\n\n"); + } + + // Update / Reset detector: + tlastwrap = tnow; + wrapcount = 0; + } + } + + // Update skip detector: + lastline = line; + + // Complete new eye image received? + if (line == totlines) { + // Yes. Our eyeimage buffer contains a new image. + + // Reset skip detector: + lastline = -1; + + crossHairInfo.w = eyewidth; + crossHairInfo.h = eyeheight; + crossHairInfo.drawLozenge = drawLozenge; + crossHairInfo.drawLine = drawLine; + crossHairInfo.getMouseState = mouseLoc?mouseLoc:getMouseState; + crossHairInfo.userdata = eyeimage; + + eyelink_draw_cross_hair(&crossHairInfo); + + + // Compute double-encoded Matlab/Octave compatible memory pointer to image buffer: + teximage = PsychPtrToDouble((void*) eyeimage); + + + + + // Ok, teximage is a memory pointer to our image buffer, encoded as a double. + // Now we need to call our Matlab callback function which actually converts + // the data in our internal image buffer into a PTB texture, then draws that + // texture etc. to display the new eye camera image. + if (Verbosity() > 6) printf("Eyelink: PsychEyelink_draw_image_line(): All %i Scanlines received. Calling Runtime!\n", (int) line); + + // Create a Matlab double matrix with 4 elements: 1st is command code '1' + // 2nd is the double pointer, 3r//d is image width, 4th is image height: + outputs[0] = NULL; + inputs[0] = mxCreateDoubleMatrix(1, 4, mxREAL); + callargs = mxGetPr(inputs[0]); + + callargs[0] = 1; // 1 == Command code for "Show eye image". + callargs[1] = teximage; + callargs[2] = eyewidth; + callargs[3] = eyeheight; + + rc = Psych_mexCallMATLAB(0, outputs, 1, inputs, eyelinkDisplayCallbackFunc); + if(rc) { + printf("EYELINK: WARNING! Failed to call eyelink camera image display callback function %s [rc=%i]!\n", eyelinkDisplayCallbackFunc, rc); + printf("EYELINK: WARNING! Make sure that function is on your Matlab/Octave path and properly initialized.\n"); + printf("EYELINK: WARNING! May also be an error during execution of that function. Type ple at command prompt for error messages.\n"); + printf("EYELINK: WARNING! Auto-Disabling all callbacks to the runtime environment for safety reasons.\n"); + eyelinkDisplayCallbackFunc[0] = 0; + } + + // Release our matrix again: + mxDestroyArray(inputs[0]); + } + } + + + + + // Done. + return; +} + +// PsychEyelink_set_image_palette() sets the color palette for decoding 1-byte color index +// values in an eyelink camera image into RGB8 1-byte-per-color-component color values: +static void ELCALLBACK PsychEyelink_set_image_palette(INT16 ncolors, byte r[], byte g[], byte b[]) +{ + short i; + + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_set_image_palette()\n"); + + if (ncolors > 256) { + printf("EYELINK: WARNING! Invalid color palette size %i (> 256 colors!) received from eyelink: Clamping to 256 colors.\n", (int) ncolors); + ncolors = 256; + } + + // Copy given r,g,b color arrays into internal remapping table palmap32: + for(i=0; i < ncolors; i++) { + // Format is ABGR + palmap32[i] = 0xFF000000 | ((unsigned int) b[i] << 16) | ((unsigned int) g[i] << 8) | ((unsigned int) r[i]); + } + + return; +} + +static INT16 ELCALLBACK PsychEyelink_setup_cal_display(void) +{ + //nj added "hack" to disable flashing instructions in drift correction and to enable sending cal and val results + int mode = -1; + + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_setup_cal_display()\n"); + + mode = eyelink_tracker_mode(); + + if (mode == 1 || mode ==9 ) //EL_DRIFT_CORR_MODE) + if (0xdeadbeef == PsychEyelinkCallRuntime(17, 0, 0, NULL)) { + // Error condition. Return error to eyelink runtime: + return(-1); + } + + // Tell runtime to setup calibration display: Command code 7. + if (0xdeadbeef == PsychEyelinkCallRuntime(7, 0, 0, NULL)) { + // Error condition. Return error to eyelink runtime: + return(-1); + } + + // Return success: + return(0); +} + +static void ELCALLBACK PsychEyelink_exit_cal_display(void) +{ + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_exit_cal_display()\n"); + + // Tell runtime to exit calibration display: Command code 11. + PsychEyelinkCallRuntime(11, 0, 0, NULL); + return; +} + +static void ELCALLBACK PsychEyelink_clear_display(void) +{ + //NJ modified to add msg to call back 6 with cal and val result + char strMessage[256]; + int result =-1; + // Clear strings + memset(strMessage, 0, sizeof(strMessage)); + + + result = eyelink_cal_message(strMessage); + + + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_clear_display()\n"); + + // Tell runtime to clear display: Command code 6. + PsychEyelinkCallRuntime(6, 0, 0, strMessage);//NULL); + + + return; +} + +static void ELCALLBACK PsychEyelink_draw_cal_target(INT16 x, INT16 y) +{ + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_draw_cal_target(): x=%i y=%i.\n", (int) x, (int) y); + + // Tell runtime about where to draw calibration target: Command code 5. + PsychEyelinkCallRuntime(5, (int) x, (int) y, NULL); + + return; +} + +static void ELCALLBACK PsychEyelink_erase_cal_target(void) +{ + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_erase_cal_target():\n"); + + // Tell runtime about clear calibration target: Command code 10. + PsychEyelinkCallRuntime(10, 0, 0, NULL); + + return; +} + +static void ELCALLBACK PsychEyelink_image_title(INT16 threshold, char *title) +{ + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_image_title(): threshold = %i : Title = %s\n", (int) threshold, title); + + //mexPrintf("C code: %s ... %d\n", title, threshold); + //fflush(stdout); + + // Tell runtime about image title: Command code 4. + PsychEyelinkCallRuntime(4, (int) threshold, 0, title); + + return; +} + +#ifndef ELKEY_DOWN +#define ELKEY_DOWN 1 //temporary while we wait for sr-research's lib to get updated with this +#endif + +static INT16 ELCALLBACK PsychEyelink_get_input_key(InputEvent *keyinput) +{ + int ky = 0; + double tnow; + static double tlastquery = 0; + const double tmininterval = 0.1; // Allow one query every 0.1 seconds. + InputEvent *key_input = keyinput; + + // Throttling routine: + // We don't want key queries to call out to the runtime too often, as this + // creates a quite significant overhead, e.g., approx. 1 msec for a KbCheck + // for a fast 2009'ish machine on OS/X! + PsychGetAdjustedPrecisionTimerSeconds(&tnow); + if (tnow - tlastquery < tmininterval) { + // Last invocation less than tmininterval seconds away. Throttle this, + // we just return "no key pressed". + if (Verbosity() > 9) printf("Eyelink: In PsychEyelink_get_input_key(): Throttling...\n"); + return(0); + } + else { + // Last invocation longer than tmininterval seconds away. Accept this + // query and update timestamp: + tlastquery = tnow; + } + + if (Verbosity() > 7) printf("Eyelink: Entering PsychEyelink_get_input_key()\n"); + + // Call runtime for keycode of pressed key (command code 2): + if ((ky = PsychEyelinkCallRuntime(2, 0, 0, NULL)) == 0xdeadbeef) { + // Error condition in runtime callback! Can't progress. We try to + // shutdown the current eyelink runtime operation by sending a fake + // keycode corresponding to the terminate key: + if (Verbosity() > 0) printf("Eyelink: In PsychEyelink_get_input_key(): Error condition detected: Trying to send TERMINATE_KEY abort keycode!\n"); + ky = TERMINATE_KEY; + } + + if (ky > 0) { + // Fill Eyelinks InputEvent struct: + memset(key_input, 0, sizeof(InputEvent)); + key_input->key.key = ky; + key_input->key.state = ELKEY_DOWN; + key_input->key.type = KEYINPUT_EVENT; + key_input->key.modifier = 0; //event.key.keysym.mod; + key_input->key.unicode = 0; //event.key.keysym.unicode; + + // One key pressed: + return(1); + } + else { + // No key pressed: + return(0); + } +} + +static void ELCALLBACK PsychEyelink_alert_printf_hook(const char *msg) +{ + // Print error message to runtime console if error output is allowed: + if (Verbosity() > 3) printf("Eyelink: Alert! Eyelink says: %s.\n\n", msg); + + // Tell runtime about alert condition: Command code 3. + PsychEyelinkCallRuntime(3, 0, 0, (char*) msg); + + return; +} + +static void ELCALLBACK PsychEyelink_cal_target_beep_hook(void) +{ + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_cal_target_beep_hook():\n"); + + PsychEyelinkCallRuntime(12, 0, 0, NULL); + return; +} + +static void ELCALLBACK PsychEyelink_dc_target_beep_hook(void) +{ + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_dc_target_beep_hook():\n"); + + PsychEyelinkCallRuntime(13, 0, 0, NULL); + return; +} + +static void ELCALLBACK PsychEyelink_cal_done_beep_hook(INT16 error) +{ + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_cal_done_beep_hook():\n"); + + PsychEyelinkCallRuntime(14, (int) error, 0, NULL); + return; +} + +static void ELCALLBACK PsychEyelink_dc_done_beep_hook(INT16 error) +{ + if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_dc_done_beep_hook():\n"); + + PsychEyelinkCallRuntime(15, (int) error, 0, NULL); + return; +} + diff --git a/PsychSourceGL/Source/Common/Eyelink/PsychEyelink.h b/PsychSourceGL/Source/Common/Eyelink/PsychEyelink.h index 2fd9cd8a5b..d88f9e8ed9 100644 --- a/PsychSourceGL/Source/Common/Eyelink/PsychEyelink.h +++ b/PsychSourceGL/Source/Common/Eyelink/PsychEyelink.h @@ -51,6 +51,7 @@ void PsychEyelink_init_core_graphics(const char* callback); void PsychEyelink_uninit_core_graphics(void); void PsychEyelink_TestEyeImage(void); void PsychEyelink_dumpHookfunctions(void); +int PsychEyelinkCallRuntime(int cmd, int x, int y, char* msg); // Defined in EyelinkSynopsis.c void InitializeSynopsis(); diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/contents.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/Contents.m similarity index 99% rename from Psychtoolbox/PsychHardware/EyelinkToolbox/contents.m rename to Psychtoolbox/PsychHardware/EyelinkToolbox/Contents.m index 7f3b2fec67..02671a3ed4 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/contents.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/Contents.m @@ -1,4 +1,4 @@ -% EyelinkToolbox. +% EyelinkToolbox % % The EyelinkToolbox can be used to ceate eye-movement experiments and % control the SR-Research Eyelink(c) gazetrackers @@ -115,3 +115,4 @@ % Enno Peters, Frans Cornelissen and John Palmer % Groningen, 27-11-2002 % +% See also SR-RESEARCHDEMOS diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/Contents.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/Contents.m new file mode 100644 index 0000000000..0ec6bf2d48 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/Contents.m @@ -0,0 +1,7 @@ +% EyelinkToolbox:EyelinkBasic +% collection of essential functions for the eyelink toolbox +% +% EyelinkBasicLegacy - Deprecated functionality retained for warnings +% +% See also SR-RESEARCHDEMOS +% diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/Eyelink.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/Eyelink.m index 3115d599a6..d9e1187f90 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/Eyelink.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/Eyelink.m @@ -1,6 +1,6 @@ function varargout = Eyelink(varargin) % The EyelinkToolbox can be used to ceate eye-movement experiments and -% control the SR-Research Eyelink gazetrackers (http://www.eyelinkinfo.com/) +% control the SR-Research Eyelink gazetrackers (https://www.sr-research.com/) % from within Octave and Matlab. % % It is incorporated into the PsychToolbox (http://www.psychtoolbox.org/) @@ -28,5 +28,10 @@ % More information on this toolbox can be found in the file: % EyelinkToolbox/contents.m (help EyelinkToolbox) % +% The EyeLink Toolbox requires the EyeLink Developers Kit installed to +% provide the dependency libraries. Please register a user account (free) +% at https://www.sr-support.com for access to all support related +% information and software downlaods from SR-Research +% AssertMex('Eyelink.m'); diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkCalDoneBeep.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkCalDoneBeep.m new file mode 100644 index 0000000000..2a66425e55 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkCalDoneBeep.m @@ -0,0 +1,4 @@ +function err=EyelinkCalDoneBeep(el, error) +warning('EyelinkToolbox:LegacyCalDoneBeep',['The function EyelinkCalDoneBeep() is deprecated. Please update your script ', ... + 'to use the current method for handling camera setup mode callbacks with PsychEyelinkDispatchCallback.m.']); +warning('off', 'EyelinkToolbox:LegacyCalDoneBeep'); diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkCalTargetBeep.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkCalTargetBeep.m new file mode 100644 index 0000000000..9c020d5be9 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkCalTargetBeep.m @@ -0,0 +1,4 @@ +function err=EyelinkCalTargetBeep(el) +warning('EyelinkToolbox:LegacyCalTargetBeep',['The function EyelinkCalTargetBeep() is deprecated. Please update your script ', ... + 'to use the current method for handling camera setup mode callbacks with PsychEyelinkDispatchCallback.m.']); +warning('off', 'EyelinkToolbox:LegacyCalTargetBeep'); diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkClearCalDisplay.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkClearCalDisplay.m new file mode 100644 index 0000000000..842c1f465f --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkClearCalDisplay.m @@ -0,0 +1,7 @@ +function EyelinkClearCalDisplay(el) +warning('EyelinkToolbox:LegacyClearCalDisplay',['The function EyelinkClearCalDisplay() is deprecated. Please update ', ... + 'your script to use the current method for handling camera setup mode callbacks with PsychEyelinkDispatchCallback.m.']); +warning('off', 'EyelinkToolbox:LegacyClearCalDisplay'); + +Screen( 'FillRect', el.window, el.backgroundcolour ); % clear_cal_display() +Screen( 'Flip', el.window); \ No newline at end of file diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkDoDriftCorrect.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkDoDriftCorrect.m new file mode 100644 index 0000000000..97bc65ecf6 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkDoDriftCorrect.m @@ -0,0 +1,153 @@ +function result=EyelinkDoDriftCorrect(el, x, y, draw, allowsetup) +% USAGE: result=EyelinkDoDriftCorrect(el [, x, y, draw, allowsetup]) +% +% NOTE: This function is deprecated, unmaintained, and not recommended anymore. +% Use EyelinkDoDriftCorrection() for a modern solution instead. +% +% el: eyelink default values +% x,y: position of driftcorrection target +% draw: set to 1 to draw driftcorrection target +% allowsetup: set to 1 to allow to go in to go to trackersetup +% +% Note that EyelinkDoDriftCorrect() internally uses Beeper() and Snd() to play +% auditory feedback tones if el.targetbeep=1 or el.feedbackbeep=1 and the +% el.callback function is set to the default PsychEyelinkDispatchCallback(). +% If you want to use PsychPortAudio in a script that also calls EyelinkDoDriftCorrect, +% then read "help Snd" for instructions on how to provide proper interoperation +% between PsychPortAudio and the feedback sounds created by Eyelink. +% + +% /********* PERFORM DRIFT CORRECTION ON TRACKER *******/ +% /* Performs a drift correction, with target at (x,y). */ +% /* We are explicitly entering a tracker subfunction, */ +% /* so we have to handle link output explicitly. */ +% /* When we finish or abort the drift correction on the tracker, */ +% /* it won't go to another mode until we tell it to. */ +% /* For drift coorection, we can use the */ +% /* drift correction result message to tell when it's done, */ +% /* and what the result was. */ + +% /* Here we display the target ourselves (ignore target updates), */ +% /* wait for local spacebar or for operator trigger or */ +% /* ESC key abort. */ +% /* If operator aborts with ESC, we assume there's a setup */ +% /* problem and go to the setup menu. */ + +% /* RETURNS: 0 if OK, 27 if Setup menu was called. */ +% + +% Eyelink Toolbox version +% 12-05-01 fwc created first version +% 12-05-01 fwc disabled unconditional erasing of screen +% 02-06-01 fwc removed use of global el, as suggested by John Palmer. +% 18-10-02 fwc made sure missing variables were filled in with defaults +% 15-06-10 fwc added code for new callback version + +warning('EyelinkToolbox:LegacyDoDriftCorrect',['The function EyelinkDoDriftCorrect() is deprecated. Please update ', ... + 'your script to use the current method, EyelinkDoDriftCorrection(), for handling camera setup mode callbacks with PsychEyelinkDispatchCallback.m.']); +warning('off', 'EyelinkToolbox:LegacyDoDriftCorrect'); + +result=-1; % initialize +if nargin < 1 || ~exist('el', 'var') || isempty(el) + error( 'USAGE: result=EyelinkDoDriftCorrect(el [, x, y, draw, allowsetup])' ); +end + +% fill in missing variables +if ~exist('x', 'var') || ~exist('y', 'var') || isempty(x) || isempty(y) + [x,y]=WindowCenter(el.window); +end + +if ~exist('draw', 'var') || isempty(draw) + draw=1; +end + +if ~exist('allowsetup', 'var') || isempty(allowsetup) + allowsetup=1; +end + +targetrect=[0 0 0 0]; + +key=1; +while key~= 0 + [key, el]=EyelinkGetKey(el); % dump old keys +end + +if draw==1 + EyelinkClearCalDisplay(el); % setup_cal_display() + targetrect=EyelinkDrawCalTarget(el, x, y); % we are told where it should be. +end + +if el.targetbeep==1 + EyelinkCalTargetBeep(el); +end + +status=Eyelink( 'DriftCorrStart', x, y); + +tickcount=0; +result=el.NO_REPLY; +while result==el.NO_REPLY + % check for result of drift correction + result=Eyelink( 'CalResult' ); + + [key, el]=EyelinkGetKey(el); % getkey() HANDLE LOCAL KEY PRESS + + if el.mousetriggersdriftcorr==1 % allow mouse to trigger drift correction (fwc trick) + [mx,my,button] = GetMouse(el.window); + if button==1 + if IsInRect(mx,my,targetrect) + key=el.SPACE_BAR; % fake a key press when mouse is pressed and on target + end + end + end + + switch key + case el.TERMINATE_KEY, % breakout key code + result=el.TERMINATE_KEY; + return; + case { 0, el.JUNK_KEY } % No key + case el.ESC_KEY, % 27 + if el.allowlocalcontrol==1 + result=el.ESC_KEY; + end + if Eyelink('IsConnected') ==-1 + result=el.ESC_KEY; + end + case el.SPACE_BAR, % 32: we trigger ourselves + if el.allowlocaltrigger==1 + Eyelink( 'AcceptTrigger'); + end + if Eyelink('IsConnected') == el.dummyconnected + result=0; + end + otherwise, % Echo to tracker for remote control + if el.allowlocalcontrol==1 + Eyelink('SendKeyButton', key, 0, el.KB_PRESS ); + end + end % switch key +end % while cal_result==NO_REPLY + +if draw==1 + EyelinkEraseCalTarget(el, targetrect); % bit superfluous actually + EyelinkClearCalDisplay(el); % exit_cal_display() +end + +if result==el.ESC_KEY || result==-1 % Did we abort drift correction? + % yes: go to setup menu to fix any problems + if el.targetbeep==1 + EyelinkCalDoneBeep(el, 0); + end + if allowsetup==1 + EyelinkLegacyDoTrackerSetup(el); + else + Eyelink( 'SetOfflineMode'); + end +else + % Otherwise, we apply the drift correction + if el.targetbeep==1 + EyelinkCalDoneBeep(el, 1); + end + Eyelink('ApplyDriftCorr' ); + result=0; +end + +return; diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDrawCalTarget.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkDrawCalTarget.m similarity index 58% rename from Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDrawCalTarget.m rename to Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkDrawCalTarget.m index bec6cda55d..b4d35ac25b 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDrawCalTarget.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkDrawCalTarget.m @@ -1,16 +1,19 @@ function rect=EyelinkDrawCalTarget(el, x, y) - % draw simple calibration target % % USAGE: rect=EyelinkDrawCalTarget(el, x, y) % -% el: eyelink default values -% x,y: position at which it should be drawn -% rect: +% el: eyelink default values +% x,y: position at which it should be drawn +% rect: % simple, standard eyelink version % 22-06-06 fwc OSX-ed +warning('EyelinkToolbox:LegacyDrawCalTarget',['The function EyelinkDrawCalTarget() is deprecated. Please update your ', ... + 'script to use the current method for handling camera setup mode callbacks with PsychEyelinkDispatchCallback.m.']); +warning('off', 'EyelinkToolbox:LegacyDrawCalTarget'); + [width, height]=Screen('WindowSize', el.window); size=round(el.calibrationtargetsize/100*width); inset=round(el.calibrationtargetwidth/100*width); diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkEraseCalTarget.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkEraseCalTarget.m new file mode 100644 index 0000000000..f16460afd6 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkEraseCalTarget.m @@ -0,0 +1,16 @@ +function EyelinkEraseCalTarget(el, rect) +% erase calibration target +% +% USAGE: EyelinkEraseCalTarget(el, rect) +% +% el: eyelink default values +% rect: rect that will be filled with background colour + +warning('EyelinkToolbox:LegacyEraseCalTarget',['The function EyelinkEraseCalTarget() is deprecated. Please update your ', ... + 'script to use the current method for handling camera setup mode callbacks with PsychEyelinkDispatchCallback.m.']); +warning('off', 'EyelinkToolbox:LegacyEraseCalTarget'); + +if ~IsEmptyRect(rect) + Screen( 'FillOval', el.window, el.backgroundcolour, rect ); + Screen( 'Flip', el.window); +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkLegacyDoTrackerSetup.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkLegacyDoTrackerSetup.m new file mode 100644 index 0000000000..3bc582c1ed --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkLegacyDoTrackerSetup.m @@ -0,0 +1,116 @@ +function result=EyelinkLegacyDoTrackerSetup(el, sendkey) +% USAGE: result=EyelinkLegacyDoTrackerSetup(el [, sendkey]) +% +% NOTE: This function is deprecated, unmaintained, and not recommended anymore. +% Use EyelinkDoTrackerSetup() for a modern solution instead. +% +% el: Eyelink default values +% +% sendkey: set to go directly into a particular mode +% sendkey is optional and ignored if el.callback is defined for +% callback based tracker setup. +% +% 'v', start validation +% 'c', start calibration +% 'd', start driftcorrection +% 13, or el.ENTER_KEY, show 'eye' setup image +% +% Note that EyelinkLegacyDoTrackerSetup() internally uses Beeper() and Snd() to play +% auditory feedback tones if el.targetbeep=1 or el.feedbackbeep=1 and the +% el.callback function is set to the default PsychEyelinkDispatchCallback(). +% If you want to use PsychPortAudio in a script that also calls EyelinkLegacyDoTrackerSetup, +% then read "help Snd" for instructions on how to provide proper interoperation +% between PsychPortAudio and the feedback sounds created by Eyelink. + +% +% 02-06-01 fwc removed use of global el, as suggest by John Palmer. +% el is now passed as a variable, we also initialize Tracker state bit +% and Eyelink key values in 'initeyelinkdefaults.m' +% 15-10-02 fwc added sendkey variable that allows to go directly into a particular mode +% 22-06-06 fwc OSX-ed +% 15-06-10 fwc added code for new callback version + +warning('EyelinkToolbox:LegacyDoTrackerSetup',['Use of the function EyelinkDoTrackerSetup() without providing a callback handler ', ... + '(such as the included PsychEyelinkDispatchCallback) is deprecated. Please update your script to use the currently supported conventions.']); +warning('off', 'EyelinkToolbox:LegacyDoTrackerSetup'); + +result=-1; +if nargin < 1 + error( 'USAGE: result=EyelinkLegacyDoTrackerSetup(el [,sendkey])' ); +end + +% if we have the new callback code, we call it. +if ~isempty(el.callback) + error('el.callback is not empty. Legacy functions not supported when callback is set.'); +end + +Eyelink( 'StartSetup' ); % start setup mode +Eyelink( 'WaitForModeReady', el.waitformodereadytime ); % time for mode change + +EyelinkClearCalDisplay(el); % setup_cal_display() +key=1; +while key~= 0 + key=EyelinkGetKey(el); % dump old keys +end + +% go directly into a particular mode +if nargin==2 && ~isempty(sendkey) + if el.allowlocalcontrol==1 + switch lower(sendkey) + case{ 'c', 'v', 'd', el.ENTER_KEY} + forcedkey=double(sendkey(1,1)); + Eyelink('SendKeyButton', forcedkey, 0, el.KB_PRESS ); + end + end +end + +tstart=GetSecs; +stop=0; +while stop==0 && bitand(Eyelink( 'CurrentMode'), el.IN_SETUP_MODE) + + i=Eyelink( 'CurrentMode'); + + if ~Eyelink( 'IsConnected' ) + stop=1; + break; + end + + if bitand(i, el.IN_TARGET_MODE) % calibrate, validate, etc: show targets + EyelinkTargetModeDisplay(el); + elseif bitand(i, el.IN_IMAGE_MODE) % display image until we're back + if Eyelink ('ImageModeDisplay')==el.TERMINATE_KEY + result=el.TERMINATE_KEY; + return; % breakout key pressed + else + EyelinkClearCalDisplay(el); % setup_cal_display() + end + end + + [key, el]=EyelinkGetKey(el); % getkey() HANDLE LOCAL KEY PRESS + if 1 && key~=0 && key~=el.JUNK_KEY % print pressed key codes and chars + fprintf('%d\t%s\n', key, char(key) ); + end + + switch key + case el.TERMINATE_KEY % breakout key code + result=el.TERMINATE_KEY; + return; + case { 0, el.JUNK_KEY } % No or uninterpretable key + case el.ESC_KEY + if Eyelink('IsConnected') == el.dummyconnected + stop=1; % instead of 'goto exit' + end + if el.allowlocalcontrol==1 + Eyelink('SendKeyButton', key, 0, el.KB_PRESS ); + end + otherwise % Echo to tracker for remote control + if el.allowlocalcontrol==1 + Eyelink('SendKeyButton', double(key), 0, el.KB_PRESS ); + end + end +end % while IN_SETUP_MODE + +% exit: +EyelinkClearCalDisplay(el); % exit_cal_display() +result=0; +return; diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkTargetModeDisplay.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkTargetModeDisplay.m new file mode 100644 index 0000000000..c64eb13e78 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkBasicLegacy/EyelinkTargetModeDisplay.m @@ -0,0 +1,115 @@ +function result=EyelinkTargetModeDisplay(el) +% USAGE: result=EyelinkTargetModeDisplay(el) +% +% NOTE: This function is deprecated, unmaintained, and not recommended anymore. +% Use EyelinkDoTrackerSetup() for a modern solution instead. +% +% el: Eyelink default values +% +% History +% 15-05-01 fwc created first version +% 22-05-01 fwc little debugging +% 02-06-01 fwc removed use of global el, as suggested by John Palmer. +% 22-06-06 fwc OSX-ed + +warning('EyelinkToolbox:LegacyTargetModeDisplay',['The function EyelinkTargetModeDisplay() is deprecated. Please update your ', ... + 'script to use the current method EyelinkDoTrackerSetup() for handling camera setup mode callbacks with PsychEyelinkDispatchCallback.m.']); +warning('off', 'EyelinkToolbox:LegacyTargetModeDisplay'); + +result=-1; % initialize +if nargin < 1 + error( 'USAGE: result=EyelinkTargetModeDisplay(el)' ); +end + +targetvisible = 0; % target currently drawn +targetrect=[0 0 0 0]; + +tx=el.MISSING; +ty=el.MISSING; + +otx=el.MISSING; % current target position +oty=el.MISSING; + +EyelinkClearCalDisplay(el); % setup_cal_display() + +key=1; +while key~= 0 + [key, el]=EyelinkGetKey(el); % dump old keys +end +% LOOP WHILE WE ARE DISPLAYING TARGETS +stop=0; +while stop==0 && bitand(Eyelink('CurrentMode'), el.IN_TARGET_MODE) + + if Eyelink( 'IsConnected' )==el.notconnected + result=-1; + return; + end; + + [key, el]=EyelinkGetKey(el); % getkey() HANDLE LOCAL KEY PRESS + + switch key + case el.TERMINATE_KEY, % breakout key code + EyelinkClearCalDisplay(el); % clear_cal_display(); + result=el.TERMINATE_KEY; + return; + case el.SPACE_BAR, % 32: accept fixation + if el.allowlocaltrigger==1 + Eyelink( 'AcceptTrigger'); + end + break; + case { 0, el.JUNK_KEY } % No key + case el.ESC_KEY, + if Eyelink('IsConnected') == el.dummyconnected + stop=1; + end + if el.allowlocalcontrol==1 + Eyelink('SendKeyButton', key, 0, el.KB_PRESS ); + end + otherwise, % Echo to tracker for remote control + if el.allowlocalcontrol==1 + Eyelink('SendKeyButton', key, 0, el.KB_PRESS ); + end + end % switch key + + + % HANDLE TARGET CHANGES + [result, tx, ty]= Eyelink( 'TargetCheck'); + + + % erased or moved: erase target + if (targetvisible==1 && result==0) || tx~=otx || ty~=oty + EyelinkEraseCalTarget(el, targetrect); + targetvisible = 0; + end + % redraw if invisible + if targetvisible==0 && result==1 + % fprintf( 'Target drawn at: x=%d, y=%d\n', tx, ty ); + + targetrect=EyelinkDrawCalTarget(el, tx, ty); + targetvisible = 1; + otx = tx; % record position for future tests + oty = ty; + if el.targetbeep==1 + EyelinkCalTargetBeep(el); % optional beep to alert subject + end + end + +end % while IN_TARGET_MODE + + +% exit: % CLEAN UP ON EXIT +if el.targetbeep==1 + if Eyelink('CalResult')==1 % does 1 signal success? + EyelinkCalDoneBeep(el, 1); + else + EyelinkCalDoneBeep(el, -1); + end +end + +if targetvisible==1 + EyelinkEraseCalTarget(el, targetrect); % erase target on exit, bit superfluous actually +end +EyelinkClearCalDisplay(el); % clear_cal_display(); + +result=0; +return; diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkCalDoneBeep.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkCalDoneBeep.m deleted file mode 100644 index 76df4acdf3..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkCalDoneBeep.m +++ /dev/null @@ -1,11 +0,0 @@ -function err=caldonebeep(el, error) - -if 0 -if error<=0 - err=SND('Play', el.calibrationfailedsound); -else - err=SND('Play', el.calibrationsuccesssound); -end -SND('Wait'); - -end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkCalTargetBeep.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkCalTargetBeep.m deleted file mode 100644 index ac60a3da6d..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkCalTargetBeep.m +++ /dev/null @@ -1,7 +0,0 @@ -function err=EyelinkCalTargetBeep(el) - -% not sure yet about OSX sound routines -if 0 -err=SND('Play', el.targetdisplaysound); -SND('Wait'); -end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkClearCalDisplay.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkClearCalDisplay.m deleted file mode 100644 index 2b76007a5c..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkClearCalDisplay.m +++ /dev/null @@ -1,4 +0,0 @@ -function EyelinkClearCalDisplay(el) - -Screen( 'FillRect', el.window, el.backgroundcolour ); % clear_cal_display() -Screen( 'Flip', el.window); diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDoDriftCorrect.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDoDriftCorrect.m deleted file mode 100644 index 297a76a6bf..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDoDriftCorrect.m +++ /dev/null @@ -1,158 +0,0 @@ -function result=EyelinkDoDriftCorrect(el, x, y, draw, allowsetup) - -% USAGE: result=EyelinkDoDriftCorrect(el [, x, y, draw, allowsetup]) -% -% el: eyelink default values -% x,y: position of driftcorrection target -% draw: set to 1 to draw driftcorrection target -% allowsetup: set to 1 to allow to go in to go to trackersetup -% -% Note that EyelinkDoDriftCorrect() internally uses Beeper() and Snd() to play -% auditory feedback tones if el.targetbeep=1 or el.feedbackbeep=1 and the -% el.callback function is set to the default PsychEyelinkDispatchCallback(). -% If you want to use PsychPortAudio in a script that also calls EyelinkDoDriftCorrect, -% then read "help Snd" for instructions on how to provide proper interoperation -% between PsychPortAudio and the feedback sounds created by Eyelink. -% - -% /********* PERFORM DRIFT CORRECTION ON TRACKER *******/ -% /* Performs a drift correction, with target at (x,y). */ -% /* We are explicitly entering a tracker subfunction, */ -% /* so we have to handle link output explicitly. */ -% /* When we finish or abort the drift correction on the tracker, */ -% /* it won't go to another mode until we tell it to. */ -% /* For drift coorection, we can use the */ -% /* drift correction result message to tell when it's done, */ -% /* and what the result was. */ - -% /* Here we display the target ourselves (ignore target updates), */ -% /* wait for local spacebar or for operator trigger or */ -% /* ESC key abort. */ -% /* If operator aborts with ESC, we assume there's a setup */ -% /* problem and go to the setup menu. */ - -% /* RETURNS: 0 if OK, 27 if Setup menu was called. */ -% - -% Eyelink Toolbox version -% 12-05-01 fwc created first version -% 12-05-01 fwc disabled unconditional erasing of screen -% 02-06-01 fwc removed use of global el, as suggested by John Palmer. -% 18-10-02 fwc made sure missing variables were filled in with defaults -% 15-06-10 fwc added code for new callback version - - -result=-1; % initialize -if nargin < 1 || ~exist('el', 'var') || isempty(el) - error( 'USAGE: result=EyelinkDoDriftCorrect(el [, x, y, draw, allowsetup])' ); -end - -% fill in missing variables -if ~exist('x', 'var') || ~exist('y', 'var') || isempty(x) || isempty(y) - [x,y]=WindowCenter(el.window); -end - -if ~exist('draw', 'var') || isempty(draw) - draw=1; -end - -if ~exist('allowsetup', 'var') || isempty(allowsetup) - allowsetup=1; -end - -% if we have the new callback code, we call it. -if ~isempty(el.callback) - result = Eyelink('DriftCorrStart', x, y, 1, draw, allowsetup); - return; -end - -% else we continue with the old version - -Eyelink('Command', 'heuristic_filter = ON'); - -targetrect=[0 0 0 0]; - -key=1; -while key~= 0 - [key, el]=EyelinkGetKey(el); % dump old keys -end - -if draw==1 - EyelinkClearCalDisplay(el); % setup_cal_display() - targetrect=EyelinkDrawCalTarget(el, x, y); % we are told where it should be. -end - -if el.targetbeep==1 - EyelinkCalTargetBeep(el); -end - -status=Eyelink( 'DriftCorrStart', x, y); - -tickcount=0; -result=el.NO_REPLY; -while result==el.NO_REPLY - % check for result of drift correction - result=Eyelink( 'CalResult' ); - - [key, el]=EyelinkGetKey(el); % getkey() HANDLE LOCAL KEY PRESS - - if el.mousetriggersdriftcorr==1 % allow mouse to trigger drift correction (fwc trick) - [mx,my,button] = GetMouse(el.window); - if button==1 - if IsInRect(mx,my,targetrect) - key=el.SPACE_BAR; % fake a key press when mouse is pressed and on target - end - end - end - - switch key - case el.TERMINATE_KEY, % breakout key code - result=el.TERMINATE_KEY; - return; - case { 0, el.JUNK_KEY } % No key - case el.ESC_KEY, % 27 - if el.allowlocalcontrol==1 - result=el.ESC_KEY; - end - if Eyelink('IsConnected') ==-1 - result=el.ESC_KEY; - end - case el.SPACE_BAR, % 32: we trigger ourselves - if el.allowlocaltrigger==1 - Eyelink( 'AcceptTrigger'); - end - if Eyelink('IsConnected') == el.dummyconnected - result=0; - end - otherwise, % Echo to tracker for remote control - if el.allowlocalcontrol==1 - Eyelink('SendKeyButton', key, 0, el.KB_PRESS ); - end - end % switch key -end % while cal_result==NO_REPLY - -if draw==1 - EyelinkEraseCalTarget(el, targetrect); % bit superfluous actually - EyelinkClearCalDisplay(el); % exit_cal_display() -end - -if result==el.ESC_KEY || result==-1 % Did we abort drift correction? - % yes: go to setup menu to fix any problems - if el.targetbeep==1 - EyelinkCalDoneBeep(el, 0); - end - if allowsetup==1 - EyelinkDoTrackerSetup(el); - else - Eyelink( 'SetOfflineMode'); - end -else - % Otherwise, we apply the drift correction - if el.targetbeep==1 - EyelinkCalDoneBeep(el, 1); - end - Eyelink('ApplyDriftCorr' ); - result=0; -end - -return; diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDoDriftCorrection.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDoDriftCorrection.m index 23bce41f4d..1f3504bf13 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDoDriftCorrection.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDoDriftCorrection.m @@ -5,45 +5,113 @@ % We repeat if ESC key pressed to do setup. % Setup might also have erased any pre-drawn graphics. % -% Note that EyelinkDoDriftCorrection() internally uses Beeper() and Snd() to play +% Note that EyelinkDoDriftCorrection() internally may use Snd() to play % auditory feedback tones if el.targetbeep=1 or el.feedbackbeep=1 and the % el.callback function is set to the default PsychEyelinkDispatchCallback(). +% % If you want to use PsychPortAudio in a script that also calls EyelinkDoDriftCorrection, % then read "help Snd" for instructions on how to provide proper interoperation -% between PsychPortAudio and the feedback sounds created by Eyelink. +% between PsychPortAudio and the feedback sounds created by Eyelink. The demos +% referenced under "help SR-ResearchDemos" show an even better approach than the +% one described in "help Snd". % +global eyelinkanimationtarget success=1; % if no x and y are supplied, set x,y to center coordinates if ~exist('x', 'var') || isempty(x) || ~exist('y', 'var') || isempty(y) - [x,y] = WindowCenter(el.window); % convenience routine part of eyelink toolbox + [x,y] = WindowCenter(el.window); % convenience routine part of eyelink toolbox end if ~exist('draw', 'var') || isempty(draw) - draw=1; + draw=1; end if ~exist('allowsetup', 'var') || isempty(allowsetup) - allowsetup=1; + allowsetup=1; end -while 1 - if Eyelink('IsConnected')==el.notconnected % Check link often so we don't lock up if tracker lost - %result=el.ABORT_EXPT; - success=0; - return; - end; - % DRIFT CORRECTION */ - % 3rd argument would be 0 to NOT draw a target */ - % fprintf('drifcorr at % d %d\n', x, y ); - error = EyelinkDoDriftCorrect(el, x, y, draw, allowsetup); - - if error==el.TERMINATE_KEY - %result=el.ABORT_EXPT; - success=0; - return; - end; - % repeat if ESC was pressed to access Setup menu - if(error~=el.ESC_KEY) break; end +while 1 + if Eyelink('IsConnected')==el.notconnected % Check link often so we don't lock up if tracker lost + success=0; + return; + end + + if ~isempty(el.callback) % if we have a callback set, we call it. + if isempty(eyelinkanimationtarget) + eyelinkanimationtarget = initmoviestruct(); + end + + global inDoTrackerSetup; + if isempty(inDoTrackerSetup) + inDoTrackerSetup = false; + end + + global inDoDriftCorrection; + if isempty(inDoDriftCorrection) + inDoDriftCorrection = true; + end + + if strcmpi(el.calTargetType, 'video') && ~eyelinkanimationtarget.init + eyelinkanimationtarget = loadanimationmovie(el, eyelinkanimationtarget); + end + result = Eyelink('DriftCorrStart', x, y, 1, draw, allowsetup); + + else + % else we continue with the old version + result = EyelinkDoDriftCorrect(el, x, y, draw, allowsetup); + end + + if result==el.TERMINATE_KEY + success=0; + return; + end + + % repeat if ESC was pressed to access Setup menu + if(result~=el.ESC_KEY) + break; + end + end % while + +% fprintf('~isempty(el.callback): %d, el.calTargetType: %s, ~inDoTrackerSetup: %d, eyelinkanimationtarget.init: %d\n', ~isempty(el.callback), el.calTargetType, ~inDoTrackerSetup, eyelinkanimationtarget.init) +if ~isempty(el.callback) && strcmpi(el.calTargetType, 'video') && ~inDoTrackerSetup && eyelinkanimationtarget.init + eyelinkanimationtarget = cleanupmovie(el, eyelinkanimationtarget); + inDoDriftCorrection = false; +end + +return + + + function eyelinkanimationtarget = initmoviestruct() + eyelinkanimationtarget.init = false; + eyelinkanimationtarget.movie = []; + eyelinkanimationtarget.movieduration = []; + eyelinkanimationtarget.fps = []; + eyelinkanimationtarget.imgw = []; + eyelinkanimationtarget.imgh = []; + eyelinkanimationtarget.calxy = []; + end + + function eyelinkanimationtarget = loadanimationmovie(el, eyelinkanimationtarget) + [movie, movieduration, fps, imgw, imgh] = Screen('OpenMovie', el.window, el.calAnimationTargetFilename, el.calAnimationOpenAsync, el.calAnimationOpenPreloadSecs, el.calAnimationOpenSpecialFlags1); + eyelinkanimationtarget.init = true; + eyelinkanimationtarget.movie = movie; + eyelinkanimationtarget.movieduration = movieduration; + eyelinkanimationtarget.fps = fps; + eyelinkanimationtarget.imgw = imgw; + eyelinkanimationtarget.imgh = imgh; + end + + function eyelinkanimationtarget = cleanupmovie(el, eyelinkanimationtarget) + texkill = Screen('GetMovieImage', el.window, eyelinkanimationtarget.movie, el.calAnimationWaitTexClose); + Screen('PlayMovie', eyelinkanimationtarget.movie, 0, el.calAnimationLoopParam); + if texkill > 0 + Screen('Close', texkill); + end + Screen('CloseMovie', eyelinkanimationtarget.movie); + eyelinkanimationtarget = initmoviestruct(); + end + +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDoTrackerSetup.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDoTrackerSetup.m index 220615d980..999395f959 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDoTrackerSetup.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkDoTrackerSetup.m @@ -12,12 +12,16 @@ % 'd', start driftcorrection % 13, or el.ENTER_KEY, show 'eye' setup image % -% Note that EyelinkDoTrackerSetup() internally uses Beeper() and Snd() to play +% Note that EyelinkDoTrackerSetup() internally may use Snd() to play % auditory feedback tones if el.targetbeep=1 or el.feedbackbeep=1 and the % el.callback function is set to the default PsychEyelinkDispatchCallback(). +% % If you want to use PsychPortAudio in a script that also calls EyelinkDoTrackerSetup, % then read "help Snd" for instructions on how to provide proper interoperation -% between PsychPortAudio and the feedback sounds created by Eyelink. +% between PsychPortAudio and the feedback sounds created by Eyelink. The demos +% referenced under "help SR-ResearchDemos" show an even better approach than the +% one described in "help Snd". +% % % 02-06-01 fwc removed use of global el, as suggest by John Palmer. @@ -26,99 +30,77 @@ % 15-10-02 fwc added sendkey variable that allows to go directly into a particular mode % 22-06-06 fwc OSX-ed % 15-06-10 fwc added code for new callback version +global eyelinkanimationtarget -result=-1; if nargin < 1 - error( 'USAGE: result=EyelinkDoTrackerSetup(el [,sendkey])' ); + error( 'USAGE: result=EyelinkDoTrackerSetup(el [,sendkey])' ); end -% if we have the new callback code, we call it. +% if we have a callback set, we call it. if ~isempty(el.callback) - if Eyelink('IsConnected') ~= el.notconnected - if ~isempty(el.window) - rect=Screen(el.window,'Rect'); - % make sure we use the correct screen coordinates - Eyelink('Command', 'screen_pixel_coords = %d %d %d %d',rect(1),rect(2),rect(3)-1,rect(4)-1); - end - else - return + if isempty(eyelinkanimationtarget) + eyelinkanimationtarget = initmoviestruct(); + end + global inDoTrackerSetup; + if isempty(inDoTrackerSetup) + inDoTrackerSetup = true; end - result = Eyelink( 'StartSetup', 1 ); - - return; -end -% else we continue with the old version + global inDoDriftCorrection; + if isempty(inDoDriftCorrection) + inDoDriftCorrection = false; + end -% Eyelink('Command', 'heuristic_filter = ON'); -Eyelink( 'StartSetup' ); % start setup mode -Eyelink( 'WaitForModeReady', el.waitformodereadytime ); % time for mode change + if strcmpi(el.calTargetType, 'video') && ~eyelinkanimationtarget.init + eyelinkanimationtarget = loadanimationmovie(el, eyelinkanimationtarget); + end -EyelinkClearCalDisplay(el); % setup_cal_display() -key=1; -while key~= 0 - key=EyelinkGetKey(el); % dump old keys -end + result = Eyelink( 'StartSetup', 1 ); -% go directly into a particular mode + if strcmpi(el.calTargetType, 'video') && ~inDoDriftCorrection && eyelinkanimationtarget.init + eyelinkanimationtarget = cleanupmovie(el, eyelinkanimationtarget); + end + inDoTrackerSetup = false; + return; +else -if nargin==2 - if el.allowlocalcontrol==1 - switch lower(sendkey) - case{ 'c', 'v', 'd', el.ENTER_KEY} - %forcedkey=BITAND(sendkey(1,1),255); - forcedkey=double(sendkey(1,1)); - Eyelink('SendKeyButton', forcedkey, 0, el.KB_PRESS ); - end - end + % else we continue with the old version + if nargin < 2 + sendkey = []; + end + result=EyelinkLegacyDoTrackerSetup(el, sendkey); + return end -tstart=GetSecs; -stop=0; -while stop==0 && bitand(Eyelink( 'CurrentMode'), el.IN_SETUP_MODE) - i=Eyelink( 'CurrentMode'); - - if ~Eyelink( 'IsConnected' ) stop=1; break; end; + function eyelinkanimationtarget = initmoviestruct() + eyelinkanimationtarget.init = false; + eyelinkanimationtarget.movie = []; + eyelinkanimationtarget.movieduration = []; + eyelinkanimationtarget.fps = []; + eyelinkanimationtarget.imgw = []; + eyelinkanimationtarget.imgh = []; + eyelinkanimationtarget.calxy = []; + end - if bitand(i, el.IN_TARGET_MODE) % calibrate, validate, etc: show targets - %fprintf ('%s\n', 'dotrackersetup: in targetmodedisplay' ); - EyelinkTargetModeDisplay(el); - elseif bitand(i, el.IN_IMAGE_MODE) % display image until we're back -% fprintf ('%s\n', 'EyelinkDoTrackerSetup: in ''ImageModeDisplay''' ); - if Eyelink ('ImageModeDisplay')==el.TERMINATE_KEY - result=el.TERMINATE_KEY; - return; % breakout key pressed - else - EyelinkClearCalDisplay(el); % setup_cal_display() - end - end + function eyelinkanimationtarget = loadanimationmovie(el, eyelinkanimationtarget) + [movie, movieduration, fps, imgw, imgh] = Screen('OpenMovie', el.window, el.calAnimationTargetFilename, el.calAnimationOpenAsync, el.calAnimationOpenPreloadSecs, el.calAnimationOpenSpecialFlags1); + eyelinkanimationtarget.init = true; + eyelinkanimationtarget.movie = movie; + eyelinkanimationtarget.movieduration = movieduration; + eyelinkanimationtarget.fps = fps; + eyelinkanimationtarget.imgw = imgw; + eyelinkanimationtarget.imgh = imgh; + end - [key, el]=EyelinkGetKey(el); % getkey() HANDLE LOCAL KEY PRESS - if 1 && key~=0 && key~=el.JUNK_KEY % print pressed key codes and chars - fprintf('%d\t%s\n', key, char(key) ); + function eyelinkanimationtarget = cleanupmovie(el, eyelinkanimationtarget) + texkill = Screen('GetMovieImage', el.window, eyelinkanimationtarget.movie, el.calAnimationWaitTexClose); + Screen('PlayMovie', eyelinkanimationtarget.movie, 0, el.calAnimationLoopParam); + if texkill > 0 + Screen('Close', texkill); + end + Screen('CloseMovie', eyelinkanimationtarget.movie); + eyelinkanimationtarget = initmoviestruct(); end - - switch key - case el.TERMINATE_KEY, % breakout key code - result=el.TERMINATE_KEY; - return; - case { 0, el.JUNK_KEY } % No or uninterpretable key - case el.ESC_KEY, - if Eyelink('IsConnected') == el.dummyconnected - stop=1; % instead of 'goto exit' - end - if el.allowlocalcontrol==1 - Eyelink('SendKeyButton', key, 0, el.KB_PRESS ); - end - otherwise, % Echo to tracker for remote control - if el.allowlocalcontrol==1 - Eyelink('SendKeyButton', double(key), 0, el.KB_PRESS ); - end - end -end % while IN_SETUP_MODE -% exit: -EyelinkClearCalDisplay(el); % exit_cal_display() -result=0; -return; +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkEraseCalTarget.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkEraseCalTarget.m deleted file mode 100644 index 606250e0e7..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkEraseCalTarget.m +++ /dev/null @@ -1,12 +0,0 @@ -function erasecaltarget(el, rect) - -% erase calibration target -% -% USAGE: erasecaltarget(el, rect) -% -% el: eyelink default values -% rect: rect that will be filled with background colour -if ~IsEmptyRect(rect) - Screen( 'FillOval', el.window, el.backgroundcolour, rect ); - Screen( 'Flip', el.window); -end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkGetKey.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkGetKey.m index fc51c9b044..fbbfc81b2c 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkGetKey.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkGetKey.m @@ -107,7 +107,7 @@ elseif keyCodes(187)==1 % KeypadBackspace key=el.ENTER_KEY; elseif any(keyCodes(el.backspace))==1 - key=el.ESC_KEY; + key=el.BACKSPACE; elseif any(keyCodes(el.escape))==1 key=el.ESC_KEY; elseif any(keyCodes(el.pageup))==1 diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkInitDefaults.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkInitDefaults.m index dfe5b40b5c..7b29830e61 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkInitDefaults.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkInitDefaults.m @@ -10,9 +10,9 @@ % Note that these values are only used by the m-file % versions of dotrackersetup and dodriftcorrect. -% 02-06-01 fwc created, as suggested by John Palmer. -% added also all control codes and defaults -% 17-10-02 fwc added event types +% 02-06-01 fwc created, as suggested by John Palmer. +% added also all control codes and defaults +% 17-10-02 fwc added event types % 26-11-02 fwc&emp added PC support % 11-01-04 fwc OS X changes % 22-06-06 fwc further OSX changes @@ -26,12 +26,15 @@ % and eye image size. Note that many default settings % are no longer used in the "callback" version of calibration % and driftcorrection. -% 15-01-13 ia Added el.devicenumber to allow better control of multiple -% input devices +% 15-01-13 ia Added el.devicenumber to allow better control of multiple +% input devices el=[]; -% eyelink computer check +% EyeLink Toolbox debug messages (reserved for future use) +el.debugPrint = false; + +% EyeLink Toolbox (Display PC) Computer Type el.computer = computer; % Enable unified keyname -> keycode mapping for all operating systems: @@ -42,35 +45,41 @@ el.RIGHT_EYE=1; el.BINOCULAR=2; -% eyelink connection states +% EyeLink connection states el.notconnected=0; el.connected=1; el.dummyconnected=-1; el.broadcastconnected=2; - +% Calibration Feedback el.displayCalResults = 0; -if ~exist('window', 'var') +if ~exist('window', 'var') || isempty(window) window = []; -end - -if ~isempty(window) - el.window=window; - el.backgroundcolour = WhiteIndex(el.window); - el.backgroundcolour = GrayIndex(el.window); - el.foregroundcolour = BlackIndex(el.window); + infoStruct = []; + el.window = window; + el.winInfo = infoStruct; + el.backgroundcolour = GrayIndex(0); + el.foregroundcolour = 0; + el.msgfontcolour = 0; + el.imgtitlecolour = 0; +else + el.window=window; + el.winInfo = Screen('GetWindowInfo', window, 0); + el.backgroundcolour = GrayIndex(el.window); + el.foregroundcolour = BlackIndex(el.window); el.msgfontcolour = BlackIndex(el.window); el.imgtitlecolour = BlackIndex(el.window); - - rect=Screen(el.window,'Rect'); + + rect=Screen(el.window,'Rect'); if Eyelink('IsConnected') ~= el.notconnected Eyelink('Command', 'screen_pixel_coords = %d %d %d %d',rect(1),rect(2),rect(3)-1,rect(4)-1); end -else - el.window=[]; end +% PsychPortAudio configuration +el.ppa_pahandle = []; + % set some more global info parameters % below are old sound definitions el.targetdisplaysound='EyelinkTargetBeep'; @@ -90,17 +99,34 @@ el.allowlocaltrigger=1; % allow user to trigger him or herself el.allowlocalcontrol=1; % allow control from subject-computer el.mousetriggersdriftcorr=0; % 1=allow mouse to trigger drift correction (fwc trick) -el.quitkey=KbName('ESCAPE'); % when pressed in combination with modifier key - % forces getkeyforeyelink to return 'TERMINATE_KEY' ! +el.quitkey=KbName('ESCAPE'); % Modifier key is always LeftGUI due to unified keyname mapping: el.modifierkey=KbName('LeftGUI'); el.waitformodereadytime=500; +el.calTargetType = 'ellipse'; % available types: 'ellipse', 'image', 'video' + el.calibrationtargetsize=2.5; % size of calibration target as percentage of screen el.calibrationtargetwidth=1; % width of calibration target's border as percentage of screen el.calibrationtargetcolour=[0 0 0]; +el.calImageTargetFilename=[]; +el.calImageInfo=[]; +el.calImageData=[]; +el.calImageTexture=[]; + +el.calAnimationTargetFilename=[]; +el.calAnimationResetOnTargetMove = false; +el.calAnimationAudioVolume = 1.0; +el.calAnimationLoopParam = 1; % For testing if default is problematic: 'loop' param passed to Screen('PlayMovie', ...); 1=PTBDefaultLooping, 2=GaplessReload, 5=SegmentSeeksRewing, 8=RewindFlushGPU +el.calAnimationOpenSpecialFlags1 = 0; % For testing if default is problematic: 'specialFlags1' param passed to Screen('OpenMovie', ...); 0=PTBDefault, 1=YUVnotRGB-decoding, 2=NoSound, 4=DrawMotionVectors, 8=SkipBFrameDecode +el.calAnimationSetIndexIsFrames = 0; % For testing if default is problematic (only used: following 'OpenMovie' and for rewind when el.calAnimationResetOnTargetMove = true): 'indexIsFrames' param passed to Screen('SetMovieTimeIndex', ...); 0=timeindexIsSeconds, 1=timeindexIsFrames +el.calAnimationOpenAsync = 0; +el.calAnimationOpenPreloadSecs = 1; +el.calAnimationWaitTex = 0; +el.calAnimationWaitTexClose = 0; + el.devicenumber = []; %see KbCheck for details of this value el.getkeyrepeat=1/5; % "sample time" for eyelinkgetkey el.getkeytime=-1; % stores last time eyelinkgetkey was used @@ -110,10 +136,9 @@ el.msgfontsize=20; % absolute, should perhaps be percentage of screen el.eyeimgsize=30; % percentage of screen el.helptext='Press RETURN (on either display computer or tracker host computer) to toggle camera image'; -el.helptext=[el.helptext '\n' 'Press ESC to Output/Record']; +el.helptext=[el.helptext '\n' 'Press Esc/O for Output/Record']; el.helptext=[el.helptext '\n' 'Press C to Calibrate']; el.helptext=[el.helptext '\n' 'Press V to Validate']; -% el.helptext=[el.helptext '\n' 'Press D for Drift correction']; % font info for camera image title el.imgtitlefont='Helvetica'; @@ -133,7 +158,11 @@ el.return=KbName('Return'); el.escape=KbName('ESCAPE'); el.space=KbName('space'); -el.backspace=KbName('DELETE'); % is this delete backspace? +if IsOSX + el.backspace=KbName('DELETE'); +else + el.backspace=KbName('BackSpace'); +end el.f1=KbName('F1'); el.f2=KbName('F2'); @@ -152,19 +181,16 @@ el.right_control=KbName('RightControl'); el.lalt=KbName('LeftAlt'); el.ralt=KbName('RightAlt'); -%el.lmeta=KbName(''); -%el.rmeta=KbName(''); el.num=KbName('NumLock'); el.caps=KbName('CapsLock'); -%el.mode=KbName(''); if IsOSX - % OS-X supports a separate keycode for the Enter key: - el.enter=KbName('ENTER'); + % OS-X supports a separate keycode for the Enter key: + el.enter=KbName('ENTER'); else - % M$-Windows and GNU/Linux don't have a separate code for Enter, - % so we will map it to the 'Return' key: - el.enter=el.return; + % M$-Windows and GNU/Linux don't have a separate code for Enter, + % so we will map it to the 'Return' key: + el.enter=el.return; end el.keysCached=1; @@ -172,7 +198,7 @@ % up quickly. Hence we disable warnings for fillup problems: % This is try-catch protected for compatibility to Matlab R11 and Octave... try - warning off MATLAB:namelengthmaxexceeded + warning off MATLAB:namelengthmaxexceeded catch % Nothing to do. We just swallow the error we'd get if that warning % statement wouldn't be supported. @@ -180,19 +206,19 @@ % Eyelink Tracker state bit: bitand() with flag word to test functionality -el.IN_DISCONNECT_MODE=16384; % disconnected -el.IN_UNKNOWN_MODE=0; % mode fits no class (i.e setup menu) -el.IN_IDLE_MODE=1; % off-line -el.IN_SETUP_MODE=2; % setup or cal/val/dcorr -el.IN_RECORD_MODE=4; % data flowing -el.IN_TARGET_MODE=8; % some mode that needs fixation targets -el.IN_DRIFTCORR_MODE=16; % drift correction -el.IN_IMAGE_MODE=32; % image-display mode -el.IN_USER_MENU=64; % user menu -el.IN_PLAYBACK_MODE=256; % tracker sending playback data +el.IN_DISCONNECT_MODE=16384; % disconnected +el.IN_UNKNOWN_MODE=0; % mode fits no class (i.e setup menu) +el.IN_IDLE_MODE=1; % off-line +el.IN_SETUP_MODE=2; % setup or cal/val/dcorr +el.IN_RECORD_MODE=4; % data flowing +el.IN_TARGET_MODE=8; % some mode that needs fixation targets +el.IN_DRIFTCORR_MODE=16; % drift correction +el.IN_IMAGE_MODE=32; % image-display mode +el.IN_USER_MENU=64; % user menu +el.IN_PLAYBACK_MODE=256; % tracker sending playback data % Eyelink key values -el.JUNK_KEY=1; % return code for untranslatable key +el.JUNK_KEY=1; % return code for untranslatable key el.TERMINATE_KEY=hex2dec('7FFF'); % return code for program exit/breakout key el.CURS_UP=hex2dec('4800'); el.CURS_DOWN=hex2dec('5000'); @@ -203,6 +229,7 @@ el.PAGE_UP=hex2dec('4900'); el.PAGE_DOWN=hex2dec('5100'); el.SPACE_BAR=32; +el.BACKSPACE=hex2dec('0008'); % BRedit - added for supporting back-stepping through targets el.F1_KEY=hex2dec('3B00'); el.F2_KEY=hex2dec('3C00'); @@ -231,7 +258,6 @@ % other Eyelink values - el.ELKEY_DOWN=1; el.ELKEY_UP=0; @@ -239,7 +265,7 @@ el.MISSING=-32768; % eyedata.h el.MISSING_DATA=-32768; -el.KEYDOWN=1; %Eyelink manual and core_expt.h have these backwards +el.KEYDOWN=1; % Eyelink manual and core_expt.h have these backwards el.KEYUP=0; % LINK RETURN CODES @@ -252,57 +278,46 @@ el.SKIP_TRIAL=2; el.ABORT_EXPT=3; -el.TRIAL_ERROR=-1; % Bad trial: no data, etc. +el.TRIAL_ERROR=-1; % Bad trial: no data, etc. % EVENT types el.SAMPLE_TYPE=200; -el.STARTPARSE=1; % /* these only have time and eye data */ +el.STARTPARSE=1; % /* these only have time and eye data */ el.ENDPARSE=2; el.BREAKPARSE=10; -el.STARTBLINK=3; % /* EYE DATA: contents determined by evt_data */ -el.ENDBLINK=4; % /* and by "read" data item */ -el.STARTSACC=5; % /* all use IEVENT format */ +el.STARTBLINK=3; % /* EYE DATA: contents determined by evt_data */ +el.ENDBLINK=4; % /* and by "read" data item */ +el.STARTSACC=5; % /* all use IEVENT format */ el.ENDSACC=6; el.STARTFIX=7; el.ENDFIX=8; el.FIXUPDATE=9; -el.STARTSAMPLES=15; %/* start of events in block *//* control events: all put data into */ -el.ENDSAMPLES=16; %/* end of samples in block *//* the EDF_FILE or ILINKDATA status */ -el.STARTEVENTS=17; % /* start of events in block */ -el.ENDEVENTS=18; %/* end of events in block */ - -el.MESSAGEEVENT=24; % /* user-definable text or data */ -el.BUTTONEVENT=25; %/* button state change */ -el.INPUTEVENT=28; % /* change of input port */ +el.STARTSAMPLES=15; % /* start of events in block *//* control events: all put data into */ +el.ENDSAMPLES=16; % /* end of samples in block *//* the EDF_FILE or ILINKDATA status */ +el.STARTEVENTS=17; % /* start of events in block */ +el.ENDEVENTS=18; % /* end of events in block */ -el.LOSTDATAEVENT=hex2dec('3F'); %/*new addition v2.1, returned by eyelink_get_next_data() to flag a gap in the data stream due to queue filling up (need to get data more frequently) - %/*described in 'EyeLink Programmers Guide.pdf' section 7.2.2, 13.3.2, 18.5.4 +el.MESSAGEEVENT=24; % /* user-definable text or data */ +el.BUTTONEVENT=25; % /* button state change */ +el.INPUTEVENT=28; % /* change of input port */ -% if exist('EyelinkDispatchCallback') %#ok -% el.callback = 'EyelinkDispatchCallback'; -% else -% el.callback = []; -% end +el.LOSTDATAEVENT=hex2dec('3F'); % /* new addition v2.1, returned by eyelink_get_next_data() to flag */ + % /* a gap in the data stream due to queue filling up (need to get data more frequently) */ + % /* described in 'EyeLink Programmers Guide.pdf' section 7.2.2, 13.3.2, 18.5.4 +el.FIVE_SAMPLE_MODEL = 1; +el.NINE_SAMPLE_MODEL = 2; +el.SEVENTEEN_SAMPLE_MODEL = 3; +el.EL1000_TRACKER_MODEL = 4; + if exist('PsychEyelinkDispatchCallback') %#ok el.callback = 'PsychEyelinkDispatchCallback'; else el.callback = []; end - EyelinkUpdateDefaults(el); - - -% % Window assigned? -% if ~isempty(el.window) && ~isempty(el.callback) -% % Yes. Assign it to our dispatch callback: -% % EyelinkDispatchCallback(el); -% PsychEyelinkDispatchCallback(el); -% end - -% el % uncomment to show contents of this default eyelink structure diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkTargetModeDisplay.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkTargetModeDisplay.m deleted file mode 100644 index 33ba505164..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkTargetModeDisplay.m +++ /dev/null @@ -1,109 +0,0 @@ -function result=EyelinkTargetModeDisplay(el) - -% USAGE: result=EyelinkTargetModeDisplay(el) -% -% el: Eyelink default values -% History -% 15-05-01 fwc created first version -% 22-05-01 fwc little debugging -% 02-06-01 fwc removed use of global el, as suggested by John Palmer. -% 22-06-06 fwc OSX-ed - - -result=-1; % initialize -if nargin < 1 - error( 'USAGE: result=EyelinkTargetModeDisplay(el)' ); -end - -targetvisible = 0; % target currently drawn -targetrect=[0 0 0 0]; - -tx=el.MISSING; -ty=el.MISSING; - -otx=el.MISSING; % current target position -oty=el.MISSING; - -EyelinkClearCalDisplay(el); % setup_cal_display() - -key=1; -while key~= 0 - [key, el]=EyelinkGetKey(el); % dump old keys -end - % LOOP WHILE WE ARE DISPLAYING TARGETS -stop=0; -while stop==0 && bitand(Eyelink('CurrentMode'), el.IN_TARGET_MODE) - - if Eyelink( 'IsConnected' )==el.notconnected - result=-1; - return; - end; - - [key, el]=EyelinkGetKey(el); % getkey() HANDLE LOCAL KEY PRESS - - switch key - case el.TERMINATE_KEY, % breakout key code - EyelinkClearCalDisplay(el); % clear_cal_display(); - result=el.TERMINATE_KEY; - return; - case el.SPACE_BAR, % 32: accept fixation - if el.allowlocaltrigger==1 - Eyelink( 'AcceptTrigger'); - end - break; - case { 0, el.JUNK_KEY } % No key - case el.ESC_KEY, - if Eyelink('IsConnected') == el.dummyconnected - stop=1; - end - if el.allowlocalcontrol==1 - Eyelink('SendKeyButton', key, 0, el.KB_PRESS ); - end - otherwise, % Echo to tracker for remote control - if el.allowlocalcontrol==1 - Eyelink('SendKeyButton', key, 0, el.KB_PRESS ); - end - end % switch key - - - % HANDLE TARGET CHANGES - [result, tx, ty]= Eyelink( 'TargetCheck'); - - - % erased or moved: erase target - if (targetvisible==1 && result==0) || tx~=otx || ty~=oty - EyelinkEraseCalTarget(el, targetrect); - targetvisible = 0; - end - % redraw if invisible - if targetvisible==0 && result==1 -% fprintf( 'Target drawn at: x=%d, y=%d\n', tx, ty ); - - targetrect=EyelinkDrawCalTarget(el, tx, ty); - targetvisible = 1; - otx = tx; % record position for future tests - oty = ty; - if el.targetbeep==1 - EyelinkCalTargetBeep(el); % optional beep to alert subject - end - end - -end % while IN_TARGET_MODE - - -% exit: % CLEAN UP ON EXIT -if el.targetbeep==1 - if Eyelink('CalResult')==1 % does 1 signal success? - EyelinkCalDoneBeep(el, 1); - else - EyelinkCalDoneBeep(el, -1); - end -end - -if targetvisible==1 - EyelinkEraseCalTarget(el, targetrect); % erase target on exit, bit superfluous actually -end -EyelinkClearCalDisplay(el); % clear_cal_display(); - -result=0; -return; diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkUpdateDefaults.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkUpdateDefaults.m index 7f8324522b..b296a7b438 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkUpdateDefaults.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/EyelinkUpdateDefaults.m @@ -3,20 +3,97 @@ function EyelinkUpdateDefaults(el) % % This function passes changes to the calibration defaults structure % to the callback function. To be called after EyelinkInitDefaults if any -% changes are made to the output structure of that function. -% -% el=EyelinkInitDefaults(window); -% el.backgroundColour = BlackIndex(window); -% EyelinkUpdateDefaults(el); +% changes are made to the output structure of that function, e.g. +% el=EyelinkInitDefaults(window); +% el.backgroundColour = BlackIndex(window); +% EyelinkUpdateDefaults(el); % % 27-1-2011 NJ created % 19-12-2012 IA Fix hardcoded callback +callStack = dbstack; if ~isempty(el.callback) && exist(el.callback,'file') - - %evaluate the callback function with the new el defaults - feval(el.callback, el); - -end + if ~isempty(el.calImageTargetFilename) + if exist(el.calImageTargetFilename, 'file') + el.calImageInfo = imfinfo(el.calImageTargetFilename); % Get image file info + el.calImageData = imread(el.calImageTargetFilename); % Read image from file + el.calImageTexture = Screen('MakeTexture', el.window, el.calImageData); % Convert image file to texture + if ~strcmpi(el.calTargetType, 'image') + warning(sprintf([ ... + 'EyelinkToolbox -- ''el.calImageTargetFilename'' is configured but will not be used until\n' ... + '\tel.calTargetType = ''image''\n' ... + 'is also set and \n' ... + '\tEyelinkUpdateDefaults(el)\n' ... + 'is called again thereafter.\n\n'])); + end + else + warning(sprintf([ ... + 'EyelinkToolbox -- File Not Found:\n', ... + '\tel.calImageTargetFilename = %s\n\n'], ... + el.calImageTargetFilename)); + end + end + + if ~isempty(el.calAnimationTargetFilename) + if exist(el.calAnimationTargetFilename, 'file') + if strcmpi(el.calTargetType, 'video') + if el.targetbeep ~= 0 + warning(sprintf([ 'EyelinkToolbox - ''el.calAnimationTargetFilename'' set for video, but\n' ... + 'el.targetbeep not set == 0 and may cause playback issues (freezing).\n'])); + end + + if el.feedbackbeep ~= 0 + warning(sprintf([ 'EyelinkToolbox - ''el.calAnimationTargetFilename'' set for video, but\n' ... + 'el.feedbackbeep not set == 0 and may cause playback issues (freezing).\n'])); + end + else + warning(sprintf([ ... + 'EyelinkToolbox -- ''el.calAnimationTargetFilename'' is configured but will not be used until\n' ... + '\tel.calTargetType = ''video''\nis also set and \n\tEyelinkUpdateDefaults(el)\nis called again thereafter.\n\n'])); + end + else + warning(sprintf([ ... + 'EyelinkToolbox -- File Not Found:\n', ... + '\tel.calAnimationTargetFilename = %s\n\n'], ... + el.calAnimationTargetFilename)); + end + end + + if (el.feedbackbeep || el.targetbeep) && ~any(strcmpi({callStack.name}, 'EyelinkInitDefaults')) + if isempty(el.ppa_pahandle) && strcmp('PsychEyelinkDispatchCallback', el.callback) + if PsychPortAudio('GetOpenDeviceCount') > 0 + warning(sprintf(['EyelinkToolbox -- el.feedbackbeep or el.targetbeep are set requiring audio playback.\n' ... + 'While a PsychPortAudio device has already been opened, no device handle was passed for EyeLink audio\n' ... + 'feedback to use.\n'... + 'See ''help SR-ResearchDemos'' projects for implementation examples.\n' ... + '***Disabling EyeLink audio feedback to avoid conflicts with PsychPortAudio device previously opened***\n' ... + '\tel.feedbackbeep = 0\n\tel.targetbeep = 0'])); + el.feedbackbeep = 0; + el.targetbeep = 0; + else + warning(sprintf(['EyelinkToolbox -- el.feedbackbeep or el.targetbeep are set requiring audio\n' ... + 'playback, but no open PsychPortAudio devices have been passed using el.ppa_pahandle.\n' ... + 'See ''help SR-ResearchDemos'' projects for implementation examples.\n' ... + '... Opening default audio device with PsychPortAudio automatically.\n' ... + 'To otherwise disable EyeLink audio feedback altogether, set el.feedbackbeep &\n' ... + 'el.targetbeep to 0.'])); + % Force-close potential old Snd() device: + Snd('Close', 1); + + % Initialize, in case it hasn't been done already: + InitializePsychSound(); + + % Open with exactly the parameters a standard Snd('Open') would use: + el.ppa_sndhandle = PsychPortAudio('Open', [], 1, 0, [], 2); + + % Attach to Snd() for permanent use: + Snd('Open', el.ppa_sndhandle, 1); + end + end + end + + % evaluate the callback function with the new el defaults + feval(el.callback, el); +end end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/PsychEyelinkDispatchCallback.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/PsychEyelinkDispatchCallback.m index 4ad24ad711..7d364af3a3 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/PsychEyelinkDispatchCallback.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/PsychEyelinkDispatchCallback.m @@ -1,5 +1,20 @@ function rc = PsychEyelinkDispatchCallback(callArgs, msg) -% Retrieve live eye-image from Eyelink, show it in onscreen window. +% PsychEyelinkDispatchCallback implementes the EyeLink Core Graphics part +% of the EyeLink API. This "Core Graphics" part of our API is responsible +% for handling the times when the API and Host PC takes control of the eye +% tracking procedures. This includes the functionality to stream camera +% images during camera/participant setup, displaying targets at locations +% on the participant screen during calibration, validation, and drift/check +% and correction routines. Complimentary to handling the visual aspect of +% these operations contingent on display routines, the functionality +% implemented herewith also handles the playback of feedback sounds to +% the experimenter and participant for guiding these interactive +% procedures. During these modes of operation, this function also +% implements the forwarding of kepresses to the Host PC that are registered +% on the computer's keyboard which is running this implementation. The +% purpose of this is to make sure that bost Host and Display PCs are +% operating as identically in these modes of operation. +% % % This function is normally called from within the Eyelink() mex file. % Normal user code only calls it once to supply the eyelink defaults struct. @@ -14,32 +29,48 @@ % myEyelinkDispatchCallback(el); % % -% to actually receive and display the images, register this function as eyelink's callback: +% To actually receive and display the images, register this function as +% eyelink's callback: +% +% % if Eyelink('Initialize', 'myEyelinkDispatchCallback') ~=0 -% error('eyelink failed init') +% error('eyelink failed init') % end % result = Eyelink('StartSetup',1) %put the tracker into a mode capable of sending images -% then you must hit 'return' on the PTB computer, this key command will be sent to the tracker host to initiate sending of images. % -% This function fetches the most recent live image from the Eylink eye -% camera and displays it in the previously assigned onscreen window. +% +% then you must hit 'return' on the PTB computer, this key command will be +% sent to the tracker host to initiate sending of images. +% % % History: -% 15.3.2009 Derived from MemoryBuffer2TextureDemo.m (MK). -% 4.4.2009 Updated to use EyelinkGetKey + fixed eyelinktex persistence crash (edf). -% 11.4.2009 Cleaned up. Should be ready for 1st release, although still -% pretty alpha quality. (MK). -% 15.6.2010 Added some drawing routines to get standard behaviour back. Enabled -% use of the callback by default. Clarified in helptext that user -% normally should not have to worry about calling this file. (fwc) -% 20.7.2010 drawing of instructions, eye-image+title, playing sounds in seperate functions +% 15. 3.2009 Derived from MemoryBuffer2TextureDemo.m (MK). +% 4. 4.2009 Updated to use EyelinkGetKey + fixed eyelinktex persistence +% crash (edf). +% 11. 4.2009 Cleaned up. Should be ready for 1st release, although still +% pretty alpha quality. (MK). +% 15. 6.2010 Added some drawing routines to get standard behaviour back. +% Enabled use of the callback by default. Clarified in +% helptext that user normally should not have to worry +% about calling this file. (fwc) +% 20. 7.2010 Drawing of instructions, eye-image+title, playing sounds in +% seperate functions % -% 1.2.2010 modified to allow for cross hair and fix bugs. (nj) -% 29.10.2018 Drop 'DrawDots' for calibration target. Some white-space fixes. - -% Cached texture handle for eyelink texture: +% 1. 2.2010 Modified to allow for cross hair and fix bugs. (nj)= +% 29.10.2018 Drop 'DrawDots' for calibration target. Some white-space fixes. +% 24. 3.2020 Cleaned up the documentation of this function, and added +% additiontal handling for two types of stereoscopic +% calibrations, ability to reference video files for +% animated calibration targets, bug fixes for audio +% feedback playback. Apologies to NJ for removing +% previous comments where code was previously added, this +% was done for easier reading of the code. + +global eyelinkanimationtarget; %#ok + +% Cached texture handle and size for eyelink eye image and texture: persistent eyelinktex; -global dw dh offscreen; +persistent dw dh; % Cached window handle for target onscreen window: persistent eyewin; @@ -50,8 +81,6 @@ % Cached(!) eyelink stucture containing keycodes persistent el; -persistent lastImageTime; %#ok -persistent drawcount; persistent ineyeimagemodedisplay; persistent clearScreen; persistent drawInstructions; @@ -61,14 +90,21 @@ persistent GL_RGBA8; persistent hostDataFormat; +% target & feedback beep waveforms and PsychPortAudio buffers +persistent audio_status; +persistent audio_devinfo; +persistent audio_n_chan; +persistent audio_fs; +%persistent audio_ppa_isSlave; +persistent beep_waveforms; + persistent inDrift; -offscreen = 0; newImage = 0; - if 0 == Screen('WindowKind', eyelinktex) - eyelinktex = []; % got persisted from a previous ptb window which has now been closed; needs to be recreated + eyelinktex = []; % Previous PTB Screen() window has closed, needs to be recreated. end + if isempty(eyelinktex) % Define the two OpenGL constants we actually need. No point in % initializing the whole PTB OpenGL mode for just two constants: @@ -78,8 +114,6 @@ GL_UNSIGNED_INT_8_8_8_8 = 32821; %#ok GL_UNSIGNED_INT_8_8_8_8_REV = 33639; hostDataFormat = GL_UNSIGNED_INT_8_8_8_8_REV; - drawcount = 0; - lastImageTime = GetSecs; end % Preinit return code to zero: @@ -107,20 +141,53 @@ if Screen('WindowKind', callArgs.window) ~= 1 error('argument didn''t contain a valid handle of an open onscreen window! pass in result of EyelinkInitDefaults(previouslyOpenedPTBWindowPtr).'); end - + % Ok, valid handle. Assign it and return: eyewin = callArgs.window; - + % Assume rest of el structure is valid: el = callArgs; clearScreen=1; eyelinktex=[]; - lastImageTime=GetSecs; ineyeimagemodedisplay=0; drawInstructions=1; return; end +if ~isempty(el.ppa_pahandle) && isempty(audio_status) + audio_status = PsychPortAudio('GetStatus', el.ppa_pahandle); + audio_devinfo = PsychPortAudio('GetDevices', [], audio_status.OutDeviceIndex); + audio_n_chan = min(2,audio_devinfo.NrOutputChannels); + audio_fs = audio_status.SampleRate; + %if PsychPortAudio('SetOpMode', pamaster) > + % audio_ppa_isSlave +elseif isempty(el.ppa_pahandle) && isempty(audio_fs) + audio_status = NaN; + audio_devinfo = NaN; + audio_n_chan = 1; + audio_fs = Snd('DefaultRate'); +end + +if isempty(beep_waveforms) + if el.targetbeep + beep_waveforms{1} = repmat(MakeBeep(el.cal_target_beep(1), el.cal_target_beep(3), audio_fs) .* el.cal_target_beep(2), audio_n_chan, 1); + beep_waveforms{2} = repmat(MakeBeep(el.drift_correction_target_beep(1), el.drift_correction_target_beep(3), audio_fs) .* el.drift_correction_target_beep(2), audio_n_chan, 1); + else + beep_waveforms{1} = NaN; + beep_waveforms{2} = NaN; + end + if el.feedbackbeep + beep_waveforms{3} = repmat(MakeBeep(el.calibration_failed_beep(1), el.calibration_failed_beep(3), audio_fs) .* el.calibration_failed_beep(2), audio_n_chan, 1); + beep_waveforms{4} = repmat(MakeBeep(el.calibration_success_beep(1), el.calibration_success_beep(3),audio_fs) .* el.calibration_success_beep(2), audio_n_chan, 1); + beep_waveforms{5} = repmat(MakeBeep(el.drift_correction_failed_beep(1), el.drift_correction_failed_beep(3), audio_fs) .* el.drift_correction_failed_beep(2), audio_n_chan, 1); + beep_waveforms{6} = repmat(MakeBeep(el.drift_correction_success_beep(1), el.drift_correction_success_beep(3), audio_fs) .* el.drift_correction_success_beep(2), audio_n_chan, 1); + else + beep_waveforms{3} = NaN; + beep_waveforms{4} = NaN; + beep_waveforms{5} = NaN; + beep_waveforms{6} = NaN; + end +end % Not an eyelink struct. Either a 4 component vector from Eyelink(), or something wrong: if length(callArgs) ~= 4 @@ -130,325 +197,421 @@ % Extract command code: eyecmd = callArgs(1); -if isempty(eyewin) +if isempty(eyewin) && eyecmd ~= 3 warning('Got called as callback function from Eyelink() but usercode has not set a valid target onscreen window handle yet! Aborted.'); %#ok return; end -% Flag that tells if a new camera image was received and our camera image texture needs update: +% (Re)set Flag for new camera image newcamimage = 0; needsupdate = 0; switch eyecmd - case 1, - % New videoframe received. See code below for actual processing. + case 1 % New Camera Image Received + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 1; New Camera Image Received\n'); + end newcamimage = 1; needsupdate = 1; - case 2, - % Eyelink Keyboard query: + + case 2 % EyeLink Keyboard Query + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 2; Keyboard Query\n'); + end [rc, el] = EyelinkGetKey(el); - case 3, - % Alert message: + + case 3 % Alert message + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 3; Alert Message\n'); + end fprintf('Eyelink Alert: %s.\n', msg); - needsupdate = 1; - case 4, - % Image title of camera image transmitted from Eyelink: - % fprintf('Eyelink image title is %s. [Threshold = %f]\n', msg, callArgs(2)); + needsupdate = 0; + + case 4 % Camera Image Caption Text + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 4; Camera Image Caption Text\n'); + end if callArgs(2) ~= -1 imgtitle = sprintf('Camera: %s [Threshold = %f]', msg, callArgs(2)); else imgtitle = msg; end needsupdate = 1; - case 5, - % Define calibration target and enable its drawing: - % fprintf('draw_cal_target.\n'); + + case 5 % Draw Cal Target + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 5; Draw Cal Target\n'); + end calxy = callArgs(2:3); clearScreen=1; needsupdate = 1; - case 6, - % Clear calibration display: - % fprintf('clear_cal_display.\n'); + if strcmpi(el.calTargetType, 'video') && ~isempty(eyelinkanimationtarget) + if el.calAnimationResetOnTargetMove && Screen('GetMovieTimeIndex', eyelinkanimationtarget.movie) + Screen('SetMovieTimeIndex', eyelinkanimationtarget.movie, 0, el.calAnimationSetIndexIsFrames); + end + Screen('PlayMovie', eyelinkanimationtarget.movie, 1, el.calAnimationLoopParam, el.calAnimationAudioVolume); + end + + case 6 % Clear Cal Display + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 6; Clear Cal Display\n'); + end clearScreen=1; drawInstructions=1; needsupdate = 1; - case 7, - % Setup calibration display: + + case 7 % Setup Cal Display + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 7; Setup Cal Display\n'); + end if inDrift drawInstructions = 0; - inDrift = 0; else drawInstructions = 1; end - clearScreen=1; - % drawInstructions=1; - drawcount = 0; - lastImageTime = GetSecs; needsupdate = 1; - case 8, + + case 8 % Setup Image Display + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 8; Setup Image Display\n'); + end newImage = 1; - % Setup image display: eyewidth = callArgs(2); eyeheight = callArgs(3); - % fprintf('setup_image_display for %i x %i pixels.\n', eyewidth, eyeheight); - drawcount = 0; - lastImageTime = GetSecs; ineyeimagemodedisplay=1; drawInstructions=1; needsupdate = 1; - case 9, - % Exit image display: - % fprintf('exit_image_display.\n'); - % fprintf('AVG FPS = %f Hz\n', drawcount / (GetSecs - lastImageTime)); + + case 9 % Exit Image Display + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 9; Exit Image Display\n'); + end clearScreen=1; ineyeimagemodedisplay=0; drawInstructions=1; needsupdate = 1; - case 10, - % Erase current calibration target: - % fprintf('erase_cal_target.\n'); + + case 10 % Erase Cal Target + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 10; Erase Cal Target\n'); + end calxy = []; + if ~isempty(eyelinkanimationtarget) + eyelinkanimationtarget.calxy=calxy; + end clearScreen=1; needsupdate = 1; - case 11, - % fprintf('exit_cal_display.\n'); - % fprintf('AVG FPS = %f Hz\n', drawcount / (GetSecs - lastImageTime)); - clearScreen=1; - % drawInstructions=1; - needsupdate = 1; - case 12, - % New calibration target sound: - % fprintf('cal_target_beep_hook.\n'); - EyelinkMakeSound(el, 'cal_target_beep'); - case 13, - % New drift correction target sound: - % fprintf('dc_target_beep_hook.\n'); - EyelinkMakeSound(el, 'drift_correction_target_beep'); - case 14, - % Calibration done sound: - errc = callArgs(2); - % fprintf('cal_done_beep_hook: %i\n', errc); - if errc > 0 - % Calibration failed: - EyelinkMakeSound(el, 'calibration_failed_beep'); - else - % Calibration success: - EyelinkMakeSound(el, 'calibration_success_beep'); - end - case 15, - % Drift correction done sound: - errc = callArgs(2); - % fprintf('dc_done_beep_hook: %i\n', errc); - if errc > 0 - % Drift correction failed: - EyelinkMakeSound(el, 'drift_correction_failed_beep'); + + if strcmpi(el.calTargetType, 'video') && ~isempty(eyelinkanimationtarget) + texkill=Screen('GetMovieImage', eyewin, eyelinkanimationtarget.movie, el.calAnimationWaitTexClose); + Screen('PlayMovie', eyelinkanimationtarget.movie, 0, el.calAnimationLoopParam); + if texkill > 0 + Screen('Close', texkill); + end + end + + case 11 % Exit Cal Display + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 11; Exit Cal Display\n'); + end + calxy = []; + if ~isempty(eyelinkanimationtarget) + eyelinkanimationtarget.calxy=calxy; + end + + if inDrift + inDrift = 0; + drawInstructions = 0; else - % Drift correction success: - EyelinkMakeSound(el, 'drift_correction_success_beep'); + drawInstructions = 1; + end + + clearScreen=1; + needsupdate=1; + if strcmpi(el.calTargetType, 'video') && ~isempty(eyelinkanimationtarget) + texkill=Screen('GetMovieImage', eyewin, eyelinkanimationtarget.movie, el.calAnimationWaitTexClose); + Screen('PlayMovie', eyelinkanimationtarget.movie, 0, el.calAnimationLoopParam); + if texkill > 0 + Screen('Close', texkill); + end + end + + case 12 % New Cal Target Sound + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 12; New Cal Target Sound\n'); + end + if el.targetbeep && ~strcmpi(el.calTargetType, 'video') + EyelinkMakeSound(el, 'cal_target_beep', 1); + end + + case 13 % New Drift Chk/Corr Target Sound + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 13; New Drift Target Sound\n'); + end + if el.targetbeep && ~strcmpi(el.calTargetType, 'video') + EyelinkMakeSound(el, 'drift_correction_target_beep', 2); + end + + case 14 % Cal Done Sound + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 14; Cal Done Sound\n'); + end + if el.feedbackbeep && ~strcmpi(el.calTargetType, 'video') + errc = callArgs(2); + if errc > 0 + % Failed + EyelinkMakeSound(el, 'calibration_failed_beep', 3); + else + % Success + EyelinkMakeSound(el, 'calibration_success_beep', 4); + end + end + + case 15 % Drift Chk/Corr Done Sound + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 15; Drift Done Sound\n'); + end + if el.feedbackbeep && ~strcmpi(el.calTargetType, 'video') + errc = callArgs(2); + if errc > 0 + % Failed + EyelinkMakeSound(el, 'drift_correction_failed_beep', 5); + else + % Success + EyelinkMakeSound(el, 'drift_correction_success_beep', 6); + end + end + + case 16 % Get Mouse Position + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 16; Get Mouse Position\n'); end - % add by NJ - case 16, [width, height]=Screen('WindowSize', eyewin); - % get mouse [x,y, buttons] = GetMouse(eyewin); - - HideCursor + HideCursor(eyewin); if find(buttons) rc = [width , height, x , y, dw , dh , 1]; else rc = [width , height, x , y , dw , dh , 0]; end - % add by NJ to prevent flashing of text in drift correct - case 17, - inDrift =1; - otherwise - % Unknown command: + % add by NJ to prevent flashing of text in drift correct + case 17 % Non-native callback, from PsychEyelink_setup_cal_display() + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == 17; Flag in drift check/correction mode\n'); + end + inDrift = 1; + + case -1 % Non-native callback, from Eyelink('Shutdown') for runtime cleanup + if Eyelink('Verbosity') >= 5 + fprintf('PsychEyelinkDispatchCallback: eyecmd == -1; Runtime cleanup\n'); + end + + % Using the Snd() path for audio output? + if isfield(el, 'ppa_sndhandle') && ~isempty(el.ppa_sndhandle) + % Let Snd() fully detach from the sound device: + Snd('Close', 1); + + % Close sound device: + PsychPortAudio('Close', el.ppa_sndhandle); + el.ppa_sndhandle = []; + end + + % Clear all persistent and local variables, effectively resetting all: + clear variables; + + % Done with cleanup / shutdown: + rc = 0; + return; + + otherwise % Unknown Command fprintf('PsychEyelinkDispatchCallback: Unknown eyelink command (%i)\n', eyecmd); return; -end +end % switch -% Display redraw and update needed? -if ~needsupdate - % Nope. Return from callback: +if ~needsupdate % Display redraw and update needed? + if ~isempty(eyelinkanimationtarget) && ~isempty(calxy) + EyelinkDrawCalibrationTarget(eyewin, el, calxy, eyelinkanimationtarget); + end return; end -% Need to rebuild/redraw and flip the display: -% need to clear screen? -if clearScreen==1 - Screen('FillRect', eyewin, el.backgroundcolour); +if clearScreen==1 % Need to clear and redraw display before new content flipped + EyelinkDrawClearScreen(eyewin, el); clearScreen=0; end -% New video data from eyelink? -if newcamimage - % Video callback from Eyelink: We have a 'eyewidth' by 'eyeheight' pixels - % live eye image from the Eyelink system. Each pixel is encoded as a 4 byte - % RGBA pixel with alpha channel set to a constant value of 255 and the RGB - % channels encoding a 1-Byte per channel R, G or B color value. The - % given 'eyeimgptr' is a specially encoded memory pointer to the memory - % buffer inside Eyelink() that encodes the image. + +if newcamimage % New image frame received from EyeLink camera stream + % Image has dimensions: 'eyewidth' by 'eyeheight' in pixel units + % Each pixel is encoded as a 4 byte RGBA pixel with A=255 always + % RGB channels each encode a 1-Byte per channel R, G or B color value. + % 'eyeimgptr' is a memory pointer to the buffer inside Eyelink() that + % encodes the image. eyeimgptr = callArgs(2); eyewidth = callArgs(3); eyeheight = callArgs(4); - % Create a new PTB texture of proper format and size and inject the 4 - % channel RGBA color image from the Eyelink memory buffer into the texture. - % Return a standard PTB texture handle to it. If such a texture already - % exists from a previous invocation of this routiene, just recycle it for - % slightly higher efficiency: + % Creates a new or reuses an existing PTB texture for the cam image eyelinktex = Screen('SetOpenGLTextureFromMemPointer', eyewin, eyelinktex, eyeimgptr, eyewidth, eyeheight, 4, 0, [], GL_RGBA8, GL_RGBA, hostDataFormat); end -% If we're in imagemodedisplay, draw eye camera image texture centered in -% window, if any such texture exists, also draw title if it exists. -if ~isempty(eyelinktex) && ineyeimagemodedisplay==1 - imgtitle=EyelinkDrawCameraImage(eyewin, el, eyelinktex, imgtitle,newImage); +if ~isempty(eyelinktex) && ineyeimagemodedisplay==1 % Draw cam image and caption + [imgtitle, dw, dh] = EyelinkDrawCameraImage(eyewin, el, eyelinktex, imgtitle, newImage); end -% Draw calibration target, if any is specified: -if ~isempty(calxy) +if ~isempty(calxy) % Draw Cal Target drawInstructions=0; - EyelinkDrawCalibrationTarget(eyewin, el, calxy); + EyelinkDrawCalibrationTarget(eyewin, el, calxy, eyelinkanimationtarget); + if strcmpi(el.calTargetType, 'video') + return; + end end -% Need to draw instructions? -if drawInstructions==1 - EyelinkDrawInstructions(eyewin, el,msg); - drawInstructions=0; +if drawInstructions == 1 % Draw Instructions + EyelinkDrawInstructions(eyewin, el, msg); + drawInstructions = 0; end -% Show it: We disable synchronization of Matlab to the vertical retrace. -% This way, display update itself is still synced and tear-free, but we -% don't waste time waiting for swap completion. Potentially higher -% performance for calibration displays and eye camera image updates... -% Neither do we erase buffer -Screen('Flip', eyewin, [], 1, 1); - -% Some counter, just to measure update rate: -drawcount = drawcount + 1; +dontsync = 1; +dontclear = 0; % set to 0 to hide instructions during camera display +Screen('Flip', eyewin, [], dontclear, dontsync); % Show it -% Done. Return from callback: return; -function EyelinkDrawInstructions(eyewin, el,msg) -oldFont=Screen(eyewin,'TextFont',el.msgfont); -oldFontSize=Screen(eyewin,'TextSize',el.msgfontsize); -DrawFormattedText(eyewin, el.helptext, 20, 20, el.msgfontcolour, [], [], [], 1); - -if el.displayCalResults && ~isempty(msg) - DrawFormattedText(eyewin, msg, 20, 150, el.msgfontcolour, [], [], [], 1); -end +% Start of nested EyelinkDraw* function declarations + function EyelinkDrawClearScreen(eyewin, el) + % Set drawScreens 0 for mono modes, 1 for stereo modes: + drawScreens = double(el.winInfo.StereoMode ~= 0); + for it = 0:drawScreens + Screen('SelectStereoDrawBuffer', eyewin, it); % select left eye window + Screen('FillRect', eyewin, el.backgroundcolour); + end + end -Screen(eyewin,'TextFont',oldFont); -Screen(eyewin,'TextSize',oldFontSize); + function EyelinkDrawInstructions(eyewin, el,msg) + oldFont=Screen(eyewin,'TextFont',el.msgfont); + oldFontSize=Screen(eyewin,'TextSize',el.msgfontsize); + + % Set drawScreens 0 for mono modes, 1 for stereo modes: + drawScreens = double(el.winInfo.StereoMode ~= 0); + for it = 0:drawScreens + Screen('SelectStereoDrawBuffer', eyewin, it); % select left eye window + DrawFormattedText(eyewin, el.helptext, 20, 20, el.msgfontcolour, [], [], [], 1); + + if el.displayCalResults && ~isempty(msg) + DrawFormattedText(eyewin, msg, 20, 150, el.msgfontcolour, [], [], [], 1); + end + end + Screen(eyewin,'TextFont',oldFont); + Screen(eyewin,'TextSize',oldFontSize); + end -function imgtitle=EyelinkDrawCameraImage(eyewin, el, eyelinktex, imgtitle,newImage) -persistent lasttitle; -global dh dw offscreen; -try - if ~isempty(eyelinktex) + function [imgtitle, dw, dh] = EyelinkDrawCameraImage(eyewin, el, eyelinktex, imgtitle, newImage) eyerect=Screen('Rect', eyelinktex); % we could cash some of the below values.... wrect=Screen('Rect', eyewin); - [width, heigth]=Screen('WindowSize', eyewin); + [width, height]=Screen('WindowSize', eyewin); dw=round(el.eyeimgsize/100*width); dh=round(dw * eyerect(4)/eyerect(3)); - drect=[ 0 0 dw dh ]; drect=CenterRect(drect, wrect); - Screen('DrawTexture', eyewin, eyelinktex, [], drect); - % fprintf('EyelinkDrawCameraImage:DrawTexture \n'); - end - % imgtitle - % if title is provided, we also draw title - if ~isempty(eyelinktex) && exist( 'imgtitle', 'var') && ~isempty(imgtitle) - %oldFont=Screen(eyewin,'TextFont',el.imgtitlefont); - %oldFontSize=Screen('TextSize',eyewin,el.imgtitlefontsize); - rect=Screen('TextBounds', eyewin, imgtitle ); - [w2, h2]=RectSize(rect); - - % added by NJ as a quick way to prevent over drawing and to clear text - if newImage || isempty(lasttitle) || ~strcmp(imgtitle,lasttitle) - if -1 == Screen('WindowKind', offscreen) - Screen('Close', offscreen); + tx = drect(1); + ty = drect(4) + el.imgtitlefontsize; + + if ~isempty(imgtitle) + otf = Screen('TextFont', eyewin, el.imgtitlefont); + ots = Screen('TextSize', eyewin, el.imgtitlefontsize); + else + imgtitle = ''; + end + + % Set drawScreens 0 for mono modes, 1 for stereo modes: + drawScreens = double(el.winInfo.StereoMode ~= 0); + for it = 0:drawScreens + try + Screen('SelectStereoDrawBuffer', eyewin, it); % select current-eye window + Screen('DrawTexture', eyewin, eyelinktex, [], drect); + Screen('DrawText', eyewin, imgtitle, tx, ty, el.imgtitlecolour); + catch + fprintf('EyelinkDrawCameraImage:error \n'); + disp(psychlasterror); end + end - sn = Screen('WindowScreenNumber', eyewin); - offscreen = Screen('OpenOffscreenWindow', sn, el.backgroundcolour, [], [], 32); - Screen(offscreen,'TextFont',el.imgtitlefont); - Screen(offscreen,'TextSize',el.imgtitlefontsize); - Screen('DrawText', offscreen, imgtitle, width/2-dw/2, heigth/2+dh/2+h2, el.imgtitlecolour); - Screen('DrawTexture',eyewin,offscreen, [width/2-dw/2 heigth/2+dh/2+h2 width/2-dw/2+500 heigth/2+dh/2+h2+500], [width/2-dw/2 heigth/2+dh/2+h2 width/2-dw/2+500 heigth/2+dh/2+h2+500]); - Screen('Close',offscreen); - newImage = 0; - end - %imgtitle=[]; % return empty title, so it doesn't get drawn over and over again. - lasttitle = imgtitle; + if ~isempty(imgtitle) + Screen('TextFont', eyewin, otf); + Screen('TextSize', eyewin, ots); + end end -catch %myerr - fprintf('EyelinkDrawCameraImage:error \n'); - disp(psychlasterror); -end -function EyelinkMakeSound(el, s) -% set all sounds in one place, sound params defined in -% eyelinkInitDefaults - -switch(s) - case 'cal_target_beep' - doBeep=el.targetbeep; - f=el.cal_target_beep(1); - v=el.cal_target_beep(2); - d=el.cal_target_beep(3); - case 'drift_correction_target_beep' - doBeep=el.targetbeep; - f=el.drift_correction_target_beep(1); - v=el.drift_correction_target_beep(2); - d=el.drift_correction_target_beep(3); - case 'calibration_failed_beep' - doBeep=el.feedbackbeep; - f=el.calibration_failed_beep(1); - v=el.calibration_failed_beep(2); - d=el.calibration_failed_beep(3); - case 'calibration_success_beep' - doBeep=el.feedbackbeep; - f=el.calibration_success_beep(1); - v=el.calibration_success_beep(2); - d=el.calibration_success_beep(3); - case 'drift_correction_failed_beep' - doBeep=el.feedbackbeep; - f=el.drift_correction_failed_beep(1); - v=el.drift_correction_failed_beep(2); - d=el.drift_correction_failed_beep(3); - case 'drift_correction_success_beep' - doBeep=el.feedbackbeep; - f=el.drift_correction_success_beep(1); - v=el.drift_correction_success_beep(2); - d=el.drift_correction_success_beep(3); - otherwise - % some defaults - doBeep=el.feedbackbeep; - f=500; - v=0.5; - d=1.5; -end - -if doBeep==1 - Beeper(f, v, d); -end + function EyelinkDrawCalibrationTarget(eyewin, el, calxy, eyelinkanimationtarget) + [width, height]=Screen('WindowSize', eyewin); -function EyelinkDrawCalibrationTarget(eyewin, el, calxy) -[width, heigth]=Screen('WindowSize', eyewin); -size=round(el.calibrationtargetsize/100*width); -inset=round(el.calibrationtargetwidth/100*width); + % Set drawScreens 0 for mono modes, 1 for stereo modes: + drawScreens = double(el.winInfo.StereoMode ~= 0); + for it = 0:drawScreens + Screen('SelectStereoDrawBuffer', eyewin, it); % select eye window + switch el.calTargetType + case 'video' + if( ~isempty(el.calAnimationTargetFilename) && ~isempty(eyelinkanimationtarget)) + rect=CenterRectOnPoint([0 0 eyelinkanimationtarget.imgw eyelinkanimationtarget.imgh], calxy(1), calxy(2)); + if it == 0 + tex=Screen('GetMovieImage', eyewin, eyelinkanimationtarget.movie, el.calAnimationWaitTex); + end + if(tex>0) + Screen('DrawTexture', eyewin , tex, [], rect, [], 0); + if it == drawScreens + Screen('Flip', eyewin); + Screen('Close', tex); + end + end + end + + case 'image' + rect=CenterRectOnPoint([0 0 el.calImageInfo.Width el.calImageInfo.Height], calxy(1), calxy(2)); + Screen('DrawTexture', eyewin , el.calImageTexture, [], rect, [], 0); + + otherwise + % default to el.calTargetType = 'ellipse' target drawing + size=round(el.calibrationtargetsize / 100 * width); + inset=round(el.calibrationtargetwidth / 100 * width); + + % Use FillOval for larger dots: + Screen('FillOval', eyewin, el.calibrationtargetcolour, [calxy(1)-size/2 calxy(2)-size/2 calxy(1)+size/2 calxy(2)+size/2], size+2); + Screen('FillOval', eyewin, el.backgroundcolour, [calxy(1)-inset/2 calxy(2)-inset/2 calxy(1)+inset/2 calxy(2)+inset/2], inset+2) + end + end + end -insetSize = size-2*inset; -if insetSize < 1 - insetSize = 1; + function EyelinkMakeSound(el, s, i) + % set all sounds in one place, sound params defined in + % eyelinkInitDefaults + if any(strcmp( ... + {'cal_target_beep', ... + 'drift_correction_target_beep', ... + 'calibration_failed_beep', ... + 'calibration_success_beep', ... + 'drift_correction_failed_beep', ... + 'drift_correction_success_beep'},s)) + % beep waveform was prepared in advance + if isempty(el.ppa_pahandle) + Snd('Play', beep_waveforms{i}, audio_fs); + else + PsychPortAudio('FillBuffer', el.ppa_pahandle, beep_waveforms{i}); + PsychPortAudio('Start', el.ppa_pahandle); + if strcmp('drift_correction_success_beep', s) + PsychPortAudio('Stop', el.ppa_pahandle, 1); + end + end + else + % some defaults + if isempty(el.ppa_pahandle) + Snd('Play', MakeBeep(500, 1.5) .* 0.5, audio_fs); + else + PsychPortAudio('FillBuffer', el.ppa_pahandle, repmat(MakeBeep(500, 1.5, audio_fs) .* 0.5, audio_n_chan, 1)); + PsychPortAudio('Start', el.ppa_pahandle); + end + end + end end - -% Use FillOval for larger dots: -Screen('FillOval', eyewin, el.calibrationtargetcolour, [calxy(1)-size/2 calxy(2)-size/2 calxy(1)+size/2 calxy(2)+size/2], size+2); -Screen('FillOval', eyewin, el.backgroundcolour, [calxy(1)-inset/2 calxy(2)-inset/2 calxy(1)+inset/2 calxy(2)+inset/2], inset+2); diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/contents.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/contents.m deleted file mode 100644 index d9e6adcf3c..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkBasic/contents.m +++ /dev/null @@ -1,2 +0,0 @@ -% collection of essential functions for the eyelink toolbox -% EyelinkToolbox:EyelinkBasic diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/contents.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/Contents.m similarity index 82% rename from Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/contents.m rename to Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/Contents.m index 4be4288863..a65a7bf665 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/contents.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/Contents.m @@ -2,3 +2,5 @@ % Demos of eyelink toolbox functions and demos % that may serve as a starting point for your own experiments % +% See also SR-RESEARCHDEMOS +% diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkEventExample.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkEventExample.m deleted file mode 100644 index d24bf51f0d..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkEventExample.m +++ /dev/null @@ -1,242 +0,0 @@ -function EyelinkEventExample -% Short MATLAB example program to demonstrate the use of events -% with the Eyelink and Psychophysics Toolboxes. When subject gazes at an object -% it changes color. When gaze moves away, it toggles back -% - -% history -% 28-10-02 fwc created it, based on eyelinkexample -% 27-11-02 fwc changed dialog for dummy mode question, fixed dodriftcorrection bug -% 07-07-10 fwc adapted to use new psychtooblox/eyelink functions, finally -% got toggling correct too ;-) -% -dummymode=0; % set to 1 to run in dummymode (using mouse as pseudo-eyetracker) -showboxes=1; % set to 1 to show boxes within which you have to fixate in order to toggle a number - -try - - fprintf('EyelinkToolbox Event Example\n\n\t'); - - % STEP 1 - % Open a graphics window on the main screen - % using the PsychToolbox's Screen function. - screenNumber=max(Screen('Screens')); - window=Screen('OpenWindow', screenNumber); - - % STEP 2 - % Provide Eyelink with details about the graphics environment - % and perform some initializations. The information is returned - % in a structure that also contains useful defaults - % and control codes (e.g. tracker state bit and Eyelink key values). - el=EyelinkInitDefaults(window); - % Disable key output to Matlab window: - ListenChar(2); - - % STEP 3 - % Initialization of the connection with the Eyelink Gazetracker. - % exit program if this fails. - if ~EyelinkInit(dummymode, 1) - fprintf('Eyelink Init aborted.\n'); - cleanup; % cleanup function - return; - end - - [v vs]=Eyelink('GetTrackerVersion'); - fprintf('Running experiment on a ''%s'' tracker.\n', vs ); - - % make sure that we get event data from the Eyelink -% Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,AREA'); - Eyelink('command', 'link_event_data = GAZE,GAZERES,HREF,AREA,VELOCITY'); - Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,BLINK,SACCADE,BUTTON'); - - % open file to record data to - edfFile='demo.edf'; - Eyelink('Openfile', edfFile); - - % STEP 4 - % Calibrate the eye tracker - EyelinkDoTrackerSetup(el); - - - % STEP 5 - % draw a stimulus in a set of buffers - [buffer, altbuffer, object]=DrawStimulus(window, showboxes); - - Screen('TextFont', buffer, el.msgfont); - Screen('TextSize', buffer, el.msgfontsize); - Screen('TextFont', altbuffer, el.msgfont); - Screen('TextSize', altbuffer, el.msgfontsize); - [width, height]=Screen('WindowSize', window); - message='Press space to stop.'; - Screen('DrawText', buffer, message, 200, height-el.msgfontsize-20, el.msgfontcolour); - Screen('DrawText', altbuffer, message, 200, height-el.msgfontsize-20, el.msgfontcolour); - - - % STEP 6 - % do a final check of calibration using driftcorrection - success=EyelinkDoDriftCorrection(el); - if success~=1 - cleanup; - return; - end - - % STEP 7 - % some final preparations - stopkey=KbName('space'); - lastchoice=-1; - choice=-1; - doflip=0; - % put buffer on screen - Screen('CopyWindow', buffer, window); - Screen('Flip', window, [], 1); % don't erase buffer - - % start recording eye position - Eyelink('startrecording'); - % record a few samples before we actually start displaying - waitsecs(0.1); - % mark zero-plot time in data file - Eyelink('message', 'SYNCTIME'); - - % STEP 8 - % adapt display based on END-SACCADE events - while 1 % loop till error or space bar is pressed - error=Eyelink('checkrecording'); % Check recording status, stop display if error - if(error~=0) - break; - end - - [keyIsDown,secs,keyCode] = KbCheck; % check for keyboard press - if keyCode(stopkey) % if spacebar was pressed stop display - break; - end - - % check for endsaccade events - if Eyelink('isconnected') == el.dummyconnected % in dummy mode use mousecoordinates - [x,y,button] = GetMouse(window); - evt.type=el.ENDSACC; - evt.genx=x; - evt.geny=y; - evtype=el.ENDSACC; - else % check for events - evtype=Eyelink('getnextdatatype'); - end - if evtype==el.ENDSACC % if the subject finished a saccade check if it fell on an object - if Eyelink('isconnected') == el.connected % if we're really measuring eye-movements - evt = Eyelink('getfloatdata', evtype); % get data - end - % check if saccade landed on an object - choice=-1; - noobject=0; - i=1; - while 1 - if 1==IsInRect(evt.genx,evt.geny, object(i).rect ) - choice=i; - break; - end - i=i+1; - if i>length(object) - noobject=1; - break; - end - end - if lastchoice>0 && (choice~=lastchoice || noobject==1) % toggle object color - if object(lastchoice).on==1 % restore screen - Screen('CopyWindow', buffer, window, object(lastchoice).rect, object(lastchoice).rect); - object(lastchoice).on=0; - lastchoice=-1; - doflip=1; - end - end - if choice>0 && choice~=lastchoice % toggle object color - if object(choice).on==0 % toggle object on screen - Screen('CopyWindow', altbuffer, window, object(choice).rect, object(choice).rect); - object(choice).on=1; - doflip=1; - end - lastchoice=choice; - end - if doflip==1 - Screen('Flip', window, [], 1); - doflip=0; - end - end % saccade? - end % main loop - - % STEP 9 - % finish demo - waitsecs(0.1); % wait a while to record a few more samples - cleanup; - -catch myerr - %this "catch" section executes in case of an error in the "try" section - %above. Importantly, it closes the onscreen window if its open. - cleanup; - psychrethrow(psychlasterror); -end %try..catch. - - -% Cleanup routine: -function cleanup - -% finish up: stop recording eye-movements, -% close graphics window, close data file and shut down tracker -% Eyelink('Stoprecording'); -% Eyelink('CloseFile'); -Eyelink('Shutdown'); - -% Close window: -sca; - -% Restore keyboard output to Matlab: -ListenChar(0); - -function [buffer, altbuffer, object]=DrawStimulus(window, showboxes) - -if ~exist('showboxes', 'var') || isempty(showboxes) - showboxes=0; -end -white=WhiteIndex(window); -black=BlackIndex(window); -gray=GrayIndex(window); -Screen(window, 'FillRect',gray); -[w,h]=WindowSize(window); -[x,y]=WindowCenter(window); -fontsize=round(w/100*5); -oldFont=Screen(window,'TextFont','Arial'); -oldFontSize=Screen(window,'TextSize',fontsize); - -buffer=Screen(window, 'OpenOffscreenWindow', gray); -altbuffer=Screen(window, 'OpenOffscreenWindow', gray); - -cols=5; -rows=4; - -Screen(buffer,'TextFont','Arial'); -Screen(altbuffer,'TextFont','Arial'); -Screen(buffer,'TextSize',fontsize); -Screen(altbuffer,'TextSize',fontsize); -k=1; -xdist=round(w/(cols)); -ydist=round(h/(rows)); -x0=x-((cols-1)/2)*xdist; -y0=y-((rows-1)/2)*ydist; -for i=1:rows - for j=1:cols - text=num2str(k); - xpos=round(x0+(j-1)*xdist-fontsize/2); - ypos=round(y0+(i-1)*ydist-fontsize/2); - Screen(buffer,'DrawText',text,xpos,ypos,white); - Screen(altbuffer,'DrawText',text,xpos,ypos,black); - rect= Screen('TextBounds', window, text, xpos,ypos ); %[,yPositionIsBaseline] [,swapTextDirection]); - object(k).rect=OffsetRect(rect, xpos, ypos); - if showboxes==1 - Screen('FrameRect', buffer, black, object(k).rect); - Screen('FrameRect', altbuffer, white, object(k).rect); - end - object(k).on=0; - k=k+1; - end -end - -Screen(window,'TextFont',oldFont); -Screen(window,'TextSize',oldFontSize); diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkExample.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkExample.m deleted file mode 100644 index bf3594bfce..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkExample.m +++ /dev/null @@ -1,165 +0,0 @@ -function EyelinkExample - -% Short MATLAB example program that uses the Eyelink and Psychophysics -% Toolboxes to create a real-time gaze-dependent display. -% This is the example as shown in the EyelinkToolbox article in BRMIC -% Cornelissen, Peters and Palmer 2002), but updated to use new routines -% and functionality. -% -% History -% ~2006 fwc created it, to use updated functions -% 15-06-10 fwc updated to enable eye image display -% 17-06-10 fwc made colour of the gaze dot change, just for fun -PsychDefaultSetup(1); - -try - fprintf('EyelinkToolbox Example\n\n\t'); - dummymode=0; % set to 1 to initialize in dummymode (rather pointless for this example though) - - % STEP 1 - % Open a graphics window on the main screen - % using the PsychToolbox's Screen function. - screenNumber=max(Screen('Screens')); - window=Screen('OpenWindow', screenNumber); - - % STEP 2 - % Provide Eyelink with details about the graphics environment - % and perform some initializations. The information is returned - % in a structure that also contains useful defaults - % and control codes (e.g. tracker state bit and Eyelink key values). - el=EyelinkInitDefaults(window); - - % Disable key output to Matlab window: - ListenChar(2); - - % STEP 3 - % Initialization of the connection with the Eyelink Gazetracker. - % exit program if this fails. - if ~EyelinkInit(dummymode, 1) - fprintf('Eyelink Init aborted.\n'); - cleanup; % cleanup function - return; - end - - [v vs]=Eyelink('GetTrackerVersion'); - fprintf('Running experiment on a ''%s'' tracker.\n', vs ); - - % make sure that we get gaze data from the Eyelink - Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,AREA'); - - % open file to record data to - edfFile='demo.edf'; - Eyelink('Openfile', edfFile); - - % STEP 4 - % Calibrate the eye tracker - EyelinkDoTrackerSetup(el); - - % do a final check of calibration using driftcorrection - EyelinkDoDriftCorrection(el); - - % STEP 5 - % start recording eye position - Eyelink('StartRecording'); - % record a few samples before we actually start displaying - WaitSecs(0.1); - % mark zero-plot time in data file - Eyelink('Message', 'SYNCTIME'); - stopkey=KbName('space'); - eye_used = -1; - - Screen('FillRect', el.window, el.backgroundcolour); - Screen('TextFont', el.window, el.msgfont); - Screen('TextSize', el.window, el.msgfontsize); - [width, height]=Screen('WindowSize', el.window); - message='Press space to stop.'; - Screen('DrawText', el.window, message, 200, height-el.msgfontsize-20, el.msgfontcolour); - Screen('Flip', el.window, [], 1); - - % STEP 6 - % show gaze-dependent display - while 1 % loop till error or space bar is pressed - % Check recording status, stop display if error - error=Eyelink('CheckRecording'); - if(error~=0) - break; - end - % check for keyboard press - [keyIsDown, secs, keyCode] = KbCheck; - % if spacebar was pressed stop display - if keyCode(stopkey) - break; - end - % check for presence of a new sample update - if Eyelink( 'NewFloatSampleAvailable') > 0 - % get the sample in the form of an event structure - evt = Eyelink( 'NewestFloatSample'); - if eye_used ~= -1 % do we know which eye to use yet? - % if we do, get current gaze position from sample - x = evt.gx(eye_used+1); % +1 as we're accessing MATLAB array - y = evt.gy(eye_used+1); - % do we have valid data and is the pupil visible? - if x~=el.MISSING_DATA && y~=el.MISSING_DATA && evt.pa(eye_used+1)>0 - % if data is valid, draw a circle on the screen at current gaze position - % using PsychToolbox's Screen function - gazeRect=[ x-9 y-9 x+10 y+10]; - colour=round(rand(3,1)*255); % coloured dot - Screen('FillOval', window, colour, gazeRect); - Screen('Flip', el.window, [], 1); % don't erase - else - % if data is invalid (e.g. during a blink), clear display - Screen('FillRect', window, el.backgroundcolour); - Screen('DrawText', window, message, 200, height-el.msgfontsize-20, el.msgfontcolour); - Screen('Flip', el.window, [], 1); % don't erase - end - else % if we don't, first find eye that's being tracked - eye_used = Eyelink('EyeAvailable'); % get eye that's tracked - if eye_used == el.BINOCULAR; % if both eyes are tracked - eye_used = el.LEFT_EYE; % use left eye - end - end - end % if sample available - end % main loop - % wait a while to record a few more samples - WaitSecs(0.1); - - % STEP 7 - % finish up: stop recording eye-movements, - % close graphics window, close data file and shut down tracker - Eyelink('StopRecording'); - Eyelink('CloseFile'); - % download data file - try - fprintf('Receiving data file ''%s''\n', edfFile ); - status=Eyelink('ReceiveFile'); - if status > 0 - fprintf('ReceiveFile status %d\n', status); - end - if 2==exist(edfFile, 'file') - fprintf('Data file ''%s'' can be found in ''%s''\n', edfFile, pwd ); - end - catch rdf - fprintf('Problem receiving data file ''%s''\n', edfFile ); - rdf; - end - - cleanup; - -catch - %this "catch" section executes in case of an error in the "try" section - %above. Importantly, it closes the onscreen window if its open. - cleanup; - psychrethrow(psychlasterror); -end %try..catch. - - -% Cleanup routine: -function cleanup - % Shutdown Eyelink: - Eyelink('Shutdown'); - - % Close window: - sca; - - % Restore keyboard output to Matlab: - ListenChar(0); diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkImageExample.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkImageExample.m deleted file mode 100644 index e5c20e1565..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkImageExample.m +++ /dev/null @@ -1,115 +0,0 @@ -function result=EyelinkImageExample - -% Short MATLAB example program that uses the Eyelink and Psychophysics -% Toolboxes to measure viewing behaviour for an image. -% -% History -% 15-06-10 fwc created it, based on EyelinkExample.m -try - - fprintf('EyelinkToolbox Image View Example\n\n\t'); - - dummymode=0; % set to 1 to initialize in dummymode - - % STEP 1 - % Open a graphics window on the main screen - screenNumber=max(Screen('Screens')); - [window, wRect]=Screen('OpenWindow', screenNumber); - - % STEP 2 - % Provide Eyelink with details about the graphics environment - % and perform some initializations. The information is returned - % in a structure that also contains useful defaults - % and control codes (e.g. tracker state bit and Eyelink key values). - el=EyelinkInitDefaults(window); - - % STEP 3 - % Initialization of the connection with the Eyelink Gazetracker. - % exit program if this fails. - if ~EyelinkInit(dummymode) - fprintf('Eyelink Init aborted.\n'); - cleanup; % cleanup function - return; - end - - [v vs]=Eyelink('GetTrackerVersion'); - fprintf('Running experiment on a ''%s'' tracker.\n', vs ); - - % open file for recording data - edfFile='demo.edf'; - Eyelink('Openfile', edfFile); - - % STEP 4 - % Do setup and calibrate the eye tracker - EyelinkDoTrackerSetup(el); - - % do a final check of calibration using driftcorrection - % You have to hit esc before return. - EyelinkDoDriftCorrection(el); - - % STEP 5 - % Start recording eye position - Eyelink('StartRecording'); - % record a few samples before we actually start displaying - WaitSecs(0.5); - - % STEP 6 - % Show image on display - basepath = [ PsychtoolboxRoot 'PsychDemos' filesep ]; - myimg = [basepath 'konijntjes1024x768.jpg']; - - imdata=imread(myimg); - imtex=Screen('MakeTexture', el.window, imdata); - - Screen('FillRect', el.window, el.backgroundcolour); - Screen('DrawTexture', el.window, imtex, [], wRect); % fill screen with image - - % Some useful info for user about what's happening. - [width, height]=Screen('WindowSize', el.window); - - Screen('TextFont', el.window, el.msgfont); - Screen('TextSize', el.window, el.msgfontsize); - Screen('DrawText', el.window, 'Just look around for a while.', 200, height-el.msgfontsize-20, el.msgfontcolour); - - % Show result on screen: - Screen('Flip', el.window) - - % mark zero-plot time in data file - Eyelink('Message', 'SYNCTIME'); - % wait a while to record a bunch of samples - WaitSecs(3); - - % STEP 7 remove image - Screen('FillRect', el.window, el.backgroundcolour); - Screen('DrawText', el.window, 'Done', width/2, height/2, el.msgfontcolour); - Screen('Flip', el.window); - % mark image removal time in data file - Eyelink('Message', 'ENDTIME'); - WaitSecs(0.5); - Eyelink('Message', 'TRIAL_END'); - - % STEP 8 - % finish up: stop recording eye-movements, - % close graphics window, close data file and shut down tracker - Eyelink('StopRecording'); - Eyelink('CloseFile'); - cleanup; - -catch - %this "catch" section executes in case of an error in the "try" section - %above. Importantly, it closes the onscreen window if it's open. - cleanup; - psychrethrow(psychlasterror); -end %try..catch. - - -% Cleanup routine: -function cleanup - % Shutdown Eyelink: - Eyelink('Shutdown'); - - % Close window: - sca; - commandwindow; - % Restore keyboard output to Matlab: - ListenChar(0); diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkQueuedDataDemo.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkQueuedDataDemo.m deleted file mode 100644 index 18310080f4..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkQueuedDataDemo.m +++ /dev/null @@ -1,223 +0,0 @@ -function EyelinkQueuedDataDemo -% Demos use of Eyelink('GetQueuedData') -% records for a while, then plots samples, events, and execution -% times of GetQueuedData and some non-preallocated storage including -% data-conversion to take less space - -% History: -% ??/??/?? Written by Erik Flister. -% 20/04/15 Removed intwarning() - no longer suppored by Matlab (MK). - -format long g -clear -clear classes -close all -clc - -KbName('UnifyKeyNames') - -if (Eyelink('initialize') ~= 0) - error('could not init connection to Eyelink') - return; -end; - -try - Eyelink('Verbosity', 0); - oldPriority = Priority(MaxPriority('KbCheck')); - - el=EyelinkInitDefaults(); - el.LOSTDATAEVENT=hex2dec('3F'); - - eyelink('command', 'active_eye = RIGHT'); - - % configure eyelink to send raw data - status=Eyelink('command','link_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,HREF,PUPIL,STATUS,INPUT,HMARKER'); - if status~=0 - status - error('link_sample_data error') - end - - status=Eyelink('command','file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,HREF,PUPIL,STATUS,INPUT,HMARKER'); - if status~=0 - status - error('file_sample_data error') - end - - status=Eyelink('command','inputword_is_window = ON'); - if status~=0 - status - error('inputword_is_window error') - end - - edfFile='demo.edf'; - status=Eyelink('openfile',edfFile); - if status~=0 - status - error('openfile error') - end - - [version, versionString] = Eyelink('GetTrackerVersion') - - status=Eyelink('startrecording'); - if status~=0 - status - error('startrecording error') - end - - WaitSecs(0.1); - status=Eyelink('message','SYNCTIME'); - if status~=0 - status - error('message error') - end - - screenFlipSimulationTime=0; - maxEvents=100; - maxSamples=2000*10*60; - - samples=nan(48,maxSamples); - events=nan(30,maxEvents); - sampleNum=1; - eventNum=1; - - times=[]; - convertTimes=[]; - unsafeConvertTimes=[]; - smalls={}; - - fprintf('hit spacebar to stop\n') - - stopkey=KbName('space'); - - eye_used = Eyelink('EyeAvailable'); - - switch eye_used - case el.BINOCULAR - error('tracker indicates binocular') - case el.LEFT_EYE - error('tracker indicates left eye') - case el.RIGHT_EYE - disp('tracker indicates right eye') - case -1 - error('eyeavailable returned -1') - otherwise - eye_used - error('unexpected result from eyeavailable') - end - - recordingStartTime=GetSecs(); - - while true - err=Eyelink('checkrecording'); - if(err~=0) - err - error('checkrecording problem') - end - - drained = false; - while ~drained - start=GetSecs; - [samplesIn, eventsIn, drained] = Eyelink('GetQueuedData', eye_used); - times(end+1)=GetSecs-start; - - if ~isempty(samplesIn) - samples(:,sampleNum:sampleNum+size(samplesIn,2)-1)=samplesIn; - sampleNum=sampleNum+size(samplesIn,2); - - lost=sum(samplesIn(2,:)==el.LOSTDATAEVENT); - if lost - fprintf('got %d losts!\n',lost) - end - - start=GetSecs; - smalls{1,end+1}=samplesIn(2,:)==el.LOSTDATAEVENT; - smalls{2,end}=uint16(samplesIn(20,:)); - smalls{3,end}=uint32(samplesIn([36:43 46],:)); - convertTimes(end+1)=GetSecs-start; - - start=GetSecs; - smalls{1,end+1}=samplesIn(2,:)==el.LOSTDATAEVENT; - smalls{2,end}=uint16(samplesIn(20,:)); - smalls{3,end}=uint32(samplesIn([36:43 46],:)); - unsafeConvertTimes(end+1)=GetSecs-start; - end - if ~isempty(eventsIn) - events(:,eventNum:eventNum+size(eventsIn,2)-1)=eventsIn; - eventNum=eventNum+size(eventsIn,2); - end - - if false && (size(samples,2)>0 || size(events,2)>0) - fprintf('got %d samples, %d events, and drained is %d\n',size(samples,2),size(events,2),drained) - end - - if ~drained - fprintf('got a drain failure!\n') - end - end - - if sampleNum>=maxSamples || eventNum>=maxEvents - fprintf('stopping cuz filled sample or event allocation\n') - break; - end - - [keyIsDown,secs,keyCode] = KbCheck; - if keyCode(stopkey) - fprintf('stopping cuz got stopkey') - break; - end - - if screenFlipSimulationTime - WaitSecs(screenFlipSimulationTime); - end - end - - figure - subplot(3,1,1) - plot(samples') - title('samples') - xlabel('sampleNum') - - subplot(3,1,2) - plot(events') - title('events') - xlabel('eventNum') - - subplot(3,1,3) - plot(times,'b'); - hold on - plot(convertTimes,'r') - plot(unsafeConvertTimes,'g') - title('times') - legend({'GetQueuedData','type conversions','unsafe type conversions'}) - xlabel('call num') - - WaitSecs(0.1); - cleanup; -catch - cleanup; -end - -function cleanup - chk=Eyelink('checkrecording'); - if chk~=0 - disp('problem: wasn''t recording but should have been') - end - Eyelink('stoprecording'); - ShowCursor; - Priority(oldPriority); - status=Eyelink('closefile'); - if status ~=0 - disp(sprintf('closefile error, status: %d',status)) - end - status=Eyelink('ReceiveFile',edfFile,pwd,1); - if status~=0 - fprintf('problem: ReceiveFile status: %d\n', status); - end - if 2==exist(edfFile, 'file') - fprintf('Data file ''%s'' can be found in ''%s''\n', edfFile, pwd ); - else - disp('unknown where data file went') - end - Eyelink('shutdown'); - end -end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/GazeContingentDemos/EyelinkBubbleDemo.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/GazeContingentDemos/EyelinkBubbleDemo.m deleted file mode 100644 index 3443a9c3c6..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/GazeContingentDemos/EyelinkBubbleDemo.m +++ /dev/null @@ -1,375 +0,0 @@ -function EyelinkBubbleDemo(mode, ms, myimgfile) -% -% Demo implementation of a generic bubble display. -% We take one input image and create - via image processing - two images -% out of it: An image to show at the screen location were the subject -% fixates (According to the eye-tracker). A second image to show in the -% peripery of the subjects field of view. These two images are blended into -% each other via a gaussian weight mask (an aperture). The mask is centered -% at the center of gaze and allows for a smooth transition between the two -% images. -% -% This illustrates an application of OpenGL Alpha blending by compositing -% two images based on a spatial gaussian weight mask. Compositing is done -% by the graphics hardware. -% -% _________________________________________________________________________ -% -% see also: PsychDemosOSX, MovieDemoOSX, DriftDemo - -% HISTORY -% -% mm/dd/yy -% -% 7/23/05 mk Derived it from Frans Cornelissens AlphaImageDemoOSX. -% 22/06/06 fwc eyelinked, derived from Mario Kleiner's BubbleDemo ;-) - -PsychDefaultSetup(1); - -% Set hurryup = 1 for benchmarking - Syncing to retrace is disabled -% in that case so we'll get the maximum refresh rate. -hurryup=0; - -% Setup default mode to color vs. gray. -if nargin < 1 - mode = 1; -end; - -% Setup default aperture size to 2*200 x 2*200 pixels. -if nargin < 2 - ms=300; -end; - -basepath = [ PsychtoolboxRoot 'PsychDemos' filesep ]; - -% Use default demo images, if no special image was provided. -if nargin < 3 - myimgfile= [basepath 'konijntjes1024x768.jpg']; -end; - -myblurimgfile= [basepath 'konijntjes1024x768blur.jpg']; -mygrayimgfile= [basepath 'konijntjes1024x768gray.jpg']; - -if 1 - Screen('Preference', 'SkipSyncTests', 1); -end - -commandwindow; - -try - fprintf('BubbleDemo (%s)\n', datestr(now)); - fprintf('Press a key or click on mouse to stop demo.\n'); - - if (Eyelink('Initialize') ~= 0) - return; - fprintf('Problem initializing eyelink\n'); - end; - - % This script calls Psychtoolbox commands available only in OpenGL-based - % versions of the Psychtoolbox. (So far, the OS X Psychtoolbox is the - % only OpenGL-based Psychtoolbox.) The Psychtoolbox command AssertOpenGL will issue - % an error message if someone tries to execute this script on a computer without - % an OpenGL Psychtoolbox - AssertOpenGL; - - % Get the list of screens and choose the one with the highest screen number. - % Screen 0 is, by definition, the display with the menu bar. Often when - % two monitors are connected the one without the menu bar is used as - % the stimulus display. Chosing the display with the highest display number is - % a best guess about where you want the stimulus displayed. - screenNumber=max(Screen('Screens')); - - % Open a double buffered fullscreen window. - [w, wRect]=Screen('OpenWindow',screenNumber, 0,[],32,2); - - - % Find the color values which correspond to white and black. Though on OS - % X we currently only support true color and thus, for scalar color - % arguments, - % black is always 0 and white 255, this rule is not true on other platforms will - % not remain true on OS X after we add other color depth modes. - white=WhiteIndex(screenNumber); - black=BlackIndex(screenNumber); - gray=GrayIndex(screenNumber); % returns as default the mean gray value of screen - - % Set background color to gray: - backgroundcolor = gray; - - % Load image file: - fprintf('Using image ''%s''\n', myimgfile); - imdata=imread(myimgfile); - imdatablur=imread(myblurimgfile); - imdatagray=imread(mygrayimgfile); - - % crop image if it is larger then screen size. There's no image scaling - % in maketexture - [iy, ix, id]=size(imdata); - [wW, wH]=WindowSize(w); - if ix>wW || iy>wH - disp('Image size exceeds screen size'); - disp('Image will be cropped'); - end - - if ix>wW - cl=round((ix-wW)/2); - cr=(ix-wW)-cl; - else - cl=0; - cr=0; - end - if iy>wH - ct=round((iy-wH)/2); - cb=(iy-wH)-ct; - else - ct=0; - cb=0; - end - - % imdata is the cropped version of the image. - imdata=imdata(1+ct:iy-cb, 1+cl:ix-cr,:); - imdatablur=imdatablur(1+ct:iy-cb, 1+cl:ix-cr,:); - imdatagray=imdatagray(1+ct:iy-cb, 1+cl:ix-cr,:); - - % Compute image for foveated region and periphery: - switch (mode) - case 1 - % Mode 1: - % Fovea contains original image data: - foveaimdata = imdata; - % Periphery contains grayscale-version: - peripheryimdata = imdatagray; - case 2 - % Fovea contains original image data: - foveaimdata = imdata; - % Periphery contains blurred-version: - peripheryimdata = imdatablur; - case 3 - % Fovea contains color-inverted image data: - foveaimdata(:,:,:) = 255 - imdata(:,:,:); - % Periphery contains original data: - peripheryimdata = imdata; - case 4 - % Test-case: One shouldn't see any foveated region on the - % screen - this is a basic correctness test for blending. - foveaimdata = imdata; - peripheryimdata = imdata; - otherwise - % Unknown mode! We force abortion: - fprintf('Invalid mode provided!'); - abortthisbeast - end; - - % Build texture for foveated region: - foveatex=Screen('MakeTexture', w, foveaimdata); - tRect=Screen('Rect', foveatex); - - % Build texture for peripheral (non-foveated) region: - nonfoveatex=Screen('MakeTexture', w, peripheryimdata); - [ctRect, dx, dy]=CenterRect(tRect, wRect); - - % We create a Luminance+Alpha matrix for use as transparency mask: - % Layer 1 (Luminance) is filled with 'backgroundcolor'. - transLayer=2; - [x,y]=meshgrid(-ms:ms, -ms:ms); - maskblob=ones(2*ms+1, 2*ms+1, transLayer) * 0; - % Layer 2 (Transparency aka Alpha) is filled with gaussian transparency - % mask. - xsd=ms/3.2; - ysd=ms/3.2; - maskblob(:,:,transLayer)=round(exp(-((x/xsd).^2)-((y/ysd).^2))*255); - - % Build a single transparency mask texture: - masktex=Screen('MakeTexture', w, maskblob); - mRect=Screen('Rect', masktex); - - fprintf('Size image texture: %d x %d\n', RectWidth(tRect), RectHeight(tRect)); - fprintf('Size mask texture: %d x %d\n', RectWidth(mRect), RectHeight(mRect)); - - - - - el=EyelinkInitDefaults(w); - - % make sure that we get gaze data from the Eyelink - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,AREA'); - - % open file to record data to - Eyelink('openfile', 'demo.edf'); - - % STEP 4 - % Calibrate the eye tracker - EyelinkDoTrackerSetup(el); - - % do a final check of calibration using driftcorrection - EyelinkDoDriftCorrection(el); - - WaitSecs(0.1); - Eyelink('StartRecording'); - - eye_used = Eyelink('EyeAvailable'); % get eye that's tracked - if eye_used == el.BINOCULAR; % if both eyes are tracked - eye_used = el.LEFT_EYE; % use left eye - end - - % Set background color to 'backgroundcolor' and do initial flip to show - % blank screen: - Screen('FillRect', w, backgroundcolor); - Screen('Flip', w); - - % The mouse-cursor position will define gaze-position (center of - % fixation) to simulate (x,y) input from an eyetracker. Set cursor - % initially to center of screen: - [a,b]=WindowCenter(w); - WaitSetMouse(a,b,0); % set cursor and wait for it to take effect - - HideCursor; - buttons=0; - - priorityLevel=MaxPriority(w); - Priority(priorityLevel); - - % Wait until all keys on keyboard are released: - while KbCheck; WaitSecs(0.1); end; - - mxold=0; - myold=0; - - Screen('BlendFunction', w, GL_ONE, GL_ZERO); - Screen('FillRect', w, [128 128 128 0]); - - oldvbl=Screen('Flip', w); - tavg = 0; - ncount = 0; - - % Infinite display loop: Whenever "gaze position" changes, we update - % the display accordingly. Loop aborts on keyboard press or mouse - % click. - while (ncount < 10000) - % We wait 1 ms each loop-iteration so that we - % don't overload the system in realtime-priority: - WaitSecs(0.001); - - error=Eyelink('CheckRecording'); - if(error~=0) - break; - end - - % Query eyetracker") - - % (mx,my) is our gaze position. - - - if Eyelink( 'NewFloatSampleAvailable') > 0 - % get the sample in the form of an event structure - evt = Eyelink( 'NewestFloatSample'); - if eye_used ~= -1 % do we know which eye to use yet? - % if we do, get current gaze position from sample - x = evt.gx(eye_used+1); % +1 as we're accessing MATLAB array - y = evt.gy(eye_used+1); - % do we have valid data and is the pupil visible? - if x~=el.MISSING_DATA && y~=el.MISSING_DATA && evt.pa(eye_used+1)>0 - - mx=x; - my=y; - end - end - end - - -% if (hurryup==0) -% [mx, my, buttons]=GetMouse(0); -% else -% % mx=500 + 500*sin(GetSecs()); my=300; -% mx=500 + 500*sin(ncount/10); my=300; -% end; - - % We only redraw if gazepos. has changed: - if (mx~=mxold || my~=myold) - % Compute position and size of source- and destinationrect and - % clip it, if necessary... - myrect=[mx-ms my-ms mx+ms+1 my+ms+1]; % center dRect on current mouseposition - dRect = ClipRect(myrect,ctRect); - sRect=OffsetRect(dRect, -dx, -dy); - - % Valid destination rectangle? - if ~IsEmptyRect(dRect) - % Yes! Draw image for current frame: - - % Step 1: Draw the alpha-mask into the backbuffer. It - % defines the aperture for foveation: The center of gaze - % has zero alpha value. Alpha values increase with distance from - % center of gaze according to a gaussian function and - % approach 255 at the border of the aperture... - Screen('BlendFunction', w, GL_ONE, GL_ONE); - Screen('DrawTexture', w, masktex, [], myrect, 0, 0); -% % Screen('DrawTexture', w, masktex, [], OffsetRect(myrect, ms, 0), 0, 0); - - % Step 2: Draw peripheral image. It is only drawn where - % the alpha-value in the backbuffer is 255 or high, leaving - % the foveated area (low or zero alpha values) alone: - % This is done by weighting each color value of each pixel - % with the corresponding alpha-value in the backbuffer - % (GL_DST_ALPHA). - Screen('BlendFunction', w, GL_DST_ALPHA, GL_ZERO); - Screen('DrawTexture', w, foveatex, [], ctRect, 0); - - % Step 3: Draw foveated image, but only where the - % alpha-value in the backbuffer is zero or low: This is - % done by weighting each color value with one minus the - % corresponding alpha-value in the backbuffer - % (GL_ONE_MINUS_DST_ALPHA). - Screen('BlendFunction', w, GL_ONE_MINUS_DST_ALPHA, GL_ONE); - Screen('DrawTexture', w, nonfoveatex, [], ctRect, 0, 0); - %Screen('FillRect', w, [128 128 128], ctRect); - % Show final result on screen. This also clears the drawing - % surface back to black background color and a zero alpha - % value. - % Actually... We use clearmode=2: This doesn't clear the - % backbuffer, but we don't need to clear it for this kind - % of stimulus and it gives us 2 msecs extra headroom for - % higher refresh rates! For benchmark purpose, we' - vbl = Screen('Flip', w, 0, 0, 2*hurryup); - vbl = GetSecs; - tavg = tavg + (vbl-oldvbl); - oldvbl=vbl; - ncount = ncount + 1; - end; - end; - - % Keep track of last gaze position: - mxold=mx; - myold=my; - - % Abort demo on keypress our mouse-click: - if KbCheck || find(buttons) % break out of loop - break; - end; - end; - - % Display full image a last time, just for fun... - Screen('BlendFunction', w, GL_ONE, GL_ZERO); - Screen('DrawTexture', w, foveatex); - Screen('Flip', w); - - - Eyelink('StopRecording'); - Eyelink('ShutDown'); - - - % The same command which closes onscreen and offscreen windows also - % closes textures. - - - Screen('CloseAll'); - ShowCursor; - Priority(0); - tavg = tavg / ncount * 1000; - fprintf('End of BubbleDemo. Avg. redraw time is %f ms = %f Hz.\n\n', tavg, 1000 / tavg); - -catch - %this "catch" section executes in case of an error in the "try" section - %above. Importantly, it closes the onscreen window if its open. - Screen('CloseAll'); - ShowCursor; - Priority(0); - rethrow(lasterr); -end %try..catch.. diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/GazeContingentDemos/EyelinkGazeContingentDemo.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/GazeContingentDemos/EyelinkGazeContingentDemo.m deleted file mode 100644 index 3027cc8b00..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/GazeContingentDemos/EyelinkGazeContingentDemo.m +++ /dev/null @@ -1,398 +0,0 @@ -function EyelinkGazeContingentDemo(mode) -% -% ___________________________________________________________________ -% -% Demo implementation of a generic gaze-contingent display. -% We take one input image and create - via image processing - two images -% out of it: An image to show at the screen location were the subject -% fixates (According to the eye-tracker). A second image to show in the -% peripery of the subjects field of view. These two images are blended into -% each other via a gaussian weight mask (an aperture). The mask is centered -% at the center of gaze and allows for a smooth transition between the two -% images. -% -% This illustrates an application of OpenGL Alpha blending by compositing -% two images based on a spatial gaussian weight mask. Compositing is done -% by the graphics hardware. -% Mode can have the following values: -% Mode 1: -% Fovea contains original image data: -% Periphery contains grayscale-version: -% Mode 2: -% Fovea contains original image data: -% Periphery contains blurred-version: -% Mode 3 -% Fovea contains color-inverted image data: -% Periphery contains original data: -% Mode 4 -% Test-case: One shouldn't see any foveated region on the -% screen - this is a basic correctness test for blending. -% -% _________________________________________________________________________ -% -% see also: EyelinkExample, EyelinkPicture - -% HISTORY -% -% mm/dd/yy -% -% 7/23/05 mk Derived it from Frans Cornelissen's AlphaImageDemoOSX. -% 29/06/06 fwc Derived from Mario Kleiner's GazeContingentDemoOSX ;-) -% 07/13/10 fwc made to work with new toolbox with callback and updated to -% enable eye image display, added "cleanup" function, -% - -PsychDefaultSetup(1); - -dummymode=0; -try - - fprintf('%s (%s)\n', mfilename, datestr(now)); - fprintf('Press space or click on mouse to stop demo.\n'); - - stopkey=KbName('space'); - - if 1; Screen('Preference', 'SkipSyncTests', 1); end; - - % Set hurryup = 1 for benchmarking - Syncing to retrace is disabled - % in that case so we'll get the maximum refresh rate. - hurryup=0; - - % Setup default mode to color vs. gray. - if nargin < 1 - mode = 1; - end; - - % Setup default aperture size to 2*200+1 x 2*200+1 pixels. - ms=200; - - % Set default demo images. - basepath = [ PsychtoolboxRoot 'PsychDemos' filesep ]; - myimgfile= [basepath 'konijntjes1024x768.jpg']; - myblurimgfile= [basepath 'konijntjes1024x768blur.jpg']; - mygrayimgfile= [basepath 'konijntjes1024x768gray.jpg']; - - % Get the list of screens and choose the one with the highest screen number. - % Screen 0 is, by definition, the display with the menu bar. Often when - % two monitors are connected the one without the menu bar is used as - % the stimulus display. Chosing the display with the highest display number is - % a best guess about where you want the stimulus displayed. - screenNumber=max(Screen('Screens')); - - % Open a fullscreen window. - [w, wRect]=Screen('OpenWindow',screenNumber); - - % Set background color to gray. - backgroundcolor=GrayIndex(w); % returns as default the mean gray value of screen - - % Provide Eyelink with details about the graphics environment - % and perform some initializations. The information is returned - % in a structure that also contains useful defaults - % and control codes (e.g. tracker state bit and Eyelink key values). - el=EyelinkInitDefaults(w); - - % Initialization of the connection with the Eyelink Gazetracker. - % exit program if this fails. - if ~EyelinkInit(dummymode) - fprintf('Eyelink Init aborted.\n'); - cleanup; % cleanup function - return; - end - - % Load image file: - fprintf('Using image ''%s''\n', myimgfile); - imdata=imread(myimgfile); - imdatablur=imread(myblurimgfile); - imdatagray=imread(mygrayimgfile); - - % crop image if it is larger then screen size. There's no image scaling - % in maketexture (Note: no longer true as Sceen's Drawtexture does scaling) - [iy, ix, id]=size(imdata); - [wW, wH]=WindowSize(w); - if ix>wW || iy>wH - disp('Image size exceeds screen size'); - disp('Image will be cropped'); - end - - if ix>wW - cl=round((ix-wW)/2); - cr=(ix-wW)-cl; - else - cl=0; - cr=0; - end - if iy>wH - ct=round((iy-wH)/2); - cb=(iy-wH)-ct; - else - ct=0; - cb=0; - end - - % imdata is the cropped version of the image. - imdata=imdata(1+ct:iy-cb, 1+cl:ix-cr,:); - imdatablur=imdatablur(1+ct:iy-cb, 1+cl:ix-cr,:); - imdatagray=imdatagray(1+ct:iy-cb, 1+cl:ix-cr,:); - - % Compute image for foveated region and periphery: - switch (mode) - case 1 - % Mode 1: - % Fovea contains original image data: - foveaimdata = imdata; - % Periphery contains grayscale-version: - peripheryimdata = imdatagray; - case 2 - % Fovea contains original image data: - foveaimdata = imdata; - % Periphery contains blurred-version: - peripheryimdata = imdatablur; - case 3 - % Fovea contains color-inverted image data: - foveaimdata(:,:,:) = 255 - imdata(:,:,:); - % Periphery contains original data: - peripheryimdata = imdata; - case 4 - % Test-case: One shouldn't see any foveated region on the - % screen - this is a basic correctness test for blending. - foveaimdata = imdata; - peripheryimdata = imdata; - otherwise - % Unknown mode! We force abortion: - fprintf('Invalid mode provided!'); - abortthisbeast - end; - - % Build texture for foveated region: - foveatex=Screen('MakeTexture', w, foveaimdata); - tRect=Screen('Rect', foveatex); - - % Build texture for peripheral (non-foveated) region: - nonfoveatex=Screen('MakeTexture', w, peripheryimdata); - [ctRect, dx, dy]=CenterRect(tRect, wRect); - - % We create a Luminance+Alpha matrix for use as transparency mask: - % Layer 1 (Luminance) is filled with 'backgroundcolor'. - transLayer=2; - [x,y]=meshgrid(-ms:ms, -ms:ms); - maskblob=ones(2*ms+1, 2*ms+1, transLayer) * backgroundcolor; - % Layer 2 (Transparency aka Alpha) is filled with gaussian transparency - % mask. - xsd=ms/2.2; - ysd=ms/2.2; - maskblob(:,:,transLayer)=round(255 - exp(-((x/xsd).^2)-((y/ysd).^2))*255); - - % Build a single transparency mask texture: - masktex=Screen('MakeTexture', w, maskblob); - mRect=Screen('Rect', masktex); - - fprintf('Size image texture: %d x %d\n', RectWidth(tRect), RectHeight(tRect)); - fprintf('Size mask texture: %d x %d\n', RectWidth(mRect), RectHeight(mRect)); - - - % Set background color to 'backgroundcolor' and do initial flip to show - % blank screen: - Screen('FillRect', w, backgroundcolor); - Screen('Flip', w); - - % make sure that we get gaze data from the Eyelink - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,AREA'); - - % open file to record data to - Eyelink('openfile', 'demo.edf'); - - % STEP 4 - % Calibrate the eye tracker - EyelinkDoTrackerSetup(el); - - % do a final check of calibration using driftcorrection - EyelinkDoDriftCorrection(el); - - WaitSecs(0.1); - Eyelink('StartRecording'); - - eye_used = Eyelink('EyeAvailable'); % get eye that's tracked - if eye_used == el.BINOCULAR; % if both eyes are tracked - eye_used = el.LEFT_EYE; % use left eye - end - - % Set background color to 'backgroundcolor' and do flip to show - % blank screen: - Screen('FillRect', w, backgroundcolor); - Screen('Flip', w); - - % The mouse-cursor position will define gaze-position (center of - % fixation) to simulate (x,y) input from an eyetracker. Set cursor - % initially to center of screen: - [a,b]=RectCenter(wRect); - WaitSetMouse(a,b,screenNumber); % set cursor and wait for it to take effect - - HideCursor; - buttons=0; - - priorityLevel=MaxPriority(w); - Priority(priorityLevel); - - % Wait until all keys on keyboard are released: - while KbCheck; WaitSecs(0.1); end; - - mxold=0; - myold=0; - - oldvbl=Screen('Flip', w); - tavg = 0; - ncount = 0; - - % Infinite display loop: Whenever "gaze position" changes, we update - % the display accordingly. Loop aborts on keyboard press or mouse - % click or after 10000 frames... - while (ncount < 10000) - - - if dummymode==0 % - error=Eyelink('CheckRecording'); - if(error~=0) - break; - end - - if Eyelink( 'NewFloatSampleAvailable') > 0 - % get the sample in the form of an event structure - evt = Eyelink( 'NewestFloatSample'); - if eye_used ~= -1 % do we know which eye to use yet? - % if we do, get current gaze position from sample - x = evt.gx(eye_used+1); % +1 as we're accessing MATLAB array - y = evt.gy(eye_used+1); - % do we have valid data and is the pupil visible? - if x~=el.MISSING_DATA && y~=el.MISSING_DATA && evt.pa(eye_used+1)>0 - mx=x; - my=y; - end - end - end - else - - % Query current mouse cursor position (our "pseudo-eyetracker") - - % (mx,my) is our gaze position. - if (hurryup==0) - [mx, my, buttons]=GetMouse; %(w); - else - % In benchmark mode, we just do a quick sinusoidal motion - % without query of the mouse: - mx=500 + 500*sin(ncount/10); my=300; - end; - end - % We only redraw if gazepos. has changed: - if (mx~=mxold || my~=myold) - % Compute position and size of source- and destinationrect and - % clip it, if necessary... - myrect=[mx-ms my-ms mx+ms+1 my+ms+1]; % center dRect on current mouseposition - dRect = ClipRect(myrect,ctRect); - sRect=OffsetRect(dRect, -dx, -dy); - - % Valid destination rectangle? - if ~IsEmptyRect(dRect) - % Yes! Draw image for current frame: - - % Step 1: Draw the alpha-mask into the backbuffer. It - % defines the aperture for foveation: The center of gaze - % has zero alpha value. Alpha values increase with distance from - % center of gaze according to a gaussian function and - % approach 255 at the border of the aperture... - Screen('BlendFunction', w, GL_ONE, GL_ZERO); - Screen('DrawTexture', w, masktex, [], myrect); - - % Step 2: Draw peripheral image. It is only drawn where - % the alpha-value in the backbuffer is 255 or high, leaving - % the foveated area (low or zero alpha values) alone: - % This is done by weighting each color value of each pixel - % with the corresponding alpha-value in the backbuffer - % (GL_DST_ALPHA). - Screen('BlendFunction', w, GL_DST_ALPHA, GL_ZERO); - Screen('DrawTexture', w, nonfoveatex, [], ctRect); - - % Step 3: Draw foveated image, but only where the - % alpha-value in the backbuffer is zero or low: This is - % done by weighting each color value with one minus the - % corresponding alpha-value in the backbuffer - % (GL_ONE_MINUS_DST_ALPHA). - Screen('BlendFunction', w, GL_ONE_MINUS_DST_ALPHA, GL_ONE); - Screen('DrawTexture', w, foveatex, sRect, dRect); - - % Show final result on screen. This also clears the drawing - % surface back to black background color and a zero alpha - % value. - % Actually... We use clearmode=2: This doesn't clear the - % backbuffer, but we don't need to clear it for this kind - % of stimulus and it gives us 2 msecs extra headroom for - % higher refresh rates! For benchmark purpose, we disable - % syncing to retrace if hurryup is == 1. - vbl = Screen('Flip', w, 0, 2, 2*hurryup); - vbl = GetSecs; - tavg = tavg + (vbl-oldvbl); - oldvbl=vbl; - ncount = ncount + 1; - end; - end; - - % Keep track of last gaze position: - mxold=mx; - myold=my; - - % We wait 1 ms each loop-iteration so that we - % don't overload the system in realtime-priority: - WaitSecs(0.001); - - % Abort demo on keypress our mouse-click: - [mx, my, buttons]=GetMouse; %(w); - if any(buttons) % break out of loop - break; - end - % check for keyboard press - [keyIsDown,secs,keyCode] = KbCheck; - % if spacebar was pressed stop display - if keyCode(stopkey) - Eyelink('Message', 'Key pressed') - break; - end - - - end - - % stop eyelink - Eyelink('StopRecording'); - - - - % Display full image a last time, just for fun... - Screen('BlendFunction', w, GL_ONE, GL_ZERO); - Screen('DrawTexture', w, foveatex); - Screen('Flip', w); - WaitSecs(1); - - cleanup; - tavg = tavg / ncount * 1000; - fprintf('End of %s. Avg. redraw time is %f ms = %f Hz.\n\n', mfilename, tavg, 1000 / tavg); - return; -catch lasterror - %this "catch" section executes in case of an error in the "try" section - %above. Importantly, it closes the onscreen window if its open. - cleanup; - fprintf('%s: some error occured\n', mfilename); - psychrethrow(lasterror); -end %try..catch.. - - -% Cleanup routine: -function cleanup -% Shutdown Eyelink: -Eyelink('Shutdown'); - -% Close window: -sca; -Priority(0); - -commandwindow; - -% Restore keyboard output to Matlab: -% % ListenChar(0); - diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/AntiSaccade.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/AntiSaccade.m deleted file mode 100644 index 2077975f90..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/AntiSaccade.m +++ /dev/null @@ -1,493 +0,0 @@ -function AntiSaccade(placeHolderFlag,gapManipulation) -% -% ___________________________________________________________________ -% -% AntiSaccade(placeHolderFlag,gapManipulation) -% placeHolderFlag = display place holders. Default 1 -% gapManipulation = time gap. Default 1 -% -% Demo implemetation of prosaccade and antisaccade task. -% -% stimulus: place holders, random duration of the initial fixation, and gap manipulation before the target presentation. -% -% This task demonstrates stimuli presentation with Eyelink and Data Viewer -% integration. -% -% ___________________________________________________________________ - -% HISTORY -% mm/dd/yy -% -% 01/28/11 NJ created -% 12/20/13 LJ changed isoctave to IsOctave, case sensitive for the latest matlab - -PsychDefaultSetup(1); - -if nargin < 1 - placeHolderFlag = true; -end -if nargin < 2 - gapManipulation = true; -end - -dummymode = 0; -TARGET_TIMER = 1250; -GAP_TIMER = 200; - -task = zeros(4, 32); - -task(1:16,1) = 1; % prosccade -task(1:8,2) = 1; % left -task(1:4,3) = 5; % amplitude -task(5:8,3) = 10; -task(9:16,2) = 2; % Right -task(9:12,3) = 5; % amplitude -task(13:16,3) = 10; - -task(17:32,1) = 2; % antisaccade -task(17:24,2) = 1; % left -task(17:20,3) = 5; % amplitude -task(21:24,3) = 10; -task(25:32,2) = 2; % Right -task(25:28,3) = 5; % amplitude -task(29:32,3) = 10; - -saccade = ''; -feedbackpos = ''; - -instructionText = [' In this experiment, you are going to see a series of events happening in each trial.\n ' ... - ' A colored fixation box will be presented at the beginning of the trial ,\n'... - ' followed by a white box appearing on either side of the screen.\n\n' ... - '* If the color of the initial fixation box is green,\n'... - ' you should look at the white box.\n' ... - '* If the color of the initial fixation box is red, \n'... - ' you should look in the opposite direction from the white box with same amount of\n'... - ' distance to the center of the screen.\n'... - '* Please try to perform the task as quickly and as precisely as possible.\n\n ' ... - 'Press any key to continue.']; - - -% Pixels Per Degree in X and Y -PPD_X = 30; -PPD_Y = 30; - -if ~IsOctave - commandwindow; -else - more off; -end - -try - - %%%%%%%%%% - % STEP 1 % - %%%%%%%%%% - - % Added a dialog box to set your own EDF file name before opening - % experiment graphics. Make sure the entered EDF file name is 1 to 8 - % characters in length and only numbers or letters are allowed. - % Note: Octave does not support GUIs. replace lines below with - % %edfFile= 'DEMO.EDF' - - if IsOctave - edfFile = 'DEMO'; - else - prompt = {'Enter tracker EDF file name (1 to 8 letters or numbers)'}; - dlg_title = 'Create EDF file'; - num_lines= 1; - def = {'DEMO'}; - answer = inputdlg(prompt,dlg_title,num_lines,def); - edfFile = answer{1}; - fprintf('EDFFile: %s\n', edfFile ); - end - - %%%%%%%%%% - % STEP 2 % - %%%%%%%%%% - - % Open a graphics window on the main screen - % using the PsychToolbox's Screen function. - screenNumber=max(Screen('Screens')); - [window, wRect]=Screen('OpenWindow', screenNumber, 0,[],32,2); %#ok<*NASGU> - Screen(window,'BlendFunction',GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - [winWidth, winHeight] = WindowSize(window); - - % Select specific text font, style and size: - Screen('TextFont',window, 'Courier New'); - Screen('TextSize',window, round(winWidth * 0.015)); - Screen('TextStyle', window, 1+2); - - DrawFormattedText(window, instructionText, 100,200, [255 255 255]); - Screen('Flip',window); - - KbWait; - while KbCheck; end; - - - %%%%%%%%%% - % STEP 3 % - %%%%%%%%%% - - % Provide Eyelink with details about the graphics environment - % and perform some initializations. The information is returned - % in a structure that also contains useful defaults - % and control codes (e.g. tracker state bit and Eyelink key values). - % make necessary changes to calibration structure parameters and pass - % it to EyelinkUpdateDefaults for changes to take affect - - el=EyelinkInitDefaults(window); - - % We are changing calibration to a black background with white targets, - % no sound and smaller targets - el.backgroundcolour = BlackIndex(el.window); - el.msgfontcolour = WhiteIndex(el.window); - el.imgtitlecolour = WhiteIndex(el.window); - el.targetbeep = 0; - el.calibrationtargetcolour= WhiteIndex(el.window); - % for lower resolutions you might have to play around with these values - % a little. If you would like to draw larger targets on lower res - % settings please edit PsychEyelinkDispatchCallback.m and see comments - % in the EyelinkDrawCalibrationTarget function - el.calibrationtargetsize= 1; - el.calibrationtargetwidth=0.5; - - EyelinkUpdateDefaults(el); - - %%%%%%%%%% - % STEP 4 % - %%%%%%%%%% - - % Initialization of the connection with the Eyelink Gazetracker. - % exit program if this fails. - if ~EyelinkInit(dummymode) - fprintf('Eyelink Init aborted.\n'); - cleanup; % cleanup function - return; - end - - % open file to record data to - res = Eyelink('Openfile', edfFile); - if res~=0 - fprintf('Cannot create EDF file ''%s'' ', edffilename); - cleanup; - return; - end - - % make sure we're still connected. - if Eyelink('IsConnected')~=1 && ~dummymode - cleanup; - return; - end - - - %%%%%%%%%% - % STEP 5 % - %%%%%%%%%% - - % SET UP TRACKER CONFIGURATION - % Setting the proper recording resolution, proper calibration type, - % as well as the data file content; - - Eyelink('command', 'add_file_preamble_text ''Recorded by EyelinkToolbox demo-experiment'''); - % This command is crucial to map the gaze positions from the tracker to - % screen pixel positions to determine fixation - Eyelink('command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, winWidth-1, winHeight-1); - Eyelink('message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, winWidth-1, winHeight-1); - % set calibration type. - Eyelink('command', 'calibration_type = HV9'); - Eyelink('command', 'generate_default_targets = YES'); - % set parser (conservative saccade thresholds) - Eyelink('command', 'saccade_velocity_threshold = 35'); - Eyelink('command', 'saccade_acceleration_threshold = 9500'); - % set EDF file contents - % 5.1 retrieve tracker version and tracker software version - [v,vs] = Eyelink('GetTrackerVersion'); - fprintf('Running experiment on a ''%s'' tracker.\n', vs ); - vsn = regexp(vs,'\d','match'); - - if v == 3 && str2double(vsn{1}) == 4 % if EL 1000 and tracker version 4.xx - - % remote mode possible add HTARGET ( head target) - Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); - Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT,HTARGET'); - % set link data (used for gaze cursor) - Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,FIXUPDATE,INPUT'); - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT,HTARGET'); - else - Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,FIXUPDATE,INPUT'); - Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT'); - % set link data (used for gaze cursor) - Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,FIXUPDATE,INPUT'); - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); - end - % allow to use the big button on the eyelink gamepad to accept the - % calibration/drift correction target - Eyelink('command', 'button_function 5 "accept_target_fixation"'); - - %%%%%%%%%% - % STEP 6 % - %%%%%%%%%% - - % Hide the mouse cursor and Calibrate the eye tracker - - Screen('HideCursorHelper', window); - % enter Eyetracker camera setup mode, calibration and validation - EyelinkDoTrackerSetup(el); - - - % setup a random permutation of the trials - order = randperm(32); - dots = zeros(2,3); - dots(2,:) = winHeight/2; - dots(1,1) = winWidth/2; - color = zeros(3,3); - if placeHolderFlag - color(:,2:3) = [255 255 255 ; 255 255 255]'; - else - color(:,2:3) = [0 0 0 ; 0 0 0]'; - end - feedback = [-15 -15 15 15]; - - %%%%%%%%%% - % STEP 7 % - %%%%%%%%%% - - % Now starts running individual trials; - % You can keep the rest of the code except for the implementation - % of graphics and event monitoring - % Each trial should have a pair of "StartRecording" and "StopRecording" - % calls as well integration messages to the data file (message to mark - % the time of critical events and the image/interest area/condition - % information for the trial) - - - for i=1:32 - - % determine current trial type and send msg to edf - perm = task(order(i),:); - Eyelink('Message', 'permutation number %d', order(i)); - if perm(1) == 1 - Eyelink('Message', 'Prosaccade'); - color(:,1) = [0 255 0]'; - else - Eyelink('Message', 'Antisaccade'); - color(:,1) = [255 0 0]'; - end - if perm(2) == 1 - feedbackpos = 'left'; - Eyelink('Message', 'Left'); - else - feedbackpos = 'right'; - Eyelink('Message', 'Right'); - end - Eyelink('Message', 'Amplitude %d', perm(3)); - - dots(1,2) = winWidth/2 + perm(3) * PPD_X; % right - dots(1,3) = winWidth/2 - perm(3) * PPD_X; % left - - if perm(2) == 1 - feedback = CenterRect(feedback, [dots(1,3)-5 dots(2,3)-5 dots(1,3)+5 dots(2,3)+5]); - else - feedback = CenterRect(feedback, [dots(1,2)-5 dots(2,2)-5 dots(1,2)+5 dots(2,2)+5]); - end - - % STEP 7.1 - % Sending a 'TRIALID' message to mark the start of a trial in Data - % Viewer. This is different than the start of recording message - % START that is logged when the trial recording begins. The viewer - % will not parse any messages, events, or samples, that exist in - % the data file prior to this message. - Eyelink('Message', 'TRIALID %d', i); - - % This supplies the title at the bottom of the eyetracker display - Eyelink('command', 'record_status_message "TRIAL %d/%d"', i, 32); - % Before recording, we place reference graphics on the host display - % Must be offline to draw to EyeLink screen - Eyelink('Command', 'set_idle_mode'); - % clear tracker display and draw box at center - Eyelink('Command', 'clear_screen 0'); - - - if perm(1) == 1 - % prosaccade - - saccade = 'pro'; - - if perm(2) == 1 - rect1 = [dots(1,3)-60, dots(2,3)-60, dots(1,3)+60, dots(2,3) + 60]; - rect2 =[dots(1,2)-60, dots(2,2)-60, dots(1,2)+60, dots(2,2) + 60]; - else - rect1 = [dots(1,2)-60, dots(2,2)-60, dots(1,2)+60, dots(2,2) + 60]; - rect2 =[dots(1,3)-60, dots(2,3)-60, dots(1,3)+60, dots(2,3) + 60]; - end - else - % anti saccade - - saccade = 'anti'; - - if perm(2) == 2 - rect1 = [dots(1,3)-60, dots(2,3)-60, dots(1,3)+60, dots(2,3) + 60]; - rect2 =[dots(1,2)-60, dots(2,2)-60, dots(1,2)+60, dots(2,2) + 60]; - else - rect1 = [dots(1,2)-60, dots(2,2)-60, dots(1,2)+60, dots(2,2) + 60]; - rect2 =[dots(1,3)-60, dots(2,3)-60, dots(1,3)+60, dots(2,3) + 60]; - end - end - - % draw shapes to host pc - Eyelink('command', 'draw_filled_box %d %d %d %d 2' ,rect1(1),rect1(2),rect1(3),rect1(4)); - Eyelink('command', 'draw_filled_box %d %d %d %d 4' ,rect2(1),rect2(2),rect2(3),rect2(4)); - - % STEP 7.2 - % Do a drift correction at the beginning of each trial - % Performing drift correction (checking) is optional for - % EyeLink 1000 eye trackers. - EyelinkDoDriftCorrection(el); - - % STEP 7.3 - % start recording eye position (preceded by a short pause so that - % the tracker can finish the mode transition) - % The paramerters for the 'StartRecording' call controls the - % file_samples, file_events, link_samples, link_events availability - Eyelink('Command', 'set_idle_mode'); - WaitSecs(0.05); - Eyelink('StartRecording'); - % record a few samples before we actually start displaying - % otherwise you may lose a few msec of data - WaitSecs(0.1); - - % random fixation time between 800 and 1200 ms - fixationTime = GetSecs + ((800 + (1200-800) * rand)/1000); - while GetSecs < fixationTime - - % STEP 7.4 - % Prepare and show the screen - Screen('FillRect', window, el.backgroundcolour); - Screen('DrawDots',window, dots,10, color); - Screen('Flip', window); - Eyelink('Message', 'FIXATION_DISPLAY'); - end - - if gapManipulation - gapTime = GetSecs + GAP_TIMER/1000; - while GetSecs < gapTime - Screen('FillRect', window, el.backgroundcolour); - Screen('Flip', window); - Eyelink('Message', 'GAP_DISPLAY'); - end - end - - targetTime = GetSecs + TARGET_TIMER/1000; - firstloop = 1; - imgfile = sprintf('img%d%d.jpg' , perm(3),perm(2)); - while GetSecs < targetTime - Screen('FillRect', window, el.backgroundcolour); - Screen('DrawDots',window, dots(:,2:3),10, color(:,2:3) ); - Screen('FrameRect',window,255, feedback); - Screen('Flip', window); - % mark zero-plot time in data file - Eyelink('Message', 'TARGET_DISPLAY'); - % this is done so that you do not write images everytime you might - % want to delete the image files if you change the trial - % parameters. Another option is hand drawing iA in Data Viewer - if firstloop && ~exist(imgfile,'file') - imageArray = Screen('GetImage', window); - imwrite(imageArray,imgfile); - firstloop = 0; - end - end - - - % STEP 7.6 - % add 100 msec of data to catch final events and blank display - WaitSecs(0.1); - Eyelink('StopRecording'); - Screen('FillRect', window, el.backgroundcolour); - Screen('Flip', window); - - % STEP 7.7 - % Send out necessary integration messages for data analysis - % See "Protocol for EyeLink Data to Viewer Integration-> Interest - % Area Commands" section of the EyeLink Data Viewer User Manual - % IMPORTANT! Don't send too many messages in a very short period of - % time or the EyeLink tracker may not be able to write them all - % to the EDF file. - % Consider adding a short delay every few messages. - WaitSecs(0.001); - % Send an integration message so that an image can be loaded as - % overlay backgound when performing Data Viewer analysis. This - % message can be placed anywhere within the scope of a trial (i.e., - % after the 'TRIALID' message and before 'TRIAL_RESULT') - % See "Protocol for EyeLink Data to Viewer Integration -> Image - % Commands" section of the EyeLink Data Viewer User Manual. - Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', imgfile, winWidth/2, winHeight/2); - - % interest areas - Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 1, rect1(1), rect1(2), rect1(3), rect1(4),'target'); - Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 2, rect2(1), rect2(2), rect2(3), rect2(4),'distractor'); - % Send messages to report trial condition information - % Each message may be a pair of trial condition variable and its - % corresponding value follwing the '!V TRIAL_VAR' token message - % See "Protocol for EyeLink Data to Viewer Integration-> Trial - % Message Commands" section of the EyeLink Data Viewer User Manual - WaitSecs(0.001); - - Eyelink('Message', '!V TRIAL_VAR index %d', i); - Eyelink('Message', '!V TRIAL_VAR amplitude %d', perm(3)); % 5 or 10 - Eyelink('Message', '!V TRIAL_VAR saccade %s', saccade); % pro or anti - Eyelink('Message', '!V TRIAL_VAR feedback %s', feedbackpos); % left or right? - % STEP 7.8 - % Sending a 'TRIAL_RESULT' message to mark the end of a trial in - % Data Viewer. This is different than the end of recording message - % END that is logged when the trial recording ends. The viewer will - % not parse any messages, events, or samples that exist in the data - % file after this message. - Eyelink('Message', 'TRIAL_RESULT 0'); - - - end - - - %%%%%%%%%% - % STEP 8 % - %%%%%%%%%% - - % End of Experiment; close the file first - % close graphics window, close data file and shut down tracker - Eyelink('Command', 'set_idle_mode'); - WaitSecs(0.5); - Eyelink('CloseFile'); - - try - fprintf('Receiving data file ''%s''\n', edfFile ); - status=Eyelink('ReceiveFile'); - if status > 0 - fprintf('ReceiveFile status %d\n', status); - end - if 2==exist(edfFile, 'file') - fprintf('Data file ''%s'' can be found in ''%s''\n', edfFile, pwd ); - end - catch %#ok<*CTCH> - fprintf('Problem receiving data file ''%s''\n', edfFile ); - end - - %%%%%%%%%% - % STEP 9 % - %%%%%%%%%% - - % run cleanup function (close the eye tracker and window). - cleanup; - -catch - cleanup; - fprintf('%s: some error occured\n', mfilename); - psychrethrow(lasterror); %#ok -end - - function cleanup - % Shutdown Eyelink: - Eyelink('Shutdown'); - Screen('CloseAll'); - end - -end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/img101.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/img101.jpg deleted file mode 100644 index 6c6507ddb1..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/img101.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/img102.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/img102.jpg deleted file mode 100644 index 7eabc4ff48..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/img102.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/img51.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/img51.jpg deleted file mode 100644 index 76f85ed351..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/img51.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/img52.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/img52.jpg deleted file mode 100644 index 1bd5cf69a1..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/img52.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/imgfile.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/imgfile.jpg deleted file mode 100644 index e738803845..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/AntiSaccade/imgfile.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/ELCustomCalibration/EyelinkPictureCustomCalibration.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/ELCustomCalibration/EyelinkPictureCustomCalibration.m deleted file mode 100644 index cb5a5b4634..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/ELCustomCalibration/EyelinkPictureCustomCalibration.m +++ /dev/null @@ -1,427 +0,0 @@ -function EyelinkPictureCustomCalibration -% -% ___________________________________________________________________ -% -% Demo implemetation of EyelinkPicture with custom calbration -% based on EyelinkPicture by JS -% -% -% stimulus: 3 pictures -% -% This task demonstrates stimuli presentation with Eyelink and Data Viewer -% integration as well as a few functionalities: -% 1. how to perform a custom calibration by modifying -% calibration target positions -% 2. how to change camera setup parameters -% on the hostfrom within ETB -% 3. how to query host pc for variable values such as mount type -% -% ___________________________________________________________________ - -% HISTORY -% mm/dd/yy -% -% 01/28/11 NJ created -% 12/20/13 LJ changed isoctave to IsOctave, case sensitive for the latest matlab -% fixed issue with non integer arguments for Eyelink('message' ...) -% 07/07/23 mk Use imread() and new Eyelink('ImageTransfer') to handle image file -% formats other than uncompressed Microsoft bitmap .bmp files. -% - -if ~IsOctave - commandwindow; -else - more off; -end - -% list of images used for the trial. Octave cares about capitalization -imageList = {'../town.jpg' '../town_blur.jpg' '../composite.jpg'}; -dummymode=0; -try - %%%%%%%%%% - % STEP 1 % - %%%%%%%%%% - - % Added a dialog box to set your own EDF file name before opening - % experiment graphics. Make sure the entered EDF file name is 1 to 8 - % characters in length and only numbers or letters are allowed. - prompt = {'Enter tracker EDF file name (1 to 8 letters or numbers)'}; - dlg_title = 'Create EDF file'; - num_lines= 1; - def = {'DEMO'}; - answer = inputdlg(prompt,dlg_title,num_lines,def); - edfFile = answer{1}; - fprintf('EDFFile: %s\n', edfFile ); - - %%%%%%%%%% - % STEP 2 % - %%%%%%%%%% - - % Open a graphics window on the main screen - % using the PsychToolbox's Screen function. - screenNumber=max(Screen('Screens')); - [window, wRect]=Screen('OpenWindow', screenNumber, 0,[],32,2); %#ok<*NASGU> - Screen('BlendFunction', window, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - [wW, wH] = WindowSize(window); - - %%%%%%%%%% - % STEP 3 % - %%%%%%%%%% - - % Provide Eyelink with details about the graphics environment - % and perform some initializations. The information is returned - % in a structure that also contains useful defaults - % and control codes (e.g. tracker state bit and Eyelink key values). - - el=EyelinkInitDefaults(window); - - el.backgroundcolour = BlackIndex(el.window); - el.msgfontcolour = WhiteIndex(el.window); - el.imgtitlecolour = WhiteIndex(el.window); - el.targetbeep = 0; - el.calibrationtargetcolour= WhiteIndex(el.window); - el.calibrationtargetsize= 1; - el.calibrationtargetwidth=0.5; - el.displayCalResults = 1; - el.eyeimgsize=50; - EyelinkUpdateDefaults(el); - - %%%%%%%%%% - % STEP 4 % - %%%%%%%%%% - - % Initialization of the connection with the Eyelink Gazetracker. - % exit program if this fails. - if ~EyelinkInit(dummymode) - fprintf('Eyelink Init aborted.\n'); - cleanup; % cleanup function - return; - end - - % open file to record data to - res = Eyelink('Openfile', edfFile); - if res~=0 - fprintf('Cannot create EDF file ''%s'' ', edffilename); - cleanup; - % Eyelink( 'Shutdown'); - return; - end - - Eyelink('command', 'add_file_preamble_text ''Recorded by EyelinkToolbox demo-experiment'''); - [width, height]=Screen('WindowSize', screenNumber); - - - % make sure we're still connected. - if Eyelink('IsConnected')~=1 && ~dummymode - fprintf('Not connected. exiting'); - cleanup; - return; - end - - %%%%%%%%%% - % STEP 5 % - %%%%%%%%%% - - % SET UP TRACKER CONFIGURATION - % Setting the proper recording resolution, proper calibration type, - % as well as the data file content; - - % it's location here is overridded by EyelinkDoTracker which resets it - % with display PC coordinates - Eyelink('command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, width-1, height-1); - Eyelink('message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, width-1, height-1); - % set calibration type. - Eyelink('command', 'calibration_type = HV5'); - % you must send this command with value NO for custom calibration - % you must also reset it to YES for subsequent experiments - Eyelink('command', 'generate_default_targets = NO'); - - % STEP 5.1 modify calibration and validation target locations - Eyelink('command','calibration_samples = 6'); - Eyelink('command','calibration_sequence = 0,1,2,3,4,5'); - Eyelink('command','calibration_targets = %d,%d %d,%d %d,%d %d,%d %d,%d',... - width/2,height/2, width/2,height*0.2, width/2,height - height*0.2, width*0.2,height/2, width - width*0.2,height/2 ); - Eyelink('command','validation_samples = 5'); - Eyelink('command','validation_sequence = 0,1,2,3,4,5'); - Eyelink('command','validation_targets = %d,%d %d,%d %d,%d %d,%d %d,%d',... - width/2,height/2, width/2,height*0.2, width/2,height - height*0.2, width*0.2,height/2, width - width*0.2,height/2 ); - - - % set parser (conservative saccade thresholds) - Eyelink('command', 'saccade_velocity_threshold = 35'); - Eyelink('command', 'saccade_acceleration_threshold = 9500'); - % set EDF file contents - % STEP 5.2 retrieve tracker version and tracker software version - [v,vs] = Eyelink('GetTrackerVersion'); - fprintf('Running experiment on a ''%s'' tracker.\n', vs ); - vsn = regexp(vs,'\d','match'); % wont work on EL I - if isempty(vsn) - eyelinkI = 1; - else - eyelinkI = 0; - end - - if v == 3 && str2double(vsn{1}) == 4 % if EL 1000 and tracker version 4.xx - - % remote mode possible add HTARGET ( head target) - Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); - Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT,HTARGET'); - % set link data (used for gaze cursor) - Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,FIXUPDATE,INPUT'); - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT,HTARGET'); - else - Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); - Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT'); - % set link data (used for gaze cursor) - Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,FIXUPDATE,INPUT'); - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); - end - % allow to use the big button on the eyelink gamepad to accept the - % calibration/drift correction target - Eyelink('command', 'button_function 5 "accept_target_fixation"'); - - - % STEP 5.3 change camera setup options - if v == 3 - % set pupil Tracking model in camera setup screen - % no = centroid. yes = ellipse - Eyelink('command', 'use_ellipse_fitter = no'); - % set sample rate in camera setup screen - Eyelink('command', 'sample_rate = %d',1000); - end - - % if desktop mount and tracker version is 4.2 or later change - % illumination - twropts = {'TOWER','MPRIM','MIRROR','BLRR','MLRR'}; - % query tracker for mount type using elcl_select_configuration variable - [result,reply]=Eyelink('ReadFromTracker','elcl_select_configuration'); - - if ~eyelinkI && ~dummymode && ~result && ~any(strcmp(reply,twropts)) && str2double(vsn{1}) == 4 && str2double(vsn{2}) >= 2 - %set illumination power in camera setup screen - % 1 = 100%, 2 = 75%, 3 = 50% - - Eyelink('command', 'elcl_tt_power = %d',2); - else - disp('failed to change illumination. possible causes: DummyMode, EL not desktop mount, EL not 1000, EL version number pre 4.2, EL disconnected'); - end - - % query host to see if automatic calibration sequencing is enabled. - % ReadFromTracker needs to have 2 outputs. - % variables querable are listed in the .ini files in the host - % directories. Note that not all variables are querable. - [result, reply]=Eyelink('ReadFromTracker','enable_automatic_calibration'); - - if reply % reply = 1 - fprintf('Automatic sequencing ON'); - else - fprintf('Automatic sequencing OFF'); - end - - %%%%%%%%%% - % STEP 6 % - %%%%%%%%%% - disp('pre cal') - % Hide the mouse cursor; - HideCursor(window); - % enter Eyetracker camera setup mode, calibration and validation - EyelinkDoTrackerSetup(el); - disp('post cal') - %%%%%%%%%% - % STEP 7 % - %%%%%%%%%% - - % Now starts running individual trials; - % You can keep the rest of the code except for the implementation - % of graphics and event monitoring - % Each trial should have a pair of "StartRecording" and "StopRecording" - % calls as well integration messages to the data file (message to mark - % the time of critical events and the image/interest area/condition - % information for the trial) - - for i=1:3 - - imgfile = char(imageList(i)); - - % STEP 7.1 - % Sending a 'TRIALID' message to mark the start of a trial in Data - % Viewer. This is different than the start of recording message - % START that is logged when the trial recording begins. The viewer - % will not parse any messages, events, or samples, that exist in - % the data file prior to this message. - Eyelink('Message', 'TRIALID %d', i); - - % This supplies the title at the bottom of the eyetracker display - Eyelink('command', 'record_status_message "TRIAL %d/%d %s"', i, 3, imgfile); - % Before recording, we place reference graphics on the host display - % Must be in offline mode to transfer image to Host PC - Eyelink('Command', 'set_idle_mode'); - % clear tracker display and draw box at center - Eyelink('Command', 'clear_screen %d', 0); - - finfo = imfinfo(imgfile); - finfo.Filename - - % The constraint on imdata for use by Eyelink('ImageTransfer') is that - % imdata can be any uint8 image with 1, 3 or 4 layers for a grayscale, - % RGB truecolor or RGBA image of 8 bpc resolution: - imdata=imread(imgfile); - - Status = Eyelink('ImageTransfer', imdata,0,0,0,0,round(wW/2 - finfo.Width/2) ,round(wH/2 - finfo.Height/2),4); - if transferStatus ~= 0 - fprintf('Image to host transfer failed\n'); - end - WaitSecs(0.1); - Eyelink('command', 'draw_box %d %d %d %d 15', width/2-50, height/2-50, width/2+50, height/2+50); - - % STEP 7.2 - % Do a drift correction at the beginning of each trial - % Performing drift correction (checking) is optional for - % EyeLink 1000 eye trackers. - EyelinkDoDriftCorrection(el); - - % STEP 7.3 - % start recording eye position (preceded by a short pause so that - % the tracker can finish the mode transition) - % The paramerters for the 'StartRecording' call controls the - % file_samples, file_events, link_samples, link_events availability - Eyelink('Command', 'set_idle_mode'); - WaitSecs(0.05); - Eyelink('StartRecording'); - % record a few samples before we actually start displaying - % otherwise you may lose a few msec of data - WaitSecs(0.1); - - % STEP 7.4 - % Prepare and show the screen. - Screen('FillRect', window, el.backgroundcolour); - imageTexture=Screen('MakeTexture',window, imdata); - Screen('DrawTexture', window, imageTexture); - Screen('DrawText', window, 'Press the SPACEBAR to end the recording of the trial.', width/5, height/2, 0); - Screen('Flip', window); - % mark zero-plot time in data file - Eyelink('Message', 'SYNCTIME'); - - % Send an integration message so that an image can be loaded as - % overlay backgound when performing Data Viewer analysis. This - % message can be placed anywhere within the scope of a trial (i.e., - % after the 'TRIALID' message and before 'TRIAL_RESULT') - % See "Protocol for EyeLink Data to Viewer Integration -> Image - % Commands" section of the EyeLink Data Viewer User Manual. - Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', imgfile, width/2, height/2); - - stopkey=KbName('space'); - - % STEP 7.5 - % Monitor the trial events; - while 1 % loop till error or space bar is pressed - % Check recording status, stop display if error - error=Eyelink('CheckRecording'); - if(error~=0) - break; - end - % check for keyboard press - [keyIsDown,secs,keyCode] = KbCheck; %#ok<*ASGLU> - % if spacebar was pressed stop display - if keyCode(stopkey) - Eyelink('Message', 'Key pressed') - break; - end - end % main loop - - - % STEP 7.6 - % Clear the display - Screen('FillRect', window, el.backgroundcolour); - Screen('Flip', window); - Eyelink('Message', 'BLANK_SCREEN'); - % adds 100 msec of data to catch final events - WaitSecs(0.1); - % stop the recording of eye-movements for the current trial - Eyelink('StopRecording'); - - - % STEP 7.7 - % Send out necessary integration messages for data analysis - % Send out interest area information for the trial - % See "Protocol for EyeLink Data to Viewer Integration-> Interest - % Area Commands" section of the EyeLink Data Viewer User Manual - % IMPORTANT! Don't send too many messages in a very short period of - % time or the EyeLink tracker may not be able to write them all - % to the EDF file. - % Consider adding a short delay every few messages. - WaitSecs(0.001); - Eyelink('Message', '!V IAREA ELLIPSE %d %d %d %d %d %s', 1, floor(width/2-50), floor(height/2-50), floor(width/2+50), floor(height/2+50),'center'); - Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 2, floor(width/4-50), floor(height/2-50), floor(width/4+50), floor(height/2+50),'left'); - Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 3, floor(3*width/4-50), floor(height/2-50), floor(3*width/4+50), floor(height/2+50),'right'); - Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 4, floor(width/2-50), floor(height/4-50), floor(width/2+50), floor(height/4+50),'up'); - Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 5, floor(width/2-50), floor(3*height/4-50), floor(width/2+50), floor(3*height/4+50),'down'); - - - % Send messages to report trial condition information - % Each message may be a pair of trial condition variable and its - % corresponding value follwing the '!V TRIAL_VAR' token message - % See "Protocol for EyeLink Data to Viewer Integration-> Trial - % Message Commands" section of the EyeLink Data Viewer User Manual - WaitSecs(0.001); - Eyelink('Message', '!V TRIAL_VAR index %d', i) - Eyelink('Message', '!V TRIAL_VAR imgfile %s', imgfile) - - % STEP 7.8 - % Sending a 'TRIAL_RESULT' message to mark the end of a trial in - % Data Viewer. This is different than the end of recording message - % END that is logged when the trial recording ends. The viewer will - % not parse any messages, events, or samples that exist in the data - % file after this message. - Eyelink('Message', 'TRIAL_RESULT 0') - end - - - %%%%%%%%%% - % STEP 8 % - %%%%%%%%%% - - % End of Experiment; close the file first - % close graphics window, close data file and shut down tracker - - % reset so tracker uses defaults calibration for other experiemnts - Eyelink('command', 'generate_default_targets = YES') - Eyelink('Command', 'set_idle_mode'); - WaitSecs(0.5); - Eyelink('CloseFile'); - - % download data file - try - fprintf('Receiving data file ''%s''\n', edfFile ); - status=Eyelink('ReceiveFile'); - if status > 0 - fprintf('ReceiveFile status %d\n', status); - end - if 2==exist(edfFile, 'file') - fprintf('Data file ''%s'' can be found in ''%s''\n', edfFile, pwd ); - end - catch %#ok<*CTCH> - fprintf('Problem receiving data file ''%s''\n', edfFile ); - end - - %%%%%%%%%% - % STEP 9 % - %%%%%%%%%% - - % run cleanup function (close the eye tracker and window). - cleanup - -catch - %this "catch" section executes in case of an error in the "try" section - %above. Importantly, it closes the onscreen window if its open. - cleanup; - -end %try..catch. - - -% Cleanup routine: - function cleanup - % Shutdown Eyelink: - Eyelink('Shutdown'); - sca; - end -end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyeLinkPicture.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyeLinkPicture.m deleted file mode 100644 index 7be3a6393c..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyeLinkPicture.m +++ /dev/null @@ -1,367 +0,0 @@ -function EyeLinkPicture - -% Short MATLAB example that uses the Eyelink and Psychophysics Toolboxes -% This is the example as shown in the EyelinkToolbox article in BRMIC -% Cornelissen, Peters and Palmer 2002), but updated to also work on the -% PC version of the toolbox, and uses some new routines. -% -% Adapted after "Psychtoolbox\PsychHardware\EyelinkToolbox\EyelinkDemos\ -% ShortDemos\EyelinkExample.m" -% -% HISTORY -% -% mm/dd/yy -% 07/01/08 js redone the structure of the experiment and added -% integration messages to the EyeLink Data Viewer software -% 07/14/08 js added code to set your own EDF file name before opening -% the experiment graphics -% 07/13/10 fwc made to work with new toolbox with callback and updated to -% enable eye image display, added "cleanup" function, -% reenabled try-catch -% 09/20/12 srresearch updated to allow: -% 1. Transfer the image to host. (STEP 7.1) -% 2. Change the calibration colors and turn on/off the -% calibration beep. (STEP 6) -% 3. End trials by button box. (STEP 7.5) -% 12/20/13 srresearch -% Added part to make it run with octave -% fixed issue with non integer arguments for Eyelink('message' ...) -% removed cleanup function, used Eyelink('ShutDown'); Screen('CloseAll') instead. -% -% 07/07/23 mk Use imread() and new Eyelink('ImageTransfer') to handle image file -% formats other than uncompressed Microsoft bitmap .bmp files. -% - -if ~IsOctave - commandwindow; -else - more off; -end - -% list of images used for the trial -imageList = {'town.jpg' 'town_blur.jpg' 'composite.jpg'}; -dummymode=0; -try - % STEP 1 - % Added a dialog box to set your own EDF file name before opening - % experiment graphics. Make sure the entered EDF file name is 1 to 8 - % characters in length and only numbers or letters are allowed. - prompt = {'Enter tracker EDF file name (1 to 8 letters or numbers)'}; - dlg_title = 'Create EDF file'; - num_lines= 1; - def = {'DEMO'}; - answer = inputdlg(prompt,dlg_title,num_lines,def); - edfFile = answer{1}; - fprintf('EDFFile: %s\n', edfFile ); - - % STEP 2 - % Initialization of the connection with the Eyelink Gazetracker. - % exit program if this fails. - if ~EyelinkInit(dummymode) - fprintf('Eyelink Init aborted.\n'); - return; - end - - % STEP 3 - % Open a graphics window on the main screen - % using the PsychToolbox's Screen function. - screenNumber=max(Screen('Screens')); - [window, wRect]=Screen('OpenWindow', screenNumber, 0); - Screen('BlendFunction', window, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - - % STEP 4 - % Provide Eyelink with details about the graphics environment - % and perform some initializations. The information is returned - % in a structure that also contains useful defaults - % and control codes (e.g. tracker state bit and Eyelink key values). - el=EyelinkInitDefaults(window); - - % the following code is used to check the version of the eye tracker - % and version of the host software - sw_version = 0; - - [v vs]=Eyelink('GetTrackerVersion'); - fprintf('Running experiment on a ''%s'' tracker.\n', vs ); - - % open file to record data to - i = Eyelink('Openfile', edfFile); - if i~=0 - fprintf('Cannot create EDF file ''%s'' ', edffilename); - Eyelink('Shutdown'); - Screen('CloseAll'); - return; - end - - Eyelink('command', 'add_file_preamble_text ''Recorded by EyelinkToolbox demo-experiment'''); - [width, height]=Screen('WindowSize', screenNumber); - - - % STEP 5 - % SET UP TRACKER CONFIGURATION - % Setting the proper recording resolution, proper calibration type, - % as well as the data file content; - Eyelink('command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, width-1, height-1); - Eyelink('message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, width-1, height-1); - % set calibration type. - Eyelink('command', 'calibration_type = HV9'); - % set parser (conservative saccade thresholds) - - % set EDF file contents using the file_sample_data and - % file-event_filter commands - % set link data thtough link_sample_data and link_event_filter - Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); - Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); - - % check the software version - % add "HTARGET" to record possible target data for EyeLink Remote - if sw_version >=4 - Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,HTARGET,GAZERES,STATUS,INPUT'); - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,HTARGET,STATUS,INPUT'); - else - Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT'); - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); - end - - % allow to use the big button on the eyelink gamepad to accept the - % calibration/drift correction target - Eyelink('command', 'button_function 5 "accept_target_fixation"'); - - - % make sure we're still connected. - if Eyelink('IsConnected')~=1 && dummymode == 0 - fprintf('not connected, clean up\n'); - Eyelink('Shutdown'); - Screen('CloseAll'); - return; - end - - - % STEP 6 - % Calibrate the eye tracker - % setup the proper calibration foreground and background colors - el.backgroundcolour = [128 128 128]; - el.calibrationtargetcolour = [0 0 0]; - - % parameters are in frequency, volume, and duration - % set the second value in each line to 0 to turn off the sound - el.cal_target_beep=[600 0.5 0.05]; - el.drift_correction_target_beep=[600 0.5 0.05]; - el.calibration_failed_beep=[400 0.5 0.25]; - el.calibration_success_beep=[800 0.5 0.25]; - el.drift_correction_failed_beep=[400 0.5 0.25]; - el.drift_correction_success_beep=[800 0.5 0.25]; - % you must call this function to apply the changes from above - EyelinkUpdateDefaults(el); - - % Hide the mouse cursor; - HideCursor(window); - EyelinkDoTrackerSetup(el); - - - % STEP 7 - % Now starts running individual trials; - % You can keep the rest of the code except for the implementation - % of graphics and event monitoring - % Each trial should have a pair of "StartRecording" and "StopRecording" - % calls as well integration messages to the data file (message to mark - % the time of critical events and the image/interest area/condition - % information for the trial) - - for i=1:3 - % Now within the scope of each trial; - imgfile= which(char(imageList(i))); - - % STEP 7.1 - % Sending a 'TRIALID' message to mark the start of a trial in Data - % Viewer. This is different than the start of recording message - % START that is logged when the trial recording begins. The viewer - % will not parse any messages, events, or samples, that exist in - % the data file prior to this message. - Eyelink('Message', 'TRIALID %d', i); - - % This supplies the title at the bottom of the eyetracker display - Eyelink('command', 'record_status_message "TRIAL %d/%d %s"', i, 3, imgfile); - % Before recording, we place reference graphics on the host display - % Must be offline to draw to EyeLink screen - Eyelink('Command', 'set_idle_mode'); - % clear tracker display and draw box at center - Eyelink('Command', 'clear_screen 0') - Eyelink('command', 'draw_box %d %d %d %d 15', width/2-50, height/2-50, width/2+50, height/2+50); - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %transfer image to host - transferimginfo=imfinfo(imgfile); - - fprintf('img file name is %s\n',transferimginfo.Filename); - - % The constraint on imdata for use by Eyelink('ImageTransfer') is that - % imdata can be any uint8 image with 1, 3 or 4 layers for a grayscale, - % RGB truecolor or RGBA image of 8 bpc resolution: - imdata=imread(imgfile); - - % parameters of ImageTransfer: - % imagePath, xPosition, yPosition, width, height, trackerXPosition, trackerYPosition, xferoptions - transferStatus = Eyelink('ImageTransfer',imdata,0,0,transferimginfo.Width,transferimginfo.Height,width/2-transferimginfo.Width/2 ,height/2-transferimginfo.Height/2,1); - if transferStatus ~= 0 - fprintf('*****Image transfer Failed*****-------\n'); - end - - WaitSecs(0.1); - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - - % STEP 7.2 - % Do a drift correction at the beginning of each trial - % Performing drift correction (checking) is optional for - % EyeLink 1000 eye trackers. - EyelinkDoDriftCorrection(el); - - % STEP 7.3 - % start recording eye position (preceded by a short pause so that - % the tracker can finish the mode transition) - % The paramerters for the 'StartRecording' call controls the - % file_samples, file_events, link_samples, link_events availability - Eyelink('Command', 'set_idle_mode'); - WaitSecs(0.05); - Eyelink('StartRecording'); - % record a few samples before we actually start displaying - % otherwise you may lose a few msec of data - WaitSecs(0.1); - - % STEP 7.4 - % Prepare and show the screen. - Screen('FillRect', window, el.backgroundcolour); - imageTexture=Screen('MakeTexture',window, imdata); - Screen('DrawTexture', window, imageTexture); - Screen('DrawText', window, 'Press the SPACEBAR or BUTTON 5 to end the recording of the trial.', floor(width/5), floor(height/2), 0); - Screen('Flip', window); - % write out a message to indicate the time of the picture onset - % this message can be used to create an interest period in EyeLink - % Data Viewer. - - Eyelink('Message', 'SYNCTIME'); - - % Send an integration message so that an image can be loaded as - % overlay backgound when performing Data Viewer analysis. This - % message can be placed anywhere within the scope of a trial (i.e., - % after the 'TRIALID' message and before 'TRIAL_RESULT') - % See "Protocol for EyeLink Data to Viewer Integration -> Image - % Commands" section of the EyeLink Data Viewer User Manual. - Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', imgfile, width/2, height/2); - - stopkey=KbName('space'); - - % STEP 7.5 - % Monitor the trial events; - while 1 % loop till error or space bar is pressed - % Check recording status, stop display if error - error=Eyelink('CheckRecording'); - if(error~=0) - break; - end - - - % ending by pressing button 5 - buttonResult = Eyelink('ButtonStates'); - if buttonResult - if(bitshift(buttonResult, -4)==1) %fprintf('button 5 pressed\n'); - Eyelink('Message','Button 5 pressed'); - break; - end - end - - % check for keyboard press - [keyIsDown,secs,keyCode] = KbCheck; - % if spacebar was pressed stop display - if keyCode(stopkey) - Eyelink('Message', 'Key pressed'); - break; - end - end % main loop - - - % STEP 7.6 - % Clear the display - Screen('FillRect', window, el.backgroundcolour); - Screen('Flip', window); - Eyelink('Message', 'BLANK_SCREEN'); - % adds 100 msec of data to catch final events - WaitSecs(0.1); - % stop the recording of eye-movements for the current trial - Eyelink('StopRecording'); - - - % STEP 7.7 - % Send out necessary integration messages for data analysis - % Send out interest area information for the trial - % See "Protocol for EyeLink Data to Viewer Integration-> Interest - % Area Commands" section of the EyeLink Data Viewer User Manual - % IMPORTANT! Don't send too many messages in a very short period of - % time or the EyeLink tracker may not be able to write them all - % to the EDF file. - % Consider adding a short delay every few messages. - - % Please note that floor(A) is used to round A to the nearest - % integers less than or equal to A - - WaitSecs(0.001); - Eyelink('Message', '!V IAREA ELLIPSE %d %d %d %d %d %s', 1, floor(width/2)-50, floor(height/2)-50, floor(width/2)+50, floor(height/2)+50,'center'); - Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 2, floor(width/4)-50, floor(height/2)-50, floor(width/4)+50, floor(height/2)+50,'left'); - Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 3, floor(3*width/4)-50, floor(height/2)-50, floor(3*width/4)+50, floor(height/2)+50,'right'); - Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 4, floor(width/2)-50, floor(height/4)-50, floor(width/2)+50, floor(height/4)+50,'up'); - Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 5, floor(width/2)-50, floor(3*height/4)-50, floor(width/2)+50, floor(3*height/4)+50,'down'); - - - % Send messages to report trial condition information - % Each message may be a pair of trial condition variable and its - % corresponding value follwing the '!V TRIAL_VAR' token message - % See "Protocol for EyeLink Data to Viewer Integration-> Trial - % Message Commands" section of the EyeLink Data Viewer User Manual - WaitSecs(0.001); - Eyelink('Message', '!V TRIAL_VAR index %d', i) - Eyelink('Message', '!V TRIAL_VAR imgfile %s', imgfile) - - % STEP 7.8 - % Sending a 'TRIAL_RESULT' message to mark the end of a trial in - % Data Viewer. This is different than the end of recording message - % END that is logged when the trial recording ends. The viewer will - % not parse any messages, events, or samples that exist in the data - % file after this message. - Eyelink('Message', 'TRIAL_RESULT 0') - end - - % STEP 8 - % End of Experiment; close the file first - % close graphics window, close data file and shut down tracker - - Eyelink('Command', 'set_idle_mode'); - WaitSecs(0.5); - Eyelink('CloseFile'); - - % download data file - try - fprintf('Receiving data file ''%s''\n', edfFile ); - status=Eyelink('ReceiveFile'); - if status > 0 - fprintf('ReceiveFile status %d\n', status); - end - if 2==exist(edfFile, 'file') - fprintf('Data file ''%s'' can be found in ''%s''\n', edfFile, pwd ); - end - catch - fprintf('Problem receiving data file ''%s''\n', edfFile ); - end - - % STEP 9 - % close the eye tracker and window - Eyelink('ShutDown'); - sca; - -catch - %this "catch" section executes in case of an error in the "try" section - %above. Importantly, it closes the onscreen window if its open. - Eyelink('ShutDown'); - sca; - rethrow(lasterr); -end %try..catch. diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyelinkFixationWindow/EyelinkFixationWindow.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyelinkFixationWindow/EyelinkFixationWindow.m deleted file mode 100644 index 6daed602f7..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyelinkFixationWindow/EyelinkFixationWindow.m +++ /dev/null @@ -1,448 +0,0 @@ -function EyelinkFixationWindow -% -% ___________________________________________________________________ -% -% Demo implemetation of detecting eye in fixation window on display -% computer through samples from the tracker -% -% stimulus: A fixation dot -% -% This task demonstrates stimuli presentation with Eyelink and Data Viewer -% integration. -% -% press space to end experiment -% ___________________________________________________________________ - -% HISTORY -% mm/dd/yy -% -% 01/28/11 NJ created -% 12/20/13 LJ fixed issue with non integer arguments for Eyelink('message' ...) -% changed EyeLink to Eyelink , case sensitive for the latest matlab - -PsychDefaultSetup(1); - -% trial defaults -dummymode=0; - -if ~IsOctave - commandwindow; -else - more off; -end -stopkey=KbName('space'); -firstRun = 1; -infix=0; -dotSize = 10; -fixWinSize = 100; -imageList = {'HappyFace.jpg' 'SadFace.jpg'}; - -%[versionString, versionStructure]=PsychtoolboxVersion; - -try - - %%%%%%%%%% - % STEP 1 % - %%%%%%%%%% - - % Added a dialog box to set your own EDF file name before opening - % experiment graphics. Make sure the entered EDF file name is 1 to 8 - % characters in length and only numbers or letters are allowed. - if IsOctave - edfFile = 'DEMO'; - else - prompt = {'Enter tracker EDF file name (1 to 8 letters or numbers)'}; - dlg_title = 'Create EDF file'; - num_lines= 1; - def = {'DEMO'}; - answer = inputdlg(prompt,dlg_title,num_lines,def); - edfFile = answer{1}; - fprintf('EDFFile: %s\n', edfFile ); - end - %%%%%%%%%% - % STEP 2 % - %%%%%%%%%% - - % Open a graphics window on the main screen - % using the PsychToolbox's Screen function. - screenNumber=max(Screen('Screens')); - [window, wRect]=Screen('OpenWindow', screenNumber, 0,[],32,2); - Screen(window,'BlendFunction',GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - [winWidth, winHeight] = WindowSize(window); - - % create fixation dot and fixation window rectangles - fixationDot = [-dotSize -dotSize dotSize dotSize]; - fixationDot = CenterRect(fixationDot, wRect); - fixationWindow = [-fixWinSize -fixWinSize fixWinSize fixWinSize]; - fixationWindow = CenterRect(fixationWindow, wRect); - - % make textures - happy = Screen('MakeTexture',window, imread(char(imageList(1)))); - sad = Screen('MakeTexture',window, imread(char(imageList(2)))); - - %%%%%%%%%% - % STEP 3 % - %%%%%%%%%% - - % Provide Eyelink with details about the graphics environment - % and perform some initializations. The information is returned - % in a structure that also contains useful defaults - % and control codes (e.g. tracker state bit and Eyelink key values). - - el=EyelinkInitDefaults(window); - - % We are changing calibration to a black background with white targets, - % no sound and smaller targets - el.backgroundcolour = BlackIndex(el.window); - el.msgfontcolour = WhiteIndex(el.window); - el.imgtitlecolour = WhiteIndex(el.window); - el.targetbeep = 0; - el.calibrationtargetcolour = WhiteIndex(el.window); - - % for lower resolutions you might have to play around with these values - % a little. If you would like to draw larger targets on lower res - % settings please edit PsychEyelinkDispatchCallback.m and see comments - % in the EyelinkDrawCalibrationTarget function - el.calibrationtargetsize= 1; - el.calibrationtargetwidth=0.5; - % call this function for changes to the calibration structure to take - % affect - EyelinkUpdateDefaults(el); - - %%%%%%%%%% - % STEP 4 % - %%%%%%%%%% - - % Initialization of the connection with the Eyelink Gazetracker. - % exit program if this fails. - - if ~EyelinkInit(dummymode) - fprintf('Eyelink Init aborted.\n'); - cleanup; % cleanup function - return; - end - - % open file to record data to - i = Eyelink('Openfile', edfFile); - if i~=0 - fprintf('Cannot create EDF file ''%s'' ', edffilename); - cleanup; - return; - end - - % make sure we're still connected. - if Eyelink('IsConnected')~=1 && ~dummymode - cleanup; - return; - end; - - %%%%%%%%%% - % STEP 5 % - %%%%%%%%%% - - % SET UP TRACKER CONFIGURATION - % Setting the proper recording resolution, proper calibration type, - % as well as the data file content; - Eyelink('command', 'add_file_preamble_text ''Recorded by EyelinkToolbox demo-experiment'''); - - % This command is crucial to map the gaze positions from the tracker to - % screen pixel positions to determine fixation - Eyelink('command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, winWidth-1, winHeight-1); - - Eyelink('message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, winWidth-1, winHeight-1); - % set calibration type. - Eyelink('command', 'calibration_type = HV9'); - Eyelink('command', 'generate_default_targets = YES'); - % set parser (conservative saccade thresholds) - Eyelink('command', 'saccade_velocity_threshold = 35'); - Eyelink('command', 'saccade_acceleration_threshold = 9500'); - % set EDF file contents - % 5.1 retrieve tracker version and tracker software version - [v,vs] = Eyelink('GetTrackerVersion'); - fprintf('Running experiment on a ''%s'' tracker.\n', vs ); - vsn = regexp(vs,'\d','match'); - - if v ==3 && str2double(vsn{1}) == 4 % if EL 1000 and tracker version 4.xx - - % remote mode possible add HTARGET ( head target) - Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); - Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT,HTARGET'); - % set link data (used for gaze cursor) - Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,FIXUPDATE,INPUT'); - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT,HTARGET'); - else - Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); - Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT'); - % set link data (used for gaze cursor) - Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,FIXUPDATE,INPUT'); - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); - end - - % calibration/drift correction target - Eyelink('command', 'button_function 5 "accept_target_fixation"'); - - %%%%%%%%%% - % STEP 6 % - %%%%%%%%%% - - if ~dummymode - % Hide the mouse cursor and setup the eye calibration window - Screen('HideCursorHelper', window); - end - % enter Eyetracker camera setup mode, calibration and validation - EyelinkDoTrackerSetup(el); - - - %%%%%%%%% - % STEP 7% - %%%%%%%%% - - % Now starts running individual trials - % You can keep the rest of the code except for the implementation - % of graphics and event monitoring - % Each trial should have a pair of "StartRecording" and "StopRecording" - % calls as well integration messages to the data file (message to mark - % the time of critical events and the image/interest area/condition - % information for the trial) - trial = 1; - numTrials = 3; - index = 1; - - % repeat until we have 3 sucessful trials - while trial <= numTrials - - % wait a second between trials - WaitSecs(1); - % STEP 7.1 - % Sending a 'TRIALID' message to mark the start of a trial in Data - % Viewer. This is different than the start of recording message - % START that is logged when the trial recording begins. The viewer - % will not parse any messages, events, or samples, that exist in - % the data file prior to this message. - Eyelink('Message', 'TRIALID %d', trial); - % This supplies the title at the bottom of the eyetracker display - Eyelink('command', 'record_status_message "TRIAL %d/%d"', trial,numTrials); - Eyelink('Command', 'set_idle_mode'); - % clear tracker display and draw box at center - Eyelink('Command', 'clear_screen %d', 0); - % draw fixation and fixation window shapes on host PC - Eyelink('command', 'draw_cross %d %d 15', winWidth/2,winHeight/2); - Eyelink('command', 'draw_box %d %d %d %d 15', fixationWindow(1), fixationWindow(2), fixationWindow(3), fixationWindow(4)); - - % STEP 7.2 - % Do a drift correction at the beginning of each trial - % Performing drift correction (checking) is optional for - % EyeLink 1000 eye trackers. Drift correcting at different - % locations x and y depending on where the ball will start - % we change the location of the drift correction to match that of - % the target start position - EyelinkDoDriftCorrection(el); - - % STEP 7.3 - % start recording eye position (preceded by a short pause so that - % the tracker can finish the mode transition) - % The paramerters for the 'StartRecording' call controls the - % file_samples, file_events, link_samples, link_events availability - - Eyelink('Command', 'set_idle_mode'); - WaitSecs(0.05); - Eyelink('StartRecording'); - eye_used = Eyelink('EyeAvailable'); % get eye that's tracked - % returns 0 (LEFT_EYE), 1 (RIGHT_EYE) or 2 (BINOCULAR) depending on what data is - if eye_used == 2 - eye_used = 1; % use the right_eye data - end - % record a few samples before we actually start displaying - % otherwise you may lose a few msec of data - WaitSecs(0.1); - - % STEP 7.4 - % Prepare and show the screen. - Screen('BlendFunction', window, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - Screen('FillRect', window, el.backgroundcolour); - Screen('FillOval', window,round([rand*255 rand*255 rand*255]), fixationDot); - Screen('Flip',window); - Eyelink('Message', 'SYNCTIME'); - - % get screen image from the first display to use as Data Viewer - % trial overlay. Note this call is very slow and will affect your - % timing for the first screen blanking - if firstRun - imageArray = Screen('GetImage', window); - firstRun =0; - end - - % set fixation display to be randomly chose between 650 and 1500 - fixateTime = GetSecs + round(650 + (1500-650).*rand)/1000 + 200/1000; - graceTime = GetSecs + 200/1000; - while GetSecs < fixateTime - - if dummymode==0 - error=Eyelink('CheckRecording'); - if(error~=0) - break; - end - - if Eyelink( 'NewFloatSampleAvailable') > 0 - % get the sample in the form of an event structure - evt = Eyelink( 'NewestFloatSample'); - evt.gx - evt.gy - if eye_used ~= -1 % do we know which eye to use yet? - % if we do, get current gaze position from sample - x = evt.gx(eye_used+1); % +1 as we're accessing MATLAB array - y = evt.gy(eye_used+1); - % do we have valid data and is the pupil visible? - if x~=el.MISSING_DATA && y~=el.MISSING_DATA && evt.pa(eye_used+1)>0 - mx=x; - my=y; - end - end - end - else - - % Query current mouse cursor position (our "pseudo-eyetracker") - - % (mx,my) is our gaze position. - [mx, my]=GetMouse(window); %#ok<*NASGU> - - end - if infixationWindow(mx,my) && ~infix - - Eyelink('Message', 'Fixation Start'); - Beeper(el.calibration_success_beep(1), el.calibration_success_beep(2), el.calibration_success_beep(3)); - infix = 1; - elseif ~infixationWindow(mx,my) && infix && GetSecs > graceTime - - Screen('DrawTexture', window, sad); - Screen('Flip',window); - disp('broke fix'); - Eyelink('Message', 'Fixation broke or grace time ended'); - Beeper(el.calibration_failed_beep(1), el.calibration_failed_beep(2), el.calibration_failed_beep(3)); - infix = 0; - break; - end - - [keyIsDown,secs,keyCode] = KbCheck; %#ok<*ASGLU> - % if spacebar was pressed stop display - if keyCode(stopkey ) - sprintf('Space pressed, exiting trial\n'); - Eyelink('Message', 'Key pressed'); - break; - end - - end - - if infix - Screen('DrawTexture', window, happy); - Screen('Flip',window); - sprintf('Trial completed. Trial %d of %d\n', trial, numTrials); - trial = trial + 1; - WaitSecs(1); - end - - - % STEP 7.5 - % add 100 msec of data to catch final events and blank display - WaitSecs(0.1); - Eyelink('StopRecording'); - - index = index + 1; - - Screen('FillRect', window, el.backgroundcolour); - Screen('Flip', window); - - imwrite(imageArray, 'imgfile.jpg', 'jpg'); - Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', 'imgfile.jpg', winWidth/2, winHeight/2); - - % STEP 7.6 - % Send out necessary integration messages for data analysis - % Send out interest area information for the trial - % See "Protocol for EyeLink Data to Viewer Integration-> Interest - % Area Commands" section of the EyeLink Data Viewer User Manual - % IMPORTANT! Don't send too many messages in a very short period of - % time or the EyeLink tracker may not be ablwWe to write them all - % to the EDF file. - % Consider adding a short delay every few messages. - WaitSecs(0.001); - Eyelink('Message', '!V IAREA ELLIPSE %d %d %d %d %d %s', 1, floor(winWidth/2-dotSize), floor(winHeight/2-dotSize), floor(winWidth/2+dotSize), floor(winHeight/2+dotSize),'center'); - Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 2, floor(winWidth/2-fixWinSize), floor(winHeight/2-fixWinSize), floor(winWidth/2+fixWinSize), floor(winHeight/2+fixWinSize),'centerWin'); - - - - % Send messages to report trial condition information - % Each message may be a pair of trial condition variable and its - % corresponding value follwing the '!V TRIAL_VAR' token message - % See "Protocol for EyeLink Data to Viewer Integration-> Trial - % Message Commands" section of the EyeLink Data Viewer User Manual - WaitSecs(0.001); - Eyelink('Message', '!V TRIAL_VAR index %d', index); - Eyelink('Message', '!V TRIAL_VAR imgfile %s', 'imgfile.jpg'); - if infix - Eyelink('Message', '!V TRIAL_VAR trialOutcome %s', 'succesful'); - else - Eyelink('Message', '!V TRIAL_VAR trialOutcome %s', 'recycled'); - end - % STEP 9 - % Sending a 'TRIAL_RESULT' message to mark the end of a trial in - % Data Viewer. This is different than the end of recording message - % END that is logged when the trial recording ends. The viewer will - % not parse any messages, events, or samples that exist in the data - % file after this message. - Eyelink('Message', 'TRIAL_RESULT 0'); - - end - - %%%%%%%%%% - % STEP 8 % - %%%%%%%%%% - - % End of Experiment; close the file first - % close graphics window, close data file and shut down tracker - Screen('CloseAll'); - Eyelink('Command', 'set_idle_mode'); - WaitSecs(0.5); - Eyelink('CloseFile'); - - % download data file - try - fprintf('Receiving data file ''%s''\n', edfFile ); - status=Eyelink('ReceiveFile'); - if status > 0 - fprintf('ReceiveFile status %d\n', status); - end - if 2==exist(edfFile, 'file') - fprintf('Data file ''%s'' can be found in ''%s''\n', edfFile, pwd ); - end - catch %#ok<*CTCH> - fprintf('Problem receiving data file ''%s''\n', edfFile ); - end - - - %%%%%%%%%% - % STEP 9 % - %%%%%%%%%% - - % run cleanup function (close the eye tracker and window). - cleanup; - -catch - cleanup; - fprintf('%s: some error occured\n', mfilename); - psychrethrow(lasterror); %#ok<*LERR> - -end - - function cleanup - % Shutdown Eyelink: - Eyelink('Shutdown'); - Screen('CloseAll'); - end - - function fix = infixationWindow(mx,my) - % determine if gx and gy are within fixation window - fix = mx > fixationWindow(1) && mx < fixationWindow(3) && ... - my > fixationWindow(2) && my < fixationWindow(4) ; - end -end - - diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyelinkFixationWindow/HappyFace.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyelinkFixationWindow/HappyFace.jpg deleted file mode 100644 index 7d7a2c1cf2..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyelinkFixationWindow/HappyFace.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyelinkFixationWindow/SadFace.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyelinkFixationWindow/SadFace.jpg deleted file mode 100644 index b6e8ab96b8..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyelinkFixationWindow/SadFace.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyelinkFixationWindow/imgfile.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyelinkFixationWindow/imgfile.jpg deleted file mode 100644 index 0252f63352..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/EyelinkFixationWindow/imgfile.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG1A.TIF b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG1A.TIF deleted file mode 100644 index cbb864cf3f..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG1A.TIF and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG1A.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG1A.jpg deleted file mode 100644 index 84e5137cb1..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG1A.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG1B.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG1B.jpg deleted file mode 100644 index 9a23259743..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG1B.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG2A.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG2A.jpg deleted file mode 100644 index cf42c84936..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG2A.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG2B.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG2B.jpg deleted file mode 100644 index 9cbc1b2e10..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/REG2B.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/RND1A.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/RND1A.jpg deleted file mode 100644 index b994757ef6..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/RND1A.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/RND1B.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/RND1B.jpg deleted file mode 100644 index 9dfc099a72..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/RND1B.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/RND2A.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/RND2A.jpg deleted file mode 100644 index 7b9b7aa195..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/RND2A.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/RND2B.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/RND2B.jpg deleted file mode 100644 index 0effcac78c..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/RND2B.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/blank.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/blank.jpg deleted file mode 100644 index ec66eebb1c..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/blank.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/change.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/change.m deleted file mode 100644 index 74d9ebe895..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/change/change.m +++ /dev/null @@ -1,530 +0,0 @@ -function change -% -% ___________________________________________________________________ -% -% Demo implemetation of detecting eye in fixation window on display -% computer through fixation update events from host pc -% -% stimulus: A changing checker board image -% -% This task demonstrates stimuli presentation with Eyelink and Data Viewer -% integration. -% -% NOTE: Please make sure you are in the same directory where the change -% images are located. -% -% Press space to exit change -% ___________________________________________________________________ - -% HISTORY -% mm/dd/yy -% -% 01/28/11 NJ created -% 12/20/13 LJ changed isoctave to IsOctave and EyeLink to Eyelink, case sensitive for the latest matlab -% added parameters for function infixationWindow, in order to run with octave -% fixed issue with non integer arguments for Eyelink('message' ...) -% - -if ~IsOctave - commandwindow; -else - more off; -end - -dummymode = 0; - -% set trial times -TIMER_DISPLAY = 150; -TIMER_BLANK = 50; -TRIAL_TIMEOUT = 60000; - -% images to use and locations of interest areas -% Only 24 and 32 bit images are supported. Octave cares about case -imageListA = {'REG1A.jpg' 'REG2A.jpg' 'RND1A.jpg' 'RND2A.jpg'}; -imageListB = {'REG1B.jpg' 'REG2B.jpg' 'RND1B.jpg' 'RND2B.jpg'}; -trigLocs = [ [149 ; 365] [279;176] [404;365] [404;176]]; - -stopkey=KbName('space'); - -try - - %%%%%%%%% - % STEP 1% - %%%%%%%%% - - % Added a dialog box to set your own EDF file name before opening - % experiment graphics. Make sure the entered EDF file name is 1 to 8 - % characters in length and only numbers or letters are allowed. - if IsOctave - edfFile = 'DEMO'; - else - prompt = {'Enter tracker EDF file name (1 to 8 letters or numbers)'}; - dlg_title = 'Create EDF file'; - num_lines= 1; - def = {'DEMO'}; - answer = inputdlg(prompt,dlg_title,num_lines,def); - edfFile = answer{1}; - fprintf('EDFFile: %s\n', edfFile ); - end - %%%%%%%%%% - % STEP 2 % - %%%%%%%%%% - - % Open a graphics window on the main screen - % using the PsychToolbox's Screen function. - screenNumber=max(Screen('Screens')); - window=Screen('OpenWindow', screenNumber, 0,[],32,2); - Screen(window,'BlendFunction',GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - [winWidth, winHeight] = WindowSize(window); - - %%%%%%%%%% - % STEP 3 % - %%%%%%%%%% - - % Provide Eyelink with details about the graphics environment - % and perform some initializations. The information is returned - % in a structure that also contains useful defaults - % and control codes (e.g. tracker state bit and Eyelink key values). - el=EyelinkInitDefaults(window); - - % We are changing calibration to a black background with white targets, - % no sound and smaller targets - el.backgroundcolour = WhiteIndex(el.window); - el.msgfontcolour = BlackIndex(el.window); - el.targetbeep = 0; - el.calibrationtargetcolour= BlackIndex(el.window); - % for lower resolutions you might have to play around with these values - % a little. If you would like to draw larger targets on lower res - % settings please edit PsychEyelinkDispatchCallback.m and see comments - % in the EyelinkDrawCalibrationTarget function - el.calibrationtargetsize= 1; - el.calibrationtargetwidth=0.5; - % call this function for changes to the calibration structure to take - % affect - EyelinkUpdateDefaults(el); - - %%%%%%%%%% - % STEP 4 % - %%%%%%%%%% - - % Initialization of the connection with the Eyelink tracker - % exit program if this fails. - if ~EyelinkInit(dummymode) - fprintf('Eyelink Init aborted.\n'); - cleanup; % cleanup function - return; - end - - % open file to record data to - res = Eyelink('Openfile', edfFile); - if res~=0 - fprintf('Cannot create EDF file ''%s'' ', edffilename); - cleanup; - return; - end - - % make sure we're still connected. - if Eyelink('IsConnected')~=1 && ~dummymode - cleanup; - return; - end - - %%%%%%%%%% - % STEP 5 % - %%%%%%%%%% - - % SET UP TRACKER CONFIGURATION - % Setting the proper recording resolution, proper calibration type, - % as well as the data file content; - Eyelink('command', 'add_file_preamble_text ''Recorded by EyelinkToolbox demo-experiment'''); - - % This command is crucial to map the gaze positions from the tracker to - % screen pixel positions to determine fixation - Eyelink('command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, winWidth-1, winHeight-1); - - Eyelink('message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, winWidth-1, winHeight-1); - % set calibration type. - Eyelink('command', 'calibration_type = HV9'); - Eyelink('command', 'generate_default_targets = YES'); - % set parser (conservative saccade thresholds) - Eyelink('command', 'saccade_velocity_threshold = 35'); - Eyelink('command', 'saccade_acceleration_threshold = 9500'); - - - % 5.1 retrieve tracker version and tracker software version - [v,vs] = Eyelink('GetTrackerVersion'); - fprintf('Running experiment on a ''%s'' tracker.\n', vs ); - vsn = regexp(vs,'\d','match'); - - % set EDF file contents. Note the FIXUPDATE event for fixation update - if v ==3 && str2double(vsn{1}) == 4 % if EL 1000 and tracker version 4.xx - % remote mode possible add HTARGET ( head target) - Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); - Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT,HTARGET'); - % set link data (used for gaze cursor) - Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT,FIXUPDATE'); - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT,HTARGET'); - else - Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT,FIXUPDATE'); - Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT'); - % set link data (used for gaze cursor) - Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT,FIXUPDATE '); - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); - end - - % allow to use the big button on the eyelink gamepad to accept the - % calibration/drift correction target - Eyelink('command', 'button_function 5 "accept_target_fixation"'); - - % Tell the Eyelink to send a fixation update every 50 ms - Eyelink('command', 'fixation_update_interval = %d', 50); - Eyelink('command', 'fixation_update_accumulate = %d', 50); - - %%%%%%%%%% - % STEP 6 % - %%%%%%%%%% - - if ~dummymode - % Hide the mouse cursor and Calibrate the eye tracker - Screen('HideCursorHelper', window); - end - - % enter Eyetracker camera setup mode, calibration and validation - EyelinkDoTrackerSetup(el); - - %%%%%%%%%% - % STEP 7 % - %%%%%%%%%% - - % Now starts running individual trials - % You can keep the rest of the code except for the implementation - % of graphics and event monitoring - % Each trial should have a pair of "StartRecording" and "StopRecording" - % calls as well integration messages to the data file (message to mark - % the time of critical events and the image/interest area/condition - % information for the trial) - - for i = 1 : 4 - stopKeyPressed = 0; - % select the two images to be displayed in the trial - imgfileA = char(imageListA(i)); - imgfileB = char(imageListB(i)); - - % STEP 7.1 - % Sending a 'TRIALID' message to mark the start of a trial in Data - % Viewer. This is different than the start of recording message - % START that is logged when the trial recording begins. The viewer - % will not parse any messages, events, or samples, that exist in - % the data file prior to this message. - Eyelink('Message', 'TRIALID %d', i); - % This supplies the title at the bottom of the eyetracker display - Eyelink('command', 'record_status_message "* TRIAL %d/%d %s"', i,4, imgfileA); - % Before recording, we place reference graphics on the host display - % Must be in offline mode to transfer image to Host PC - Eyelink('Command', 'set_idle_mode'); - % clear tracker display and draw box at center - Eyelink('Command', 'clear_screen %d', 0); - - % pause between trials - WaitSecs(0.1); - - % transfer image A to host pc - imgArray = imread(imgfileA); - finfo = imfinfo(imgfileA); - transferStatus = Eyelink('ImageTransfer', finfo.Filename ,0,0,0,0,round(winWidth/2 - finfo.Width/2) ,round(winHeight/2 - finfo.Height/2),4); - if transferStatus ~= 0 - fprintf('Image to host transfer failed\n'); - end - % give image transfer time to finish - WaitSecs(0.1); - - % find interest area locations for fixation window - trigLocs(1,i) = trigLocs(1,i) + (winWidth/2 - finfo.Width/2); - trigLocs(2,i) = trigLocs(2,i) + (winHeight/2 - finfo.Height/2); - fixationWindow = [trigLocs(1,i) trigLocs(2,i) trigLocs(1,i)+64 trigLocs(2,i)+64]; - % draw filled box after image transfer so it is drawn on top of - % image on Host PC - Eyelink('command', 'draw_filled_box %d %d %d %d 2', fixationWindow(1), fixationWindow(2), fixationWindow(3), fixationWindow(4)); - - % STEP 7.2 - % Do a drift correction at the beginning of each trial - % Performing drift correction (checking) is optional for - % EyeLink 1000 eye trackers. - EyelinkDoDriftCorrection(el); - - % STEP 7.3 - % start recording eye position (preceded by a short pause so that - % the tracker can finish the mode transition) - % The paramerters for the 'StartRecording' call controls the - % file_samples, file_events, link_samples, link_events availability - Eyelink('Command', 'set_idle_mode'); - WaitSecs(0.05); - % we are disabling samples transfered to the display but we're still - % getting events. This speeds things up a little since in this - % example we do not care about samples but about events from the - % tracker - Eyelink('StartRecording',1,1,0,1); - - % record a few samples before we actually start displaying - % otherwise you may lose a few msec of data - WaitSecs(0.1); - - % get eye that's tracked - eye_used = Eyelink('EyeAvailable'); - % returns 0 (LEFT_EYE), 1 (RIGHT_EYE) or 2 (BINOCULAR) depending on what data is - if eye_used == 2 - eye_used = 1; % use the right_eye data - end - - - % STEP 7.4 - % Prepare and show the screen. - Screen('FillRect', window, el.backgroundcolour); - Screen('Flip', window); - % create trial textures - tex(1) = Screen('MakeTexture', window,imgArray); - imgArray = imread(imgfileB); - tex(2) = Screen('MakeTexture', window,imgArray); - - % variables to determine what to display in each iteration - counter = 0; - counter2 = 0; - correct = 0; - trialtime = GetSecs + TRIAL_TIMEOUT/1000; - totalFixTime = 0; - mouseTimer = GetSecs; - - - % loop until either trial time over or subject fixates changing - % square - while GetSecs < trialtime - - if correct - break; - end - - display = mod(counter,2) + 1; - if display < 2 - tex2show = mod(counter2, 2) + 1; - counter2 = counter2 + 1; - displayTimer = GetSecs + TIMER_DISPLAY/1000; - else - displayTimer = GetSecs + TIMER_BLANK/1000; - end - - - while GetSecs < displayTimer - - [keyIsDown,secs,keyCode] = KbCheck; %#ok<*ASGLU> - % if spacebar was pressed stop display - if keyCode(stopkey ) - sprintf('Space pressed, exiting trial\n'); - Eyelink('Message', 'Stop Key pressed'); - stopKeyPressed = 1; - break; - end - - - if display < 2 - % display - Screen('FillRect', window, el.backgroundcolour); - Screen('DrawTexture', window, tex(tex2show)); - Screen('Flip',window); - % send a msg to host with display 1 or 2 ( A or B) - Eyelink('Message', 'DISPLAY %d', tex2show); - - else - % blank screen - Screen('FillRect', window, el.backgroundcolour); - Screen('Flip',window); - Eyelink('Message', 'BLANK_SCREEN'); - end - - if dummymode==0 - error=Eyelink('CheckRecording'); - if(error~=0) - disp('Error in Recording'); - break; - end - % we need to loop over this a few times ( 30 is - % randomly chosen) so that we do not miss any events - % and to prevent any buffer overflow - for j=1:30 - evtype = Eyelink('GetNextDataType'); - if evtype == el.FIXUPDATE - if Eyelink('isconnected') == el.connected % if we're really measuring eye-movements - evt = Eyelink('getfloatdata', evtype);% get data - - % only process if its the desired eye - if evt.eye == eye_used - % send msg with details of fixation - % update event - Eyelink('message', 'Fixupdate: avg_x %d, y %d, dur %d',floor(evt.gavx), floor(evt.gavy), floor(evt.entime)-floor(evt.sttime)); - - % determine if gaze values are within - % interest region and if gaze has been - % maintained over 300 ms. This method - % allows for saccades as long as they - % are withing interest area - if infixationWindow(fixationWindow, evt.gavx,evt.gavy) - - totalFixTime = totalFixTime + 50; - if totalFixTime >= 300 - break; - end - else % broke fixation reset time - totalFixTime = 0; - end - end - else - disp('Eyelink disconnected!'); - end - end - end %end for - else - % using display PC mouse - [x,y] = GetMouse(window); %#ok<*NASGU> - evt.type=el.FIXUPDATE; - evt.gavx=x; - evt.gavy=y; - - if infixationWindow(fixationWindow, evt.gavx,evt.gavy) - - if GetSecs - mouseTimer >= 0.300 - disp('in fixation window'); - correct = 1; - break; - end - else % reset - mouseTimer = GetSecs; - end - end - - % has the subject completed 300 ms of fixation within iA? - if totalFixTime >= 300 - Beeper(el.calibration_success_beep(1), el.calibration_success_beep(2), el.calibration_success_beep(3)); - correct = 1; - break; - end - end - - - - if stopKeyPressed - break; - end - - counter = counter + 1; - - [keyIsDown,secs,keyCode] = KbCheck; - % if spacebar was pressed stop display - if keyCode(stopkey ) - sprintf('Space pressed, exiting trial\n'); - Eyelink('Message', 'Key pressed'); - break; - end - end - - - % STEP 7.6 - % add 100 msec of data to catch final events and blank display - WaitSecs(0.1); - Eyelink('StopRecording'); - Screen('FillRect', window, el.backgroundcolour); - Screen('Flip', window); - - % did the trial time out without user fixating? send a message to - % edf - if GetSecs > trialtime - disp('Trial time out'); - Eyelink('Message', 'Trial time out'); - end - - % STEP 7.7 - % Send out necessary integration messages for data analysis - % See "Protocol for EyeLink Data to Viewer Integration-> Interest - % Area Commands" section of the EyeLink Data Viewer User Manual - % IMPORTANT! Don't send too many messages in a very short period of - % time or the EyeLink tracker may not be able to write them all - % to the EDF file. - % Consider adding a short delay every few messages. - WaitSecs(0.001); - % Send an integration message so that an image can be loaded as - % overlay backgound when performing Data Viewer analysis. This - % message can be placed anywhere within the scope of a trial (i.e., - % after the 'TRIALID' message and before 'TRIAL_RESULT') - % See "Protocol for EyeLink Data to Viewer Integration -> Image - % Commands" section of the EyeLink Data Viewer User Manual. - Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', imgfileA, winWidth/2, winHeight/2); - % Send out interest area information for the trial - Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 2, fixationWindow(1), fixationWindow(2), fixationWindow(3), fixationWindow(4),'change'); - % Send messages to report trial condition information - % Each message may be a pair of trial condition variable and its - % corresponding value follwing the '!V TRIAL_VAR' token message - % See "Protocol for EyeLink Data to Viewer Integration-> Trial - % Message Commands" section of the EyeLink Data Viewer User Manual - WaitSecs(0.001); - Eyelink('Message', '!V TRIAL_VAR index %d', i); - Eyelink('Message', '!V TRIAL_VAR imgfile %s', imgfileA); - % STEP 7.8 - % Sending a 'TRIAL_RESULT' message to mark the end of a trial in - % Data Viewer. This is different than the end of recording message - % END that is logged when the trial recording ends. The viewer will - % not parse any messages, events, or samples that exist in the data - % file after this message. - Eyelink('Message', 'TRIAL_RESULT 0'); - - end - - %%%%%%%%%% - % STEP 8 % - %%%%%%%%%% - - % End of Experiment; close the file first - % close graphics window, close data file and shut down tracker - Eyelink('Command', 'set_idle_mode'); - WaitSecs(0.5); - Eyelink('CloseFile'); - - try - fprintf('Receiving data file ''%s''\n', edfFile ); - status=Eyelink('ReceiveFile'); - if status > 0 - fprintf('ReceiveFile status %d\n', status); - end - if 2==exist(edfFile, 'file') - fprintf('Data file ''%s'' can be found in ''%s''\n', edfFile, pwd ); - end - catch - fprintf('Problem receiving data file ''%s''\n', edfFile ); - end - - %%%%%%%%%% - % STEP 9 % - %%%%%%%%%% - - % run cleanup function (close the eye tracker and window). - cleanup; - -catch %#ok<*CTCH> - cleanup; - fprintf('%s: some error occured\n', mfilename); - psychrethrow(lasterror); %#ok - -end - - function cleanup - % Shutdown Eyelink: - Eyelink('Shutdown'); - Screen('CloseAll'); - end - - function fix = infixationWindow(fixationWindow, mx,my) - - % determine if gx and gy are within fixation window - fix = mx > fixationWindow(1) && mx < fixationWindow(3) && ... - my > fixationWindow(2) && my < fixationWindow(4) ; - end - - -end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/pursuit/pursuit.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/pursuit/pursuit.m deleted file mode 100644 index 225495cede..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/pursuit/pursuit.m +++ /dev/null @@ -1,378 +0,0 @@ -function pursuit -% -% ___________________________________________________________________ -% -% Demo implemetation of pursuit task -% stimulus: A target moving to a sinusoidal projection -% This task demonstrates stimuli presentation with Eyelink and Data Viewer -% integration. -% -% ___________________________________________________________________ - -% HISTORY -% mm/dd/yy -% -% 01/28/11 NJ created -% 12/20/13 LJ changed isoctave to IsOctave, case sensitive for the latest matlab -% fixed issue with non integer arguments for Eyelink('message' ...) and Eyelink('command' ...) -% - - - -% trial defaults -type = {'Horizontal' 'Horizontal' 'Vertical' 'Vertical' 'Elliptic' 'Elliptic'}; - -% freq_x freq_y phase_x phase_y dc_x dc_y -trials = [[0.2;0;270;0] [0.5;0;90;0] [0;0.2;0;0] [0;0.3;0;180] ... - [0.2;0.2;270;180] [0.3;0.3;270;0]]; - - -TRIAL_TIMER = 10000; - -if ~IsOctave - commandwindow; -else - more off; -end - - - -dummymode = 0; - -try - %%%%%%%%%% - % STEP 1 % - %%%%%%%%%% - - % Added a dialog box to set your own EDF file name before opening - % experiment graphics. Make sure the entered EDF file name is 1 to 8 - % characters in length and only numbers or letters are allowed. - - if IsOctave - edfFile = 'DEMO'; - else - prompt = {'Enter tracker EDF file name (1 to 8 letters or numbers)'}; - dlg_title = 'Create EDF file'; - num_lines= 1; - def = {'DEMO'}; - answer = inputdlg(prompt,dlg_title,num_lines,def); - edfFile = answer{1}; - fprintf('EDFFile: %s\n', edfFile ); - end - - %%%%%%%%%% - % STEP 2 % - %%%%%%%%%% - - % Open a graphics window on the main screen - % using the PsychToolbox's Screen function. - screenNumber=max(Screen('Screens')); - [window, wRect]=Screen('OpenWindow', screenNumber, 0,[],32,2); %#ok<*NASGU> - Screen(window,'BlendFunction',GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - [winWidth, winHeight] = WindowSize(window); - - % define sine function - sine_plot_x = winWidth/2; - sine_plot_y = winHeight/2; - amplitudeX = winWidth/3; - amplitudeY = winHeight/3; - - %%%%%%%%%% - % STEP 3 % - %%%%%%%%%% - - % Provide Eyelink with details about the graphics environment - % and perform some initializations. The information is returned - % in a structure that also contains useful defaults - % and control codes (e.g. tracker state bit and Eyelink key values). - - el=EyelinkInitDefaults(window); - - % We are changing calibration to match task background and target - % this eliminates affects of changes in luminosity between screens - % no sound and smaller targets - el.targetbeep = 0; - el.backgroundcolour = WhiteIndex(el.window); - el.calibrationtargetcolour= [255 0 0]; - % for lower resolutions you might have to play around with these values - % a little. If you would like to draw larger targets on lower res - % settings please edit PsychEyelinkDispatchCallback.m and see comments - % in the EyelinkDrawCalibrationTarget function - el.calibrationtargetsize= 1; - el.calibrationtargetwidth=0.5; - % call this function for changes to the el calibration structure to take - % affect - EyelinkUpdateDefaults(el); - - %%%%%%%%%% - % STEP 4 % - %%%%%%%%%% - - % Initialization of the connection with the Eyelink tracker - % exit program if this fails. - - if ~EyelinkInit(dummymode) - fprintf('Eyelink Init aborted.\n'); - cleanup; % cleanup function - return; - end - - % open file to record data to - res = Eyelink('Openfile', edfFile); - if res~=0 - fprintf('Cannot create EDF file ''%s'' ', edffilename); - cleanup; - return; - end - - % make sure we're still connected. - if Eyelink('IsConnected')~=1 && ~dummymode - cleanup; - return; - end - - %%%%%%%%%% - % STEP 5 % - %%%%%%%%%% - - % SET UP TRACKER CONFIGURATION - - Eyelink('command', 'add_file_preamble_text ''Recorded by EyelinkToolbox demo-experiment'''); - % Setting the proper recording resolution, proper calibration type, - % as well as the data file content; - - % This command is crucial to map the gaze positions from the tracker to - % screen pixel positions to determine fixation - Eyelink('command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, winWidth-1, winHeight-1); - Eyelink('message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, winWidth-1, winHeight-1); - % set calibration type. - Eyelink('command', 'calibration_type = HV9'); - Eyelink('command', 'generate_default_targets = YES'); - - % STEP 5.1 retrieve tracker version and tracker software version - [v,vs] = Eyelink('GetTrackerVersion'); - fprintf('Running experiment on a ''%s'' tracker.\n', vs ); - vsn = regexp(vs,'\d','match'); - - if v == 3 && str2double(vsn{1}) == 4 % if EL 1000 and tracker version 4.xx - - % remote mode possible add HTARGET ( head target) - Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); - Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT,HTARGET'); - % set link data (used for gaze cursor) - Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,FIXUPDATE,INPUT'); - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT,HTARGET'); - else - Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); - Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT'); - % set link data (used for gaze cursor) - Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,FIXUPDATE,INPUT'); - Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); - end - - % allow to use the big button on the eyelink gamepad to accept the - % calibration/drift correction target - Eyelink('command', 'button_function 5 "accept_target_fixation"'); - - - %%%%%%%%%% - % STEP 6 % - %%%%%%%%%% - - % Hide the mouse cursor - Screen('HideCursorHelper', window); - % enter Eyetracker camera setup mode, calibration and validation - EyelinkDoTrackerSetup(el); - - %%%%%%%%%% - % STEP 7 % - %%%%%%%%%% - - % Now starts running individual trials - % You can keep the rest of the code except for the implementation - % of graphics and event monitoring - % Each trial should have a pair of "StartRecording" and "StopRecording" - % calls as well integration messages to the data file (message to mark - % the time of critical events and the image/interest area/condition - % information for the trial) - - for i = 1 : 6 - - % STEP 7.1 - % Sending a 'TRIALID' message to mark the start of a trial in Data - % Viewer. This is different than the start of recording message - % START that is logged when the trial recording begins. The viewer - % will not parse any messages, events, or samples, that exist in - % the data file prior to this message. - Eyelink('Message', 'TRIALID %d', i); - - % This supplies the title at the bottom of the eyetracker display - Eyelink('command', 'record_status_message "TRIAL %d/%d %s"', i,6, char(type(i))); - % Before recording, we place reference graphics on the host display - % Must be in offline mode to transfer image to Host PC - Eyelink('Command', 'set_idle_mode'); - % clear tracker display and draw box at center - Eyelink('Command', 'clear_screen %d', 0); - - % calculate locations of target peripheries so that we can draw - % matching lines and boxes on host pc - Eyelink('command', 'draw_filled_box %d %d %d %d 2' ,floor(winWidth/2-amplitudeX)-20, floor(winHeight/2-20), floor(winWidth/2-amplitudeX)+20, floor(winHeight/2+20)); - Eyelink('command', 'draw_line %d %d %d %d 2' ,floor(winWidth/2-amplitudeX), floor(winHeight/2), floor(winWidth/2+amplitudeX), floor(winHeight/2)); - Eyelink('command', 'draw_filled_box %d %d %d %d 2' ,floor(winWidth/2+amplitudeX)-20, floor(winHeight/2-20), floor(winWidth/2+amplitudeX)+20, floor(winHeight/2+20)); - Eyelink('command', 'draw_filled_box %d %d %d %d 2' ,floor(winWidth/2-20), floor((winHeight/2-amplitudeY)-20), floor(winWidth/2+20), floor(winHeight/2-amplitudeY)+20); - Eyelink('command', 'draw_line %d %d %d %d 2' ,floor(winWidth/2), floor(winHeight/2-amplitudeY), floor(winWidth/2), floor(winHeight/2+amplitudeY)); - Eyelink('command', 'draw_filled_box %d %d %d %d 2' ,floor(winWidth/2-20), floor(winHeight/2+amplitudeY)-20, floor(winWidth/2+20), floor(winHeight/2+amplitudeY)+20); - - phaseX = (trials(3,i)/360 + ( (0)) * trials(1,i)); - phaseY = (trials(4,i)/360 + ( (0)) * trials(2,i)); - x = sine_plot_x + amplitudeX* sin(phaseX*2*pi); - y = sine_plot_y + amplitudeY* sin(phaseY*2*pi); - ball([1 3]) = [x-10 x+10]; - ball([2 4]) = [y-10 y+10]; - - WaitSecs(0.1); - % STEP 7.2 - % Do a drift correction at the beginning of each trial - % Performing drift correction (checking) is optional for - % EyeLink 1000 eye trackers. Drift correcting at different - % locations x and y depending on where the ball will start - % we change the location of the drift correction to match that of - % the target start position - % Note drift correction does not accept fractionals in PTB! - EyelinkDoDriftCorrection(el,round(x),round(y)); - - % STEP 7.3 - % start recording eye position (preceded by a short pause so that - % the tracker can finish the mode transition) - % The paramerters for the 'StartRecording' call controls the - % file_samples, file_events, link_samples, link_events availability - Eyelink('Command', 'set_idle_mode'); - WaitSecs(0.05); - Eyelink('StartRecording'); - % record a few samples before we actually start displaying - % otherwise you may lose a few msec of data - WaitSecs(0.1); - - % get eye that's tracked - eye_used = Eyelink('EyeAvailable'); - - trialTime = GetSecs + TRIAL_TIMER/1000; - sttime = GetSecs; - while GetSecs < trialTime - - % STEP 7.4 - % Prepare and show the screen. - % Enable alpha blending with proper blend-function. We need it - % for drawing of smoothed points: - Screen('BlendFunction', window, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - Screen('FillRect', window, el.backgroundcolour); - Screen('FillOval', window,[255 0 0], ball); - Screen('Flip', window); - Eyelink('Message', 'SYNCTIME'); - % STEP 7.5 - % send the location of the target at each iteration so that - % target can be displayed in Dataviewer - Eyelink('message', '!V TARGET_POS TARG1 (%d, %d) 1 0',floor(x),floor(y)); - - - - phaseX = (trials(3,i)/360 + ( (GetSecs-sttime)) * trials(1,i)); - phaseY = (trials(4,i)/360 + ( (GetSecs-sttime)) * trials(2,i)); - - x = sine_plot_x + amplitudeX* sin(phaseX*2*pi); - y = sine_plot_y + amplitudeY* sin(phaseY*2*pi); - - ball([1 3]) = [x-10 x+10]; - ball([2 4]) = [y-10 y+10]; - end - - % STEP 7.6 - % add 100 msec of data to catch final events and blank display - WaitSecs(0.1); - Eyelink('StopRecording'); - - Screen('FillRect', window, el.backgroundcolour); - Screen('Flip', window); - - % STEP 7.7 - % Send out necessary integration messages for data analysis - % See "Protocol for EyeLink Data to Viewer Integration-> Interest - % Area Commands" section of the EyeLink Data Viewer User Manual - % IMPORTANT! Don't send too many messages in a very short period of - % time or the EyeLink tracker may not be able to write them all - % to the EDF file. - % Consider adding a short delay every few messages. - WaitSecs(0.001); - % Send messages to report trial condition information - % Each message may be a pair of trial condition variable and its - % corresponding value follwing the '!V TRIAL_VAR' token message - % See "Protocol for EyeLink Data to Viewer Integration-> Trial - % Message Commands" section of the EyeLink Data Viewer User Manual - WaitSecs(0.001); - - - Eyelink('Message', '!V TRIAL_VAR index %d', i); - - % a limitation of the currect ETB only accepts ints as input to - % messages and commands a possible work around is given below - - - msg1 = sprintf('!V TRIAL_VAR freq_x %2.3f ', trials(1,i)); - msg2 = sprintf('!V TRIAL_VAR freq_y %2.3f ', trials(2,i)); - Eyelink('Message', msg1); - Eyelink('Message', msg2); - - % STEP 7.8 - % Sending a 'TRIAL_RESULT' message to mark the end of a trial in - % Data Viewer. This is different than the end of recording message - % END that is logged when the trial recording ends. The viewer will - % not parse any messages, events, or samples that exist in the data - % file after this message. - Eyelink('Message', 'TRIAL_RESULT 0'); - - end - - %%%%%%%%%% - % STEP 8 % - %%%%%%%%%% - - % End of Experiment; close the file first - % close graphics window, close data file and shut down tracker - Eyelink('Command', 'set_idle_mode'); - WaitSecs(0.5); - Eyelink('CloseFile'); - - try - fprintf('Receiving data file ''%s''\n', edfFile ); - status=Eyelink('ReceiveFile'); - if status > 0 - fprintf('ReceiveFile status %d\n', status); - end - if 2==exist(edfFile, 'file') - fprintf('Data file ''%s'' can be found in ''%s''\n', edfFile, pwd ); - end - catch %#ok<*CTCH> - fprintf('Problem receiving data file ''%s''\n', edfFile ); - end - - %%%%%%%%%% - % STEP 9 % - %%%%%%%%%% - - % run cleanup function (close the eye tracker and window). - cleanup; - -catch - cleanup; - fprintf('%s: some error occured\n', mfilename); - psychrethrow(lasterror); %#ok<*LERR> - -end - - function cleanup - % Shutdown Eyelink: - Eyelink('Shutdown'); - Screen('CloseAll'); - end - -end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/town_blur.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/town_blur.jpg deleted file mode 100644 index 7760c423d4..0000000000 Binary files a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/town_blur.jpg and /dev/null differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/Contents.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/Contents.m new file mode 100644 index 0000000000..69b43e558d --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/Contents.m @@ -0,0 +1,166 @@ +% EyelinkToolbox:EyelinkDemos:SR-ResearchDemos +% +% EyeLink systems allow for rich integration between a Display PC (the computer running Psychtoolbox) and the EyeLink Host PC via an ethernet connection using +% the EyeLink application programming interface (API). The API can be installed by downloading the EyeLink Developers Kit from our Support site: +% +% EyeLink Developers Kit / API Downloads (Windows, macOS, Linux) +% +% You will also need to configure your Display PC network settings. See this link for more details: +% +% How to configure a Display PC +% +% Psychtoolbox interfaces with the EyeLink API via the 'Eyelink Toolbox' which can be found in the Psychtoolbox root folder > PsychHardware. +% You can add EyeLink integration code to your own Psychtoolbox script to interact with the Host PC so your experiment can: +% - initialize an EyeLink connection +% - open an EyeLink data file (edf) on the Host PC and name it +% - set some EyeLink-specific parameters /change various default options +% - put Host PC in 'Camera Setup' mode and allow for: +% - transfer of eye image to Display PC for ease of participant setup +% - presentation of targets on Display PC monitor for participant calibration / validation +% - transfer text, image and/or other graphics to the Host PC at the beginning of each trial for experimenter feedback +% - do a drift-check / correction +% - start recording eye movement data at the beginning of each trial (or block) +% - during a trial write messages in the edf file to mark time of events +% - write messages in the edf file for rich integration with Data Viewer +% - stop recording at the end of each trial (or block) +% - towards the end of a session close the edf and transfer a copy to the Display PC +% - close the EyeLink connection +% +% EyeLink integration also allows for online access of eye movement data for gaze-contingent experiments. +% +% In a typical task eye movements are recorded onto the EyeLink Host PC on a trial-by-trial basis: recording starts at the beginning of a trial +% and ends at the end of a trial. This allows for an optional drift-check/correction and for transferring of images to the Host PC between trials. +% However this trial-based recording might not be suitable for paradigms that require a fixed ITI. In such cases it is possible to record +% continuously throughout a block or session. +% +% The following is a list of Psychtoolbox demos with EyeLink integration that are included with the EyeLink Toolbox: +% +% SimplePicture - Eyelink_SimplePicture.m +% +% A simple EyeLink integration demo that records eye movements while an image is presented on the screen. Each trial ends when the +% space bar or a button is pressed. +% +% Illustrates how to: +% - use a non-default IP address for the Host PC +% - replace the default calibration/validation/drift correct bull's eye target with an image (target recommended by Thaler et al., 2013) +% - set calibration target and feedback beep sounds on / off +% - change edf file name when a copy is transferred to the Display PC +% +% GazeContingent - Accessing sample and event gaze data online in real-time over the Ethernet link +% +% Basic EyeLink integration involves saving eye movement data onto the EyeLink Host PC and transferring a copy of the EyeLink data file to the Display PC +% at the end of the session. +% EyeLink eye movement data consists of SAMPLES (eye position recorded at every sample - based on tracker sampling rate) +% and EVENTS (saccades, fixations, blinks detected by the EyeLink online parser) +% However it is also possible to have Psychtoolbox access eye movement data online for gaze-contingent tasks while still saving data onto the Host PC +% +% There are two ways of accessing eye data online: +% +% (1) Accessing buffered data by looping through the function pair Eyelink('GetNextDataType') / Eyelink('GetFloatData') to access buffered EVENTS and SAMPLES +% Use buffered data if you need to: +% a) grab every single consecutive SAMPLE online without missing any +% b) grab EVENT data (fixation/saccade/blink events) online +% +% Note that buffered event data can take some time to be available online due to the time involved +% in calculating velocity/acceleration. If you need to retrieve online gaze +% position as fast as possible and/or you don't need to get all available SAMPLES or other +% EVENTS, then use option (2) +% +% When using buffered data it is advisable to initially loop through the function pair for ~100ms to clear +% old data from the buffer before using it, thus allowing access to the most recent EVENTS or SAMPLES +% +% Demos using buffered data: +% +% BufferedEndSacEvents - Eyelink_BufferedEndSacEvents.m +% A simple EyeLink gaze-contingent demo showing how to retrieve online EVENTS from a buffer. +% In each trial an image is presented with a red gaze-contingent dot overlaid on top. +% The dot's location is updated online based on the end x y coordinates of each saccade detected. +% Each trial ends when the space bar is pressed +% +% BufferedFixUpdateEvents - Eyelink_BufferedFixUpdateEvents.m +% A simple EyeLink gaze-contingent demo showing how to retrieve online events from a buffer. +% In each trial an image is presented with a red gaze-contingent dot overlaid on top. +% The dot's location is based on the average x y coordinates of fixations updated online every 50ms via a FIXUPDATE event. +% See EyeLink Programmers Guide manual > Experiment Templates Overview > Control > Fixation Update Events +% Each trial ends when the space bar is pressed. +% +% FIXUPDATE events send updates about a current fixation at regular intervals. By default an interval of 50ms is used. +% The first update is sent one update interval after the start of the fixation, and the last is sent at +% the end of the fixation. This demo uses FIXUPDATE events to get the averaged gaze x y position across each fixation interval. +% +% FixWindowBufferedSamples - Eyelink_FixWindowBufferedSamples.m +% EyeLink gaze-contingent demo that shows how to retrieve online gaze SAMPLES from a buffer. +% In each trial central crosshairs are shown until gaze is detected continuously within a central +% square window for 500ms or until the space bar is pressed. An image is +% then presented until the space bar is pressed to end the trial. +% +% (2) Fast access of SAMPLES by looping through the function pair Eyelink('NewFloatSampleAvailable') / Eyelink('NewestFloatSample') to access the most +% recent SAMPLES online. +% Use this option if you need to retrieve online eye position (e.g. GAZE data) as fast as possible. +% This option may not necessarily retrieve every single consecutive SAMPLE (this depends on how fast your loop is executed by the Display PC) +% and this option does not allow for EVENT checking. +% +% Demos using fast samples (option 2 above): +% +% GCfastSamples - Eyelink_GCfastSamples.m +% A simple EyeLink gaze-contingent demo showing how to retrieve fast online SAMPLES. +% In each trial an image is presented with a red gaze-contingent dot overlaid on top. +% The dot's location is updated online based on the x y coordinates of the latest gaze SAMPLE retrieved online. +% Each trial ends when the space bar is pressed. +% +% FixWindowFastSamples - Eyelink_FixWindowFastSamples.m +% EyeLink gaze-contingent demo showing how to retrieve fast gaze samples online. +% In each trial central crosshairs are shown until gaze is detected continuously within a central +% square window for 500ms or until the space bar is pressed. An image is +% then presented until the space bar is pressed to end the trial. +% +% +% SimpleVideo - Eyelink_SimpleVideo.m +% +% Simple video demo with EyeLink integration and animated calibration / drift-check/correction targets. +% In each trial eye movements are recorded while a video stimulus is presented on the screen. +% Each trial ends when the space bar is pressed or video stops playing. A different drift-check/correction +% animated target is used in each of the 3 trials. +% +% Illustrates how to send messages to allow for a video file to be played back in Data Viewer's 'Trial Play Back Animation' view. +% Shows how to use animated targets: +% - replacing the default calibration/validation/drift-check (or drift-correction) targets with a video file +% - updating the drift-check/correction video file on a trial-by-trial basis +% +% +% StereoPicture - Eyelink_StereoPicture.m +% +% EyeLink integration demo for stereo presentation. +% Records eye movements passively while presenting a stereo stimulus. Supports both split-screen mode +% and dual-monitor setup. +% Each trial ends when the space bar is pressed. +% Data Viewer integration with both left and right eyes superimposed on the same eye window view. +% Illustrates how to calibrate on both sides of a split-screen or both monitors in dual monitor setup. +% +% MRI_BlockRecord - Eyelink_MRI_BlockRecord.m +% +% Simple MRI demo with EyeLink integration. +% 6 trials are presented in 2 blocks of 3 trials. Trial duration is 5.5s during which a 4s stimulus is presented. +% A block starts with a drift-check followed by presentation of central crosshairs. Eye movements are recorded while +% waiting for an MRI trigger (keyboard key 't' in this demo). The stimulus is presented when the trigger is received. +% A fixed ITI is maintained by presenting crosshairs between each 4s stimulus. Eye movements are recorded throughout +% an entire block rather than on a trial-by-trial basis. +% +% Illustrates how to: +% - shrink the spread of the calibration/validation targets so they are all visible if the MRI bore blocks part of the screen +% - apply an optional online drift correction (see EyeLink 1000 Plus User Manual section 3.11.2) +% +% PursuitTarget - Eyelink_PursuitTarget.m +% +% A smooth pursuit EyeLink integration demo that records eye movements +% while a target moves sinusoidally across the screen. Each trial ends after 5s. +% +% Illustrates how to: +% - change the drift-check/correction target location before each trial +% - create a moving target for Data Viewer's Play Back Animation view +% - create dynamic target location for Data Viewer's Temporal Graph view and sample reports +% - create target dynamic interest areas for Data Viewer +% +% +% Thaler L, Schutz AC, Goodale MA, Gegenfurtner KR. What is the best fixation target? The effect of target shape on stability of fixational eye movements. Vision Res. 2013; 76: 31–42. +% \ No newline at end of file diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/Contents.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/Contents.m new file mode 100644 index 0000000000..feab2b2d89 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/Contents.m @@ -0,0 +1,12 @@ +% EyelinkToolbox:EyelinkDemos:SR-ResearchDemos:GazeContingent +% +% Demos provided by SR-Research to demonstrate a few different ways +% of implementing gaze contingent functionality +% +% FixWindowBufferedSamples - Fixation window using online gaze samples from a buffer +% FixWindowFastSamples - Fixation window using fast gaze samples online +% GCFastSamples - Gaze contingent drawing using fast samples online +% GCBufferedEvents - Gaze contingent drawing using two types of parsed event data +% +% See also SR-RESEARCHDEMOS +% diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowBufferedSamples/EyeLink_FixWindowBufferedSamples.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowBufferedSamples/EyeLink_FixWindowBufferedSamples.m new file mode 100644 index 0000000000..5b4f33052c --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowBufferedSamples/EyeLink_FixWindowBufferedSamples.m @@ -0,0 +1,557 @@ +function EyeLink_FixWindowBufferedSamples(screenNumber) +% EyeLink gaze-contingent demo that shows how to retrieve online gaze samples from a buffer. +% In each trial central crosshairs are shown until gaze is detected continuously within a central +% square window for 500ms or until the space bar is pressed. An image is +% then presented until the space bar is pressed to end the trial. +% +% Usage: +% Eyelink_FixWindowBufferedSamples(screenNumber) +% +% screenNumber is an optional parameter which can be used to pass a specific value to PsychImaging('OpenWindow', ...) +% If screenNumber is not specified, or if isempty(screenNumber) then the default: +% screenNumber = max(Screen('Screens')); +% will be used. +% +% This demo uses the 'GetNextDataType'/'GetFloatData' function pair that allows access to the following buffered samples and events +% (See EyeLink Programmers Guide manual > Data Structures > FEVENT): +% +% STARTBLINK 3 (the start of a blink) +% ENDBLINK 4 (the end of a blink) +% STARTSACC 5 (the start of a saccade) +% ENDSACC 6 (the end of a saccade) +% STARTFIX 7 (the start of a fixation) +% ENDFIX 8 (the end of a fixation) +% FIXUPDATE 9 (a fixation update during a fixation) +% SAMPLE_TYPE 200 (a sample) +% MISSING_DATA -32768 (missing data) +% +% Use buffered data if you need to: +% a) grab every single consecutive sample online +% b) grab event data (e.g. fixation/saccade/blink events) online +% +% Note that some buffered event data take some time to be available online due to the times involved +% in calculating velocity/acceleration. If you need to retrieve online gaze +% position as fast as possible and/or you don't need to get all subsequent samples or other +% events, then use the Eyelink('NewFloatSampleAvailable') / Eyelink('NewestFloatSample') function pair, +% as illustrated in the GCfastSamples.m example. +% --------------------------------------------------------------------------------------------- +% +% Events structure and fields available via the 'GetNextDataType'/'GetFloatData' function pair: +% STARTBLINK, STARTSACC, STARTFIX: +% type (number assigned to event - STARTBLINK=3, STARTSACC=5, STARTFIX=7) +% eye (0=left eye, 1=right eye) +% sttime (event start time) +% +% ENDBLINK: +% type (number assigned to event - ENDBLINK=4) +% eye (0=left eye, 1=right eye) +% sttime (event start time) +% entime (event end time) +% +% ENDSACC: +% type (number assigned to event - ENDSACC=6) +% eye (0=left eye, 1=right eye) +% sttime (event start time) +% entime (event end time) +% gstx (Saccade start x gaze position) +% gsty (Saccade start y gaze position) +% genx (Saccade end x gaze position) +% geny (Saccade end y gaze position) +% supd_x (Saccade start x 'pixel per degree' value) +% supd_y (Saccade start y 'pixel per degree' value) +% eupd_x (Saccade end x 'pixel per degree' value) +% eupd_y (Saccade end y 'pixel per degree' value) +% +% FIXUPDATE, ENDFIX: +% type (number assigned to event - FIXUPDATE=9, ENDFIX=8) +% eye (0=left eye, 1=right eye) +% sttime (event start time) +% entime (event end time) +% gavx (average gaze x position during fixation) +% gavy (average gaze y position during fixation) +% ava (average pupil size) +% supd_x (Fixation start x 'pixel per degree' value) +% supd_y (Fixation start y 'pixel per degree' value) +% eupd_x (Fixation end x 'pixel per degree' value) +% eupd_y (Fixation end y 'pixel per degree' value) +% +% SAMPLE_TYPE +% time (sample time) +% type (SAMPLE=200) +% pa ([lef eye pupil size, right eye pupil size]) +% gx ([left gaze x, right gaze x]) +% gy ([left gaze y, right gaze y]) +% rx (x 'pixel per degree' value) +% ry (y 'pixel per degree' value) +% buttons (button state and changes) +% hdata (contains a list of 8 fields. Only the first 4 values are important: +% [uncalibrated target sticker x, uncalibrated target sticker y, target sticker distance in mm, target flags) + +% Bring the Command Window to the front if it is already open +if ~IsOctave; commandwindow; end + +PsychDefaultSetup(2); + +% Some initial parameters: +fixWinSize = 100; % Width and Height of square fixation window [in pixels] +fixateTime = 500; % Duration of gaze inside fixation window required before stimulus presentation [ms] +% Use default screenNumber if none specified +if (nargin < 1) + screenNumber = []; +end +try + %% STEP 1: INITIALIZE EYELINK CONNECTION; OPEN EDF FILE; GET EYELINK TRACKER VERSION + + % Initialize EyeLink connection (dummymode = 0) or run in "Dummy Mode" without an EyeLink connection (dummymode = 1); + dummymode = 0; + EyelinkInit(dummymode); % Initialize EyeLink connection + status = Eyelink('IsConnected'); + if status < 1 % If EyeLink not connected + dummymode = 1; + end + + % Open dialog box for EyeLink Data file name entry. File name up to 8 characters + prompt = {'Enter EDF file name (up to 8 characters)'}; + dlg_title = 'Create EDF file'; + def = {'demo'}; % Create a default edf file name + answer = inputdlg(prompt, dlg_title, 1, def); % Prompt for new EDF file name + % Print some text in Matlab's Command Window if a file name has not been entered + if isempty(answer) + fprintf('Session cancelled by user\n') + cleanup; % Abort experiment (see cleanup function below) + return + end + edfFile = answer{1}; % Save file name to a variable + % Print some text in Matlab's Command Window if file name is longer than 8 characters + if length(edfFile) > 8 + fprintf('Filename needs to be no more than 8 characters long (letters, numbers and underscores only)\n'); + cleanup; % Abort experiment (see cleanup function below) + return + end + + % Open an EDF file and name it + failOpen = Eyelink('OpenFile', edfFile); + if failOpen ~= 0 % Abort if it fails to open + fprintf('Cannot create EDF file %s', edfFile); % Print some text in Matlab's Command Window + cleanup; %see cleanup function below + return + end + + % Get EyeLink tracker and software version + % returns 0 if not connected + % returns 'EYELINK I', 'EYELINK II x.xx', 'EYELINK CL x.xx' where 'x.xx' is the software version + ELsoftwareVersion = 0; % Default EyeLink version in dummy mode + [ver, versionstring] = Eyelink('GetTrackerVersion'); + if dummymode == 0 % If connected to EyeLink + % Extract software version number. + [~, vnumcell] = regexp(versionstring,'.*?(\d)\.\d*?','Match','Tokens'); % Extract EL version before decimal point + ELsoftwareVersion = str2double(vnumcell{1}{1}); % Returns 1 for EyeLink I, 2 for EyeLink II, 3/4 for EyeLink 1K, 5 for EyeLink 1KPlus, 6 for Portable Duo + % Print some text in Matlab's Command Window + fprintf('Running experiment on %s version %d\n', versionstring, ver ); + end + % Add a line of text in the EDF file to identify the current experimemt name and session. This is optional. + % If your text starts with "RECORDED BY " it will be available in DataViewer's Inspector window by clicking + % the EDF session node in the top panel and looking for the "Recorded By:" field in the bottom panel of the Inspector. + preambleText = sprintf('RECORDED BY Psychtoolbox demo %s session name: %s', mfilename, edfFile); + Eyelink('Command', 'add_file_preamble_text "%s"', preambleText); + + + %% STEP 2: SELECT AVAILABLE SAMPLE/EVENT DATA + % See EyeLinkProgrammers Guide manual > Useful EyeLink Commands > File Data Control & Link Data Control + + % Select which events are saved in the EDF file. Include everything just in case + Eyelink('Command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); + % Select which events are available online for gaze-contingent experiments. Include everything just in case + Eyelink('Command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON,FIXUPDATE,INPUT'); + % Select which sample data is saved in EDF file or available online. Include everything just in case + if ELsoftwareVersion > 3 % Check tracker version and include 'HTARGET' to save head target sticker data for supported eye trackers + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,HTARGET,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,HTARGET,STATUS,INPUT'); + else + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); + end + + + %% STEP 3: OPEN GRAPHICS WINDOW + + % Open experiment graphics on the specified screen + if isempty(screenNumber) + screenNumber = max(Screen('Screens')); % Use default screen if none specified + end + + [window, wRect] = PsychImaging('OpenWindow', screenNumber, GrayIndex(screenNumber)); % Open graphics window + Screen('Flip', window); + + % Get max color value for rescaling to RGB for Host PC & Data Viewer integration + colorMaxVal = Screen('ColorRange', window); + % Return width and height of the graphics window/screen in pixels + [width, height] = Screen('WindowSize', window); + + + %% STEP 4: SET CALIBRATION SCREEN COLOURS; PROVIDE WINDOW SIZE TO EYELINK HOST & DATAVIEWER; SET CALIBRATION PARAMETERS; CALIBRATE + + % Provide EyeLink with some defaults, which are returned in the structure "el". + el = EyelinkInitDefaults(window); + % set calibration/validation/drift-check(or drift-correct) size as well as background and target colors. + % It is important that this background colour is similar to that of the stimuli to prevent large luminance-based + % pupil size changes (which can cause a drift in the eye movement data) + el.calibrationtargetsize = 3;% Outer target size as percentage of the screen + el.calibrationtargetwidth = 0.7;% Inner target size as percentage of the screen + el.backgroundcolour = repmat(GrayIndex(screenNumber),1,3); + el.calibrationtargetcolour = repmat(BlackIndex(screenNumber),1,3); + % set "Camera Setup" instructions text colour so it is different from background colour + el.msgfontcolour = repmat(BlackIndex(screenNumber),1,3); + + % Initialize PsychSound for calibration/validation audio feedback + % EyeLink Toolbox now supports PsychPortAudio integration and interop + % with legacy Snd() wrapping. Below we open the default audio device in + % output mode as master, create a slave device, and pass the device + % handle to el.ppa_pahandle. + % el.ppa_handle supports passing either standard mode handle, or as + % below one opened as a slave device. When el.ppa_handle is empty, for + % legacy support EyelinkUpdateDefaults() will open the default device + % and use that with Snd() interop, and close the device handle when + % calling Eyelink('Shutdown') at the end of the script. + InitializePsychSound(); + pamaster = PsychPortAudio('Open', [], 8+1); + PsychPortAudio('Start', pamaster); + pahandle = PsychPortAudio('OpenSlave', pamaster, 1); + el.ppa_pahandle = pahandle; + + % You must call this function to apply the changes made to the el structure above + EyelinkUpdateDefaults(el); + + % Set display coordinates for EyeLink data by entering left, top, right and bottom coordinates in screen pixels + Eyelink('Command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Write DISPLAY_COORDS message to EDF file: sets display coordinates in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Pre-trial Message Commands + Eyelink('Message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Set number of calibration/validation dots and spread: horizontal-only(H) or horizontal-vertical(HV) as H3, HV3, HV5, HV9 or HV13 + Eyelink('Command', 'calibration_type = HV9'); % horizontal-vertical 9-points + % Allow a supported EyeLink Host PC button box to accept calibration or drift-check/correction targets via button 5 + Eyelink('Command', 'button_function 5 "accept_target_fixation"'); + % Hide mouse cursor + HideCursor(window); + % Suppress keypress output to command window. + ListenChar(-1); + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Put EyeLink Host PC in Camera Setup mode for participant setup/calibration + EyelinkDoTrackerSetup(el); + + + %% STEP 5: TRIAL LOOP. + + % Create central square fixation window + fixationWindow = [-fixWinSize -fixWinSize fixWinSize fixWinSize]; + fixationWindow = CenterRect(fixationWindow, wRect); + + spaceBar = KbName('space');% Identify keyboard key code for space bar to end each trial later on + imgList = {'img1.jpg' 'img2.jpg'};% Provide image list for 2 trials + + for i = 1:length(imgList) % Trial loop + % Reset some parameters for each trial + sCross = 0; % Reset crosshairs display marker for each trial + fixWinComplete = 'yes'; % Reset variable for gaze maintained inside fixation window successfully + + % STEP 5.1: PREBUILD STIMULUS (GREY BACKGROUND + IMAGE + TEXT) + + % Prepare grey background on backbuffer + Screen('FillRect', window, el.backgroundcolour); + % Use 'drawBuffer' to copy unprocessed backbuffer images without additional processing. Prevents image size info issues on Retina displays + backgroundArray = Screen('GetImage', window, [], 'drawBuffer'); % Copy unprocessed backbuffer + backgroundTexture = Screen('MakeTexture', window, backgroundArray); % Convert background to texture so it is ready for drawing later on + % Prepare image on backbuffer + imgName = char(imgList(i)); % Get image file name for current trial + imgInfo = imfinfo(imgName); % Get image file info + imgData = imread(imgName); % Read image from file + imgTexture = Screen('MakeTexture',window, imgData); % Convert image file to texture + Screen('DrawTexture', window, imgTexture); % Prepare image texture on backbuffer + % Prepare text on backbuffer + Screen('TextSize', window, 30); % Specify text size + Screen('DrawText', window, 'Press space to end trial', 5, height-35, 0); % Prepare text on backbuffer + % Save complete backbuffer as trial*.bmp to be used as stimulus and as Host PC & DataViewer backdrop + stimName = ['trial' num2str(i) '.bmp']; % Prepare stimulus file name + stimArray = Screen('GetImage', window, [], 'drawBuffer'); % Copy backbuffer to be used as stimulus + imwrite(stimArray, stimName); % Save .bmp stimulus file in experment folder + % Convert stimulus to texture so it is ready for drawing later on + stimInfo = imfinfo(stimName); % Get stimulus info + stimTexture = Screen('MakeTexture', window, stimArray); % Convert to texture + + % STEP 5.2: START TRIAL; SHOW TRIAL INFO ON HOST PC; SHOW BACKDROP IMAGE AND/OR DRAW FEEDBACK GRAPHICS ON HOST PC; DRIFT-CHECK/CORRECTION + + % Write TRIALID message to EDF file: marks the start of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIALID %d', i); + + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Supply the trial number as a line of text on Host PC screen + Eyelink('Command', 'record_status_message "TRIAL %d/%d"', i, length(imgList)); + + % Draw graphics on the EyeLink Host PC display. See COMMANDS.INI in the Host PC's exe folder for a list of commands + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode before drawing Host PC graphics and before recording + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Optional: Send an image to the Host PC to be displayed as the backdrop image over which + % the gaze-cursor is overlayed during trial recordings. + % See Eyelink('ImageTransfer?') for information about supported syntax and compatible image formats. + % Below, we use the new option to pass image data from imread() as the imageArray parameter, which + % enables the use of many image formats. + % [status] = Eyelink('ImageTransfer', imageArray, xs, ys, width, height, xd, yd, options); + % xs, ys: top-left corner of the region to be transferred within the source image + % width, height: size of region to be transferred within the source image (note, values of 0 will include the entire width/height) + % xd, yd: location (top-left) where image region to be transferred will be presented on the Host PC + % This image transfer function works for non-resized image presentation only. If you need to resize images and use this function please resize + % the original image files beforehand + transferStatus = Eyelink('ImageTransfer', stimArray, 0, 0, 0, 0, 0, 0); + if dummymode == 0 && transferStatus ~= 0 % If connected to EyeLink and image transfer fails + fprintf('Image transfer Failed\n'); % Print some text in Matlab's Command Window + end + + % Optional: draw feedback box and lines on Host PC interface instead of (or on top of) backdrop image. + % See section 25.7 'Drawing Commands' in the EyeLink Programmers Guide manual + Eyelink('Command', 'draw_box %d %d %d %d 15', fixationWindow(1), fixationWindow(2), fixationWindow(3), fixationWindow(4)); % Fixation window + Eyelink('Command', 'draw_cross %d %d 15 ', width/2, height/2); % Central crosshairs + + % Perform a drift check/correction. + % Optionally provide x y target location, otherwise target is presented on screen centre + EyelinkDoDriftCorrection(el, round(width/2), round(height/2)); + + %STEP 5.3: START RECORDING + + % Put tracker in idle/offline mode before recording. Eyelink('SetOfflineMode') is recommended + % however if Eyelink('Command', 'set_idle_mode') is used allow 50ms before recording as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode before recording + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode before recording + Eyelink('StartRecording'); % Start tracker recording + WaitSecs(0.1); % Allow some time to record a few samples before presenting first stimulus + + % STEP 5.4: PRESENT CROSSHAIRS; WAIT FOR GAZE INSIDE WINDOW OR FOR KEYPRESS + + % Check which eye is available online. Returns 0 (left), 1 (right) or 2 (binocular) + eyeUsed = Eyelink('EyeAvailable'); + % Get events from right eye if binocular + if eyeUsed == 2 + eyeUsed = 1; + end + bufferStart = GetSecs; % Start a ~100ms counter + + % loop until gaze is in fixation window for minimum fixation window time (fixateTime) or until space bar is pressed + while 1 + % Check that tracker is still recording. Otherwise close and transfer copy of EDF file to Display PC + err = Eyelink('CheckRecording'); + if(err ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below + cleanup; % Abort experiment (see cleanup function below) + return + end + % Run the 'GetNextDataType'/'GetFloatData' function pair in a loop for ~100ms before drawing crosshairs. + % This will clear old data from the buffer and allow access to the most recent online samples. + if GetSecs - bufferStart > 0.1 && sCross == 0 % If ~100ms have elapsed and crosshairs not yet drawn... + % Present central crosshairs on a grey background + Screen('DrawTexture', window, backgroundTexture); % Prepare background texture on backbuffer + Screen('DrawLine', window, 0, round(width/2-20), round(height/2), round(width/2+20), round(height/2), 5); + Screen('DrawLine', window, 0, round(width/2), round(height/2-20), round(width/2), round(height/2+20), 5); + [~, gazeWinStart] = Screen('Flip', window); % Present crosshairs. Start timer for fixation window + % Write message to EDF file to mark the crosshairs presentation time. + Eyelink('Message', 'CROSSHAIRS'); + % Return the current EDF time (in seconds) to make sure we only use online samples that started after crosshairs drawing + StimEDFtime = (Eyelink('TrackerTime'))*1000; % Multiply by 1000 to convert to milliseconds + % Write messages to EDF to draw central crosshairs in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + Eyelink('Message', '!V DRAWLINE 0 0 0 %d %d %d %d', round(width/2-20), round(height/2), round(width/2+20), round(height/2)); + Eyelink('Message', '!V DRAWLINE 0 0 0 %d %d %d %d', round(width/2), round(height/2-20), round(width/2), round(height/2+20)); + % Write !V IAREA message to EDF file: creates fixation window interest area in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Interest Area Commands + Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 1, fixationWindow(1), fixationWindow(2), fixationWindow(3), fixationWindow(4),'FIXWINDOW_IA'); + sCross = 1; % Crosshairs have been drawn + end + % Get next data item (sample or event) from link buffer. + % This is equivalent to EyeLink_get_next_data() in C API. See EyeLink Programmers Guide manual > Message and Command Sending/Receiving > Functions + evtype = Eyelink('GetNextDataType'); + % Read item type returned by getnextdatatype. Wait for a gaze sample from the buffer + % 'GetFloatData' is equivalent to eyelink_get_float_data() in C API. See EyeLink Programmers Guide manual > Function Lists > Message and Command Sending/Receiving > Functions + % This pair of functions should be called as quickly/frequently as possible in the + % recording loop. If there is a process that blocks calling the function pair, then + % try calling them repeatedly to clear the buffer when you have the opportunity to do that. + if evtype == el.SAMPLE_TYPE % if a gaze sample is detected + evt = Eyelink('GetFloatData', evtype); % access the sample structure + if sCross == 1 % Start gaze-contingent window checking only after having looped through sample/event-checking for ~100ms + % Use sample only if it occurred after trial image onset + if evt.time > StimEDFtime + % Save current gaze x y sample fields in variables. See EyeLink Programmers Guide manual > Data Structures > FEVENT + x_gaze = evt.gx(eyeUsed+1); % +1 as we are accessing an array + y_gaze = evt.gy(eyeUsed+1); + if inFixWindow(x_gaze,y_gaze) % If gaze sample is within fixation window (see inFixWindow function below) + if (GetSecs - gazeWinStart)*1000 >= fixateTime % If gaze duration >= minimum fixation window time + break; % break while loop to show stimulus + end + elseif ~inFixWindow(x_gaze,y_gaze) % If gaze sample is not within fixation window + gazeWinStart = GetSecs; % Reset fixation window timer + end + end + end + end + % Wait for space bar to end crosshairs presentation if participant is unable to maintain gaze inside fixation window for duration 'fixateTime' + [~, ~, keyCode] = KbCheck; + if keyCode(spaceBar) + % Write message to EDF file to mark the space bar press time + Eyelink('Message', 'FIXATION_KEY_PRESSED'); + fixWinComplete = 'no'; % Update variable: gaze not maintained inside window for duration 'fixateTime' + break; + end + end % End of gaze-checking while loop + + % STEP 5.5: PRESENT STIMULUS; CREATE DATAVIEWER BACKDROP AND INTEREST AREA + + % Present initial trial image + Screen('DrawTexture', window, stimTexture); % Prepare stimulus texture on backbuffer + [~, RtStart] = Screen('Flip', window); % Present stimulus + % Write message to EDF file to mark the start time of stimulus presentation. + Eyelink('Message', 'STIM_ONSET'); + % Write !V IMGLOAD message to EDF file: creates backdrop image for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Image Commands + Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', stimName, width/2, height/2); + % Write !V IAREA message to EDF file: creates image interest area in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Interest Area Commands + Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 2, round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2),'IMAGE_IA'); + + % STEP 5.6: WAIT FOR KEYPRESS; SHOW BLANK SCREEN; STOP RECORDING + + KbReleaseWait; % Wait until space bar release if pressed in prevous while loop + while 1 % loop until error or space bar press + % Check that eye tracker is still recording. Otherwise close and transfer copy of EDF file to Display PC + err = Eyelink('CheckRecording'); + if(err ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below + cleanup; % Abort experiment (see cleanup function below) + return + end + % End trial if space bar is pressed + [~, RtEnd, keyCode] = KbCheck; + if keyCode(spaceBar) + % Write message to EDF file to mark the space bar press time + Eyelink('Message', 'KEY_PRESSED'); + reactionTime = round((RtEnd - RtStart)*1000); % Calculate RT [ms] from stimulus onset + break; + end + end % End of while loop + + % Draw blank screen at end of trial + Screen('DrawTexture', window, backgroundTexture); % Prepare background texture on backbuffer + Screen('Flip', window); % Present blank screen + % Write message to EDF file to mark time when blank screen is presented + Eyelink('Message', 'BLANK_SCREEN'); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Stop recording eye movements at the end of each trial + WaitSecs(0.1); % Add 100 msec of data to catch final events before stopping + Eyelink('StopRecording'); % Stop tracker recording + + % STEP 5.7: CREATE VARIABLES FOR DATAVIEWER; END TRIAL + + % Write !V TRIAL_VAR messages to EDF file: creates trial variables in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Trial Message Commands + Eyelink('Message', '!V TRIAL_VAR iteration %d', i); % Trial iteration + Eyelink('Message', '!V TRIAL_VAR image %s', imgName); % Image name + WaitSecs(0.001); % Allow some time between messages. Some messages can be lost if too many are written at the same time + Eyelink('Message', '!V TRIAL_VAR fix_completed %s', fixWinComplete); % Was gaze maintained inside fixation window successfully (yes/no)? + Eyelink('Message', '!V TRIAL_VAR rt %d', reactionTime); % Key press RT [ms] from stimulus onset + % Write TRIAL_RESULT message to EDF file: marks the end of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIAL_RESULT 0'); + WaitSecs(0.01); % Allow some time before ending the trial + + % Clear Screen() textures that were initialized for each trial iteration + Screen('Close', backgroundTexture); + Screen('Close', imgTexture); + Screen('Close', stimTexture); + end % End trial loop + + + %% STEP 6: CLOSE EDF FILE. TRANSFER EDF COPY TO DISPLAY PC. CLOSE EYELINK CONNECTION. FINISH UP + + % Put tracker in idle/offline mode before closing file. Eyelink('SetOfflineMode') is recommended. + % However if Eyelink('Command', 'set_idle_mode') is used, allow 50ms before closing the file as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('Command', 'clear_screen 0'); % Clear Host PC backdrop graphics at the end of the experiment + WaitSecs(0.5); % Allow some time before closing and transferring file + Eyelink('CloseFile'); % Close EDF file on Host PC + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below +catch % If syntax error is detected + cleanup; + % Print error message and line number in Matlab's Command Window + psychrethrow(psychlasterror); +end +PsychPortAudio('Close', pahandle); +PsychPortAudio('Close', pamaster); + +% Function that determines if gaze x y coordinates are within fixation window + function fix = inFixWindow(mx,my) + fix = mx > fixationWindow(1) && mx < fixationWindow(3) && ... + my > fixationWindow(2) && my < fixationWindow(4) ; + end + +% Cleanup function used throughout the script above + function cleanup + sca; % PTB's wrapper for Screen('CloseAll') & related cleanup, e.g. ShowCursor + Eyelink('Shutdown'); % Close EyeLink connection + ListenChar(0); % Restore keyboard output to Matlab + if ~IsOctave; commandwindow; end % Bring Command Window to front + end + +% Function for transferring copy of EDF file to the experiment folder on Display PC. +% Allows for optional destination path which is different from experiment folder + function transferFile + try + if dummymode ==0 % If connected to EyeLink + % Show 'Receiving data file...' text until file transfer is complete + Screen('FillRect', window, el.backgroundcolour); % Prepare background on backbuffer + Screen('DrawText', window, 'Receiving data file...', 5, height-35, 0); % Prepare text + Screen('Flip', window); % Present text + fprintf('Receiving data file ''%s.edf''\n', edfFile); % Print some text in Matlab's Command Window + + % Transfer EDF file to Host PC + % [status =] Eyelink('ReceiveFile',['src'], ['dest'], ['dest_is_path']) + status = Eyelink('ReceiveFile'); + + % Check if EDF file has been transferred successfully and print file size in Matlab's Command Window + if status > 0 + fprintf('EDF file size: %.1f KB\n', status/1024); % Divide file size by 1024 to convert bytes to KB + end + % Print transferred EDF file path in Matlab's Command Window + fprintf('Data file ''%s.edf'' can be found in ''%s''\n', edfFile, pwd); + else + fprintf('No EDF file saved in Dummy mode\n'); + end + cleanup; + catch % Catch a file-transfer error and print some text in Matlab's Command Window + fprintf('Problem receiving data file ''%s''\n', edfFile); + cleanup; + psychrethrow(psychlasterror); + end + end +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/town.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowBufferedSamples/img1.jpg similarity index 100% rename from Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/town.jpg rename to Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowBufferedSamples/img1.jpg diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/composite.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowBufferedSamples/img2.jpg similarity index 100% rename from Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemo/composite.jpg rename to Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowBufferedSamples/img2.jpg diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowFastSamples/EyeLink_FixWindowFastSamples.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowFastSamples/EyeLink_FixWindowFastSamples.m new file mode 100644 index 0000000000..0599cf54cf --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowFastSamples/EyeLink_FixWindowFastSamples.m @@ -0,0 +1,478 @@ +function EyeLink_FixWindowFastSamples(screenNumber) +% EyeLink gaze-contingent demo showing how to retrieve fast gaze samples online. +% In each trial central crosshairs are shown until gaze is detected continuously within a central +% square window for 500ms or until the space bar is pressed. An image is +% then presented until the space bar is pressed to end the trial. +% +% Usage: +% Eyelink_FixWindowFastSamples(screenNumber) +% +% screenNumber is an optional parameter which can be used to pass a specific value to PsychImaging('OpenWindow', ...) +% If screenNumber is not specified, or if isempty(screenNumber) then the default: +% screenNumber = max(Screen('Screens')); +% will be used. +% +% The demo checks if a new sample is available online via the link. +% This is equivalent to eyeLink_newest_float_sample() in C API. +% See EyeLink Programmers Guide manual > Function Lists > Message and Command Sending/Receiving > Functions +% It allows access to the following sample properties: +% +% time (sample time) +% type (SAMPLE=200) +% gx ([left gaze x, right gaze x]) +% gy ([left gaze y, right gaze y]) +% pa ([lef eye pupil size, right eye pupil size]) +% rx (x 'pixel per degree' value) +% ry (y 'pixel per degree' value) +% buttons (button state and changes) +% hdata (contains a list of 8 fields. Only the first 4 values are important: +% [uncalibrated target sticker x, uncalibrated target sticker y, target sticker distance in mm, target flags) + +% Some initial parameters: +fixWinSize = 100; % Width and Height of square fixation window [in pixels] +fixateTime = 500; % Duration of gaze inside fixation window required before stimulus presentation [ms] + +% Bring the Command Window to the front if it is already open +if ~IsOctave; commandwindow; end + +PsychDefaultSetup(2); + +% Use default screenNumber if none specified +if (nargin < 1) + screenNumber = []; +end + +try + %% STEP 1: INITIALIZE EYELINK CONNECTION; OPEN EDF FILE; GET EYELINK TRACKER VERSION + + % Initialize EyeLink connection (dummymode = 0) or run in "Dummy Mode" without an EyeLink connection (dummymode = 1); + dummymode = 0; + EyelinkInit(dummymode); % Initialize EyeLink connection + status = Eyelink('IsConnected'); + if status < 1 % If EyeLink not connected + dummymode = 1; + end + + % Open dialog box for EyeLink Data file name entry. File name up to 8 characters + prompt = {'Enter EDF file name (up to 8 characters)'}; + dlg_title = 'Create EDF file'; + def = {'demo'}; % Create a default edf file name + answer = inputdlg(prompt, dlg_title, 1, def); % Prompt for new EDF file name + % Print some text in Matlab's Command Window if a file name has not been entered + if isempty(answer) + fprintf('Session cancelled by user\n') + cleanup; % Abort experiment (see cleanup function below) + return + end + edfFile = answer{1}; % Save file name to a variable + % Print some text in Matlab's Command Window if file name is longer than 8 characters + if length(edfFile) > 8 + fprintf('Filename needs to be no more than 8 characters long (letters, numbers and underscores only)\n'); + cleanup; % Abort experiment (see cleanup function below) + return + end + + % Open an EDF file and name it + failOpen = Eyelink('OpenFile', edfFile); + if failOpen ~= 0 % Abort if it fails to open + fprintf('Cannot create EDF file %s', edfFile); % Print some text in Matlab's Command Window + cleanup; %see cleanup function below + return + end + + % Get EyeLink tracker and software version + % returns 0 if not connected + % returns 'EYELINK I', 'EYELINK II x.xx', 'EYELINK CL x.xx' where 'x.xx' is the software version + ELsoftwareVersion = 0; % Default EyeLink version in dummy mode + [ver, versionstring] = Eyelink('GetTrackerVersion'); + if dummymode == 0 % If connected to EyeLink + % Extract software version number. + [~, vnumcell] = regexp(versionstring,'.*?(\d)\.\d*?','Match','Tokens'); % Extract EL version before decimal point + ELsoftwareVersion = str2double(vnumcell{1}{1}); % Returns 1 for EyeLink I, 2 for EyeLink II, 3/4 for EyeLink 1K, 5 for EyeLink 1KPlus, 6 for Portable Duo + % Print some text in Matlab's Command Window + fprintf('Running experiment on %s version %d\n', versionstring, ver ); + end + % Add a line of text in the EDF file to identify the current experimemt name and session. This is optional. + % If your text starts with "RECORDED BY " it will be available in DataViewer's Inspector window by clicking + % the EDF session node in the top panel and looking for the "Recorded By:" field in the bottom panel of the Inspector. + preambleText = sprintf('RECORDED BY Psychtoolbox demo %s session name: %s', mfilename, edfFile); + Eyelink('Command', 'add_file_preamble_text "%s"', preambleText); + + + %% STEP 2: SELECT AVAILABLE SAMPLE/EVENT DATA + % See EyeLinkProgrammers Guide manual > Useful EyeLink Commands > File Data Control & Link Data Control + + % Select which events are saved in the EDF file. Include everything just in case + Eyelink('Command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); + % Select which events are available online for gaze-contingent experiments. Include everything just in case + Eyelink('Command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON,FIXUPDATE,INPUT'); + % Select which sample data is saved in EDF file or available online. Include everything just in case + if ELsoftwareVersion > 3 % Check tracker version and include 'HTARGET' to save head target sticker data for supported eye trackers + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,HTARGET,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,HTARGET,STATUS,INPUT'); + else + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); + end + + + %% STEP 3: OPEN GRAPHICS WINDOW + + % Open experiment graphics on the specified screen + if isempty(screenNumber) + screenNumber = max(Screen('Screens')); % Use default screen if none specified + end + + [window, wRect] = PsychImaging('OpenWindow', screenNumber, GrayIndex(screenNumber)); % Open graphics window + Screen('Flip', window); + + % Get max color value for rescaling to RGB for Host PC & Data Viewer integration + colorMaxVal = Screen('ColorRange', window); + % Return width and height of the graphics window/screen in pixels + [width, height] = Screen('WindowSize', window); + + + %% STEP 4: SET CALIBRATION SCREEN COLOURS; PROVIDE WINDOW SIZE TO EYELINK HOST & DATAVIEWER; SET CALIBRATION PARAMETERS; CALIBRATE + + % Provide EyeLink with some defaults, which are returned in the structure "el". + el = EyelinkInitDefaults(window); + % set calibration/validation/drift-check(or drift-correct) size as well as background and target colors. + % It is important that this background colour is similar to that of the stimuli to prevent large luminance-based + % pupil size changes (which can cause a drift in the eye movement data) + el.calibrationtargetsize = 3;% Outer target size as percentage of the screen + el.calibrationtargetwidth = 0.7;% Inner target size as percentage of the screen + el.backgroundcolour = repmat(GrayIndex(screenNumber),1,3); + el.calibrationtargetcolour = repmat(BlackIndex(screenNumber),1,3); + % set "Camera Setup" instructions text colour so it is different from background colour + el.msgfontcolour = repmat(BlackIndex(screenNumber),1,3); + + % Initialize PsychSound for calibration/validation audio feedback + % EyeLink Toolbox now supports PsychPortAudio integration and interop + % with legacy Snd() wrapping. Below we open the default audio device in + % output mode as master, create a slave device, and pass the device + % handle to el.ppa_pahandle. + % el.ppa_handle supports passing either standard mode handle, or as + % below one opened as a slave device. When el.ppa_handle is empty, for + % legacy support EyelinkUpdateDefaults() will open the default device + % and use that with Snd() interop, and close the device handle when + % calling Eyelink('Shutdown') at the end of the script. + InitializePsychSound(); + pamaster = PsychPortAudio('Open', [], 8+1); + PsychPortAudio('Start', pamaster); + pahandle = PsychPortAudio('OpenSlave', pamaster, 1); + el.ppa_pahandle = pahandle; + + % You must call this function to apply the changes made to the el structure above + EyelinkUpdateDefaults(el); + + % Set display coordinates for EyeLink data by entering left, top, right and bottom coordinates in screen pixels + Eyelink('Command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Write DISPLAY_COORDS message to EDF file: sets display coordinates in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Pre-trial Message Commands + Eyelink('Message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Set number of calibration/validation dots and spread: horizontal-only(H) or horizontal-vertical(HV) as H3, HV3, HV5, HV9 or HV13 + Eyelink('Command', 'calibration_type = HV9'); % horizontal-vertical 9-points + % Allow a supported EyeLink Host PC button box to accept calibration or drift-check/correction targets via button 5 + Eyelink('Command', 'button_function 5 "accept_target_fixation"'); + % Hide mouse cursor + HideCursor(window); + % Suppress keypress output to command window. + ListenChar(-1); + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Put EyeLink Host PC in Camera Setup mode for participant setup/calibration + EyelinkDoTrackerSetup(el); + + + %% STEP 5: TRIAL LOOP. + + imgList = {'img1.jpg' 'img2.jpg'};% Provide image list for 2 trials + spaceBar = KbName('space');% Identify keyboard key code for space bar to end each trial later on + % Create central square fixation window + fixationWindow = [-fixWinSize -fixWinSize fixWinSize fixWinSize]; + fixationWindow = CenterRect(fixationWindow, wRect); + + for i = 1:length(imgList) % Trial loop + + % STEP 5.1: PREBUILD STIMULUS (GREY BACKGROUND + IMAGE + TEXT) + + % Prepare grey background on backbuffer + Screen('FillRect', window, el.backgroundcolour); + % Use 'drawBuffer' to copy unprocessed backbuffer images without additional processing. Prevents image size info issues on Retina displays + backgroundArray = Screen('GetImage', window, [], 'drawBuffer'); % Copy unprocessed backbuffer + backgroundTexture = Screen('MakeTexture', window, backgroundArray); % Convert background to texture so it is ready for drawing later on + % Prepare image on backbuffer + imgName = char(imgList(i)); % Get image file name for current trial + imgInfo = imfinfo(imgName); % Get image file info + imgData = imread(imgName); % Read image from file + imgTexture = Screen('MakeTexture',window, imgData); % Convert image file to texture + Screen('DrawTexture', window, imgTexture); % Prepare image texture on backbuffer + % Prepare text on backbuffer + Screen('TextSize', window, 30); % Specify text size + Screen('DrawText', window, 'Press space to end trial', 5, height-35, 0); % Prepare text on backbuffer + % Save complete backbuffer as trial*.bmp to be used as stimulus and as Host PC & DataViewer backdrop + stimName = ['trial' num2str(i) '.bmp']; % Prepare stimulus file name + stimArray = Screen('GetImage', window, [], 'drawBuffer'); % Copy backbuffer to be used as stimulus + imwrite(stimArray, stimName); % Save .bmp stimulus file in experment folder + % Convert stimulus to texture so it is ready for drawing later on + stimInfo = imfinfo(stimName); % Get stimulus info + stimTexture = Screen('MakeTexture', window, stimArray); % Convert to texture + + % STEP 5.2: START TRIAL; SHOW TRIAL INFO ON HOST PC; SHOW BACKDROP IMAGE AND/OR DRAW FEEDBACK GRAPHICS ON HOST PC; DRIFT-CHECK/CORRECTION + + % Write TRIALID message to EDF file: marks the start of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIALID %d', i); + + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Supply the trial number as a line of text on Host PC screen + Eyelink('Command', 'record_status_message "TRIAL %d/%d"', i, length(imgList)); + + % Draw graphics on the EyeLink Host PC display. See COMMANDS.INI in the Host PC's exe folder for a list of commands + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode before drawing Host PC graphics and before recording + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Optional: Send an image to the Host PC to be displayed as the backdrop image over which + % the gaze-cursor is overlayed during trial recordings. + % See Eyelink('ImageTransfer?') for information about supported syntax and compatible image formats. + % Below, we use the new option to pass image data from imread() as the imageArray parameter, which + % enables the use of many image formats. + % [status] = Eyelink('ImageTransfer', imageArray, xs, ys, width, height, xd, yd, options); + % xs, ys: top-left corner of the region to be transferred within the source image + % width, height: size of region to be transferred within the source image (note, values of 0 will include the entire width/height) + % xd, yd: location (top-left) where image region to be transferred will be presented on the Host PC + % This image transfer function works for non-resized image presentation only. If you need to resize images and use this function please resize + % the original image files beforehand + transferStatus = Eyelink('ImageTransfer', stimArray, 0, 0, 0, 0, 0, 0); + if dummymode == 0 && transferStatus ~= 0 % If connected to EyeLink and image transfer fails + fprintf('Image transfer Failed\n'); % Print some text in Matlab's Command Window + end + % Optional: draw feedback box and lines on Host PC interface instead of (or on top of) backdrop image. + % See section 25.7 'Drawing Commands' in the EyeLink Programmers Guide manual + Eyelink('Command', 'draw_box %d %d %d %d 15', fixationWindow(1), fixationWindow(2), fixationWindow(3), fixationWindow(4)); % Fixation window + Eyelink('Command', 'draw_cross %d %d 15 ', width/2, height/2); % Central crosshairs + + % Perform a drift check/correction. + % Optionally provide x y target location, otherwise target is presented on screen centre + EyelinkDoDriftCorrection(el, round(width/2), round(height/2)); + + %STEP 5.3: START RECORDING + + % Put tracker in idle/offline mode before recording. Eyelink('SetOfflineMode') is recommended + % however if Eyelink('Command', 'set_idle_mode') is used allow 50ms before recording as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode before recording + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode before recording + Eyelink('StartRecording'); % Start tracker recording + WaitSecs(0.1); % Allow some time to record a few samples before presenting first stimulus + + % Check which eye is available online. Returns 0 (left), 1 (right) or 2 (binocular) + eyeUsed = Eyelink('EyeAvailable'); + % Get events from right eye if binocular + if eyeUsed == 2 + eyeUsed = 1; + end + + % STEP 5.4: PRESENT CROSSHAIRS; WAIT FOR GAZE INSIDE WINDOW OR FOR KEYPRESS + + % Present central crosshairs on a grey background + Screen('DrawTexture', window, backgroundTexture); % Prepare background texture on backbuffer + Screen('DrawLine', window, 0, round(width/2-20), round(height/2), round(width/2+20), round(height/2), 5); + Screen('DrawLine', window, 0, round(width/2), round(height/2-20), round(width/2), round(height/2+20), 5); + [~, gazeWinStart] = Screen('Flip', window); % Present crosshairs. Start timer for fixation window + % Write message to EDF file to mark the crosshairs presentation time. + Eyelink('Message', 'CROSSHAIRS'); + % Write messages to EDF: prepare backdrop and draw central crosshairs in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + Eyelink('Message', '!V DRAWLINE 0 0 0 %d %d %d %d', round(width/2-20), round(height/2), round(width/2+20), round(height/2)); + Eyelink('Message', '!V DRAWLINE 0 0 0 %d %d %d %d', round(width/2), round(height/2-20), round(width/2), round(height/2+20)); + % Write !V IAREA message to EDF file: creates fixation window interest areas in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Interest Area Commands + Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 1, fixationWindow(1), fixationWindow(2), fixationWindow(3), fixationWindow(4),'FIXWINDOW_IA'); + fixWinComplete = 'yes'; % Reset variable for gaze maintained inside fixation window successfully + while 1 % loop until error or space bar press + % Check tracker is still recording, otherwise close and transfer copy of EDF file to Display PC + err = Eyelink('CheckRecording'); + if(err ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below + cleanup; % Abort experiment (see cleanup function below) + return + end + % Check if a new sample is available online via the link. + % This is equivalent to eyeLink_newest_float_sample() in C API. See EyeLink Programmers Guide manual > Function Lists > Message and Command Sending/Receiving > Functions + if Eyelink('NewFloatSampleAvailable') > 0 + % Get sample data in a Matlab structure + % This is equivalent to eyeLink_newest_float_sample() in C API. See EyeLink Programmers Guide manual > Function Lists > Message and Command Sending/Receiving > Functions + evt = Eyelink('NewestFloatSample'); + % Save sample properties as variables. See EyeLink Programmers Guide manual > Data Structures > FSAMPLE + x_gaze = evt.gx(eyeUsed+1); % [left eye gaze x, right eye gaze x] +1 as we're accessing an array + y_gaze = evt.gy(eyeUsed+1); % [left eye gaze y, right eye gaze y] + if inFixWindow(x_gaze,y_gaze) % If gaze sample is within fixation window (see inFixWindow function below) + if (GetSecs - gazeWinStart)*1000 >= fixateTime % If gaze duration >= minimum fixation window time (fxateTime) + break; % break while loop to show stimulus + end + elseif ~inFixWindow(x_gaze,y_gaze) % If gaze sample is not within fixation window + gazeWinStart = GetSecs; % Reset fixation window timer + end + end + % Wait for space bar to end crosshairs if participant is unable to maintain gaze inside window for duration 'fixateTime' + [~, ~, keyCode] = KbCheck; + if keyCode(spaceBar) + % Write message to EDF file to mark the space bar press time + Eyelink('Message', 'FIXATION_KEY_PRESSED'); + fixWinComplete = 'no'; % Update variable for gaze not maintained inside window + break; % break while loop to show stimulus + end + end % End of gaze-checking while loop + + % STEP 5.5: PRESENT STIMULUS; CREATE DATAVIEWER BACKDROP AND INTEREST AREA + + % Present initial trial image + Screen('DrawTexture', window, stimTexture); % Prepare stimulus texture on backbuffer + [~, RtStart] = Screen('Flip', window); % Present stimulus + % Write message to EDF file to mark the start time of stimulus presentation. + Eyelink('Message', 'STIM_ONSET'); + % Write !V IMGLOAD message to EDF file: creates backdrop image for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Image Commands + Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', stimName, width/2, height/2); + % Write !V IAREA message to EDF file: creates image interest area in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Interest Area Commands + Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 2, round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2),'IMAGE_IA'); + + % STEP 5.6: WAIT FOR KEYPRESS; SHOW BLANK SCREEN; STOP RECORDING + + KbReleaseWait; % Wait until space bar release if pressed in prevous while loop + while 1 % loop until error or space bar press + % Check that eye tracker is still recording. Otherwise close and transfer copy of EDF file to Display PC + err = Eyelink('CheckRecording'); + if(err ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below + cleanup; % Abort experiment (see cleanup function below) + return + end + % End trial if space bar is pressed + [~, RtEnd, keyCode] = KbCheck; + if keyCode(spaceBar) + % Write message to EDF file to mark the space bar press time + Eyelink('Message', 'KEY_PRESSED'); + reactionTime = round((RtEnd - RtStart)*1000); % Calculate RT [ms] from stimulus onset + break; + end + end % End of while loop + % Draw blank screen at end of trial + Screen('DrawTexture', window, backgroundTexture); % Prepare background texture on backbuffer + Screen('Flip', window); % Present blank screen + % Write message to EDF file to mark time when blank screen is presented + Eyelink('Message', 'BLANK_SCREEN'); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Stop recording eye movements at the end of each trial + WaitSecs(0.1); % Add 100 msec of data to catch final events before stopping + Eyelink('StopRecording'); % Stop tracker recording + + % STEP 5.7: CREATE VARIABLES FOR DATAVIEWER; END TRIAL + + % Write !V TRIAL_VAR messages to EDF file: creates trial variables in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Trial Message Commands + Eyelink('Message', '!V TRIAL_VAR iteration %d', i); % Trial iteration + Eyelink('Message', '!V TRIAL_VAR image %s', imgName); % Image name + WaitSecs(0.001); % Allow some time between messages. Some messages can be lost if too many are written at the same time + Eyelink('Message', '!V TRIAL_VAR fix_completed %s', fixWinComplete); % Was gaze maintained inside fixation window successfully (yes/no)? + Eyelink('Message', '!V TRIAL_VAR rt %d', reactionTime); % Key press RT [ms] from stimulus onset + % Write TRIAL_RESULT message to EDF file: marks the end of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIAL_RESULT 0'); + WaitSecs(0.01); % Allow some time before ending the trial + + % Clear Screen() textures that were initialized for each trial iteration + Screen('Close', backgroundTexture); + Screen('Close', imgTexture); + Screen('Close', stimTexture); + end % End trial loop + + + %% STEP 6: CLOSE EDF FILE. TRANSFER EDF COPY TO DISPLAY PC. CLOSE EYELINK CONNECTION. FINISH UP + + % Put tracker in idle/offline mode before closing file. Eyelink('SetOfflineMode') is recommended. + % However if Eyelink('Command', 'set_idle_mode') is used, allow 50ms before closing the file as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('Command', 'clear_screen 0'); % Clear Host PC backdrop graphics at the end of the experiment + WaitSecs(0.5); % Allow some time before closing and transferring file + Eyelink('CloseFile'); % Close EDF file on Host PC + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below +catch % If syntax error is detected + cleanup; + % Print error message and line number in Matlab's Command Window + psychrethrow(psychlasterror); +end +PsychPortAudio('Close', pahandle); +PsychPortAudio('Close', pamaster); + + +% Function that determines if gaze x y coordinates are within fixation window + function fix = inFixWindow(mx,my) + fix = mx > fixationWindow(1) && mx < fixationWindow(3) && ... + my > fixationWindow(2) && my < fixationWindow(4) ; + end + +% Cleanup function used throughout the script above + function cleanup + sca; % PTB's wrapper for Screen('CloseAll') & related cleanup, e.g. ShowCursor + Eyelink('Shutdown'); % Close EyeLink connection + ListenChar(0); % Restore keyboard output to Matlab + if ~IsOctave; commandwindow; end % Bring Command Window to front + end + +% Function for transferring copy of EDF file to the experiment folder on Display PC. +% Allows for optional destination path which is different from experiment folder + function transferFile + try + if dummymode ==0 % If connected to EyeLink + % Show 'Receiving data file...' text until file transfer is complete + Screen('FillRect', window, el.backgroundcolour); % Prepare background on backbuffer + Screen('DrawText', window, 'Receiving data file...', 5, height-35, 0); % Prepare text + Screen('Flip', window); % Present text + fprintf('Receiving data file ''%s.edf''\n', edfFile); % Print some text in Matlab's Command Window + + % Transfer EDF file to Host PC + % [status =] Eyelink('ReceiveFile',['src'], ['dest'], ['dest_is_path']) + status = Eyelink('ReceiveFile'); + + % Check if EDF file has been transferred successfully and print file size in Matlab's Command Window + if status > 0 + fprintf('EDF file size: %.1f KB\n', status/1024); % Divide file size by 1024 to convert bytes to KB + end + % Print transferred EDF file path in Matlab's Command Window + fprintf('Data file ''%s.edf'' can be found in ''%s''\n', edfFile, pwd); + else + fprintf('No EDF file saved in Dummy mode\n'); + end + cleanup; + catch % Catch a file-transfer error and print some text in Matlab's Command Window + fprintf('Problem receiving data file ''%s''\n', edfFile); + cleanup; + psychrethrow(psychlasterror); + end + end +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowFastSamples/img1.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowFastSamples/img1.jpg new file mode 100644 index 0000000000..00663e2e44 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowFastSamples/img1.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowFastSamples/img2.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowFastSamples/img2.jpg new file mode 100644 index 0000000000..3fa77e178b Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/FixWindowFastSamples/img2.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedEndSacEvents/EyeLink_BufferedEndSacEvents.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedEndSacEvents/EyeLink_BufferedEndSacEvents.m new file mode 100644 index 0000000000..f0cea79b9a --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedEndSacEvents/EyeLink_BufferedEndSacEvents.m @@ -0,0 +1,528 @@ +function EyeLink_BufferedEndSacEvents(screenNumber) +% A simple EyeLink gaze-contingent demo showing how to retrieve online events from a buffer. +% In each trial an image is presented with a red gaze-contingent dot overlaid on top. +% The dot's location is updated online based on the end x y coordinates of each saccade detected. +% Each trial ends when the space bar is pressed. +% +% Usage: +% Eyelink_BufferedEndSacEvents(screenNumber) +% +% screenNumber is an optional parameter which can be used to pass a specific value to PsychImaging('OpenWindow', ...) +% If screenNumber is not specified, or if isempty(screenNumber) then the default: +% screenNumber = max(Screen('Screens')); +% will be used. +% +% This demo uses the 'GetNextDataType'/'GetFloatData' function pair that allows access to the following buffered samples and events +% (See EyeLink Programmers Guide manual > Data Structures > FEVENT): +% +% STARTBLINK 3 (the start of a blink) +% ENDBLINK 4 (the end of a blink) +% STARTSACC 5 (the start of a saccade) +% ENDSACC 6 (the end of a saccade) +% STARTFIX 7 (the start of a fixation) +% ENDFIX 8 (the end of a fixation) +% FIXUPDATE 9 (a fixation update during a fixation) +% SAMPLE_TYPE 200 (a sample) +% MISSING_DATA -32768 (missing data) +% +% Use buffered data if you need to: +% a) grab every single consecutive sample online +% b) grab event data (e.g. fixation/saccade/blink events) online +% +% Note that some buffered event data take some time to be available online due to the time involved +% in calculating velocity/acceleration. If you need to retrieve online gaze +% position as fast as possible and/or you don't need to get all subsequent samples or other +% events, then use the Eyelink('NewFloatSampleAvailable') / Eyelink('NewestFloatSample') function pair, +% as illustrated in the GCfastSamples.m example. +% --------------------------------------------------------------------------------------------- +% +% Events structure and fields available via the 'GetNextDataType'/'GetFloatData' function pair: +% STARTBLINK, STARTSACC, STARTFIX: +% type (number assigned to event - STARTBLINK=3, STARTSACC=5, STARTFIX=7) +% eye (0=left eye, 1=right eye) +% sttime (event start time) +% +% ENDBLINK: +% type (number assigned to event - ENDBLINK=4) +% eye (0=left eye, 1=right eye) +% sttime (event start time) +% entime (event end time) +% +% ENDSACC: +% type (number assigned to event - ENDSACC=6) +% eye (0=left eye, 1=right eye) +% sttime (event start time) +% entime (event end time) +% gstx (Saccade start x gaze position) +% gsty (Saccade start y gaze position) +% genx (Saccade end x gaze position) +% geny (Saccade end y gaze position) +% supd_x (Saccade start x 'pixel per degree' value) +% supd_y (Saccade start y 'pixel per degree' value) +% eupd_x (Saccade end x 'pixel per degree' value) +% eupd_y (Saccade end y 'pixel per degree' value) +% +% FIXUPDATE, ENDFIX: +% type (number assigned to event - FIXUPDATE=9, ENDFIX=8) +% eye (0=left eye, 1=right eye) +% sttime (event start time) +% entime (event end time) +% gavx (average gaze x position during fixation) +% gavy (average gaze y position during fixation) +% ava (average pupil size) +% supd_x (Fixation start x 'pixel per degree' value) +% supd_y (Fixation start y 'pixel per degree' value) +% eupd_x (Fixation end x 'pixel per degree' value) +% eupd_y (Fixation end y 'pixel per degree' value) +% +% SAMPLE_TYPE +% time (sample time) +% type (SAMPLE=200) +% pa ([lef eye pupil size, right eye pupil size]) +% gx ([left gaze x, right gaze x]) +% gy ([left gaze y, right gaze y]) +% rx (x 'pixel per degree' value) +% ry (y 'pixel per degree' value) +% buttons (button state and changes) +% hdata (contains a list of 8 fields. Only the first 4 values are important: +% [uncalibrated target sticker x, uncalibrated target sticker y, target sticker distance in mm, target flags) + +% Bring the Command Window to the front if it is already open +if ~IsOctave; commandwindow; end + +PsychDefaultSetup(2); + +% Use default screenNumber if none specified +if (nargin < 1) + screenNumber = []; +end +try + %% STEP 1: INITIALIZE EYELINK CONNECTION; OPEN EDF FILE; GET EYELINK TRACKER VERSION + + % Initialize EyeLink connection (dummymode = 0) or run in "Dummy Mode" without an EyeLink connection (dummymode = 1); + dummymode = 0; + EyelinkInit(dummymode); % Initialize EyeLink connection + status = Eyelink('IsConnected'); + if status < 1 % If EyeLink not connected + dummymode = 1; + end + + % Open dialog box for EyeLink Data file name entry. File name up to 8 characters + prompt = {'Enter EDF file name (up to 8 characters)'}; + dlg_title = 'Create EDF file'; + def = {'demo'}; % Create a default edf file name + answer = inputdlg(prompt, dlg_title, 1, def); % Prompt for new EDF file name + % Print some text in Matlab's Command Window if a file name has not been entered + if isempty(answer) + fprintf('Session cancelled by user\n') + cleanup; % Abort experiment (see cleanup function below) + return + end + edfFile = answer{1}; % Save file name to a variable + % Print some text in Matlab's Command Window if file name is longer than 8 characters + if length(edfFile) > 8 + fprintf('Filename needs to be no more than 8 characters long (letters, numbers and underscores only)\n'); + cleanup; % Abort experiment (see cleanup function below) + return + end + + % Open an EDF file and name it + failOpen = Eyelink('OpenFile', edfFile); + if failOpen ~= 0 % Abort if it fails to open + fprintf('Cannot create EDF file %s', edfFile); % Print some text in Matlab's Command Window + cleanup; %see cleanup function below + return + end + + % Get EyeLink tracker and software version + % returns 0 if not connected + % returns 'EYELINK I', 'EYELINK II x.xx', 'EYELINK CL x.xx' where 'x.xx' is the software version + ELsoftwareVersion = 0; % Default EyeLink version in dummy mode + [ver, versionstring] = Eyelink('GetTrackerVersion'); + if ver ~=0 % If connected to EyeLink + % Extract software version number. + [~, vnumcell] = regexp(versionstring,'.*?(\d)\.\d*?','Match','Tokens'); % Extract EL version before decimal point + ELsoftwareVersion = str2double(vnumcell{1}{1}); % Returns 1 for EyeLink I, 2 for EyeLink II, 3/4 for EyeLink 1K, 5 for EyeLink 1KPlus, 6 for Portable Duo + % Print some text in Matlab's Command Window + fprintf('Running experiment on %s version %d\n', versionstring, ver ); + end + % Add a line of text in the EDF file to identify the current experimemt name and session. This is optional. + % If your text starts with "RECORDED BY " it will be available in DataViewer's Inspector window by clicking + % the EDF session node in the top panel and looking for the "Recorded By:" field in the bottom panel of the Inspector. + preambleText = sprintf('RECORDED BY Psychtoolbox demo %s session name: %s', mfilename, edfFile); + Eyelink('Command', 'add_file_preamble_text "%s"', preambleText); + + + %% STEP 2: SELECT AVAILABLE SAMPLE/EVENT DATA + % See EyeLinkProgrammers Guide manual > Useful EyeLink Commands > File Data Control & Link Data Control + + % Select which events are saved in the EDF file. Include everything just in case + Eyelink('Command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); + % Select which events are available online for gaze-contingent experiments. Include everything just in case + Eyelink('Command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON,FIXUPDATE,INPUT'); + % Select which sample data is saved in EDF file or available online. Include everything just in case + if ELsoftwareVersion > 3 % Check tracker version and include 'HTARGET' to save head target sticker data for supported eye trackers + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,HTARGET,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,HTARGET,STATUS,INPUT'); + else + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); + end + + + %% STEP 3: OPEN GRAPHICS WINDOW + + % Open experiment graphics on the specified screen + if isempty(screenNumber) + screenNumber = max(Screen('Screens')); % Use default screen if none specified + end + + window = PsychImaging('OpenWindow', screenNumber, GrayIndex(screenNumber)); % Open graphics window + Screen('Flip', window); + + % Get max color value for rescaling to RGB for Host PC & Data Viewer integration + colorMaxVal = Screen('ColorRange', window); + % Return width and height of the graphics window/screen in pixels + [width, height] = Screen('WindowSize', window); + + + %% STEP 4: SET CALIBRATION SCREEN COLOURS; PROVIDE WINDOW SIZE TO EYELINK HOST & DATAVIEWER; SET CALIBRATION PARAMETERS; CALIBRATE + + % Provide EyeLink with some defaults, which are returned in the structure "el". + el = EyelinkInitDefaults(window); + % set calibration/validation/drift-check(or drift-correct) size as well as background and target colors. + % It is important that this background colour is similar to that of the stimuli to prevent large luminance-based + % pupil size changes (which can cause a drift in the eye movement data) + el.calibrationtargetsize = 3;% Outer target size as percentage of the screen + el.calibrationtargetwidth = 0.7;% Inner target size as percentage of the screen + el.backgroundcolour = repmat(GrayIndex(screenNumber),1,3); + el.calibrationtargetcolour = repmat(BlackIndex(screenNumber),1,3); + % set "Camera Setup" instructions text colour so it is different from background colour + el.msgfontcolour = repmat(BlackIndex(screenNumber),1,3); + + % Initialize PsychSound for calibration/validation audio feedback + % EyeLink Toolbox now supports PsychPortAudio integration and interop + % with legacy Snd() wrapping. Below we open the default audio device in + % output mode as master, create a slave device, and pass the device + % handle to el.ppa_pahandle. + % el.ppa_handle supports passing either standard mode handle, or as + % below one opened as a slave device. When el.ppa_handle is empty, for + % legacy support EyelinkUpdateDefaults() will open the default device + % and use that with Snd() interop, and close the device handle when + % calling Eyelink('Shutdown') at the end of the script. + InitializePsychSound(); + pamaster = PsychPortAudio('Open', [], 8+1); + PsychPortAudio('Start', pamaster); + pahandle = PsychPortAudio('OpenSlave', pamaster, 1); + el.ppa_pahandle = pahandle; + + % You must call this function to apply the changes made to the el structure above + EyelinkUpdateDefaults(el); + + % Set display coordinates for EyeLink data by entering left, top, right and bottom coordinates in screen pixels + Eyelink('Command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Write DISPLAY_COORDS message to EDF file: sets display coordinates in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Pre-trial Message Commands + Eyelink('Message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, width-1, height-1); + + % Set number of calibration/validation dots and spread: horizontal-only(H) or horizontal-vertical(HV) as H3, HV3, HV5, HV9 or HV13 + Eyelink('Command', 'calibration_type = HV9'); % horizontal-vertical 9-points + % Allow a supported EyeLink Host PC button box to accept calibration or drift-check/correction targets via button 5 + Eyelink('Command', 'button_function 5 "accept_target_fixation"'); + % Hide mouse cursor + HideCursor(window); + % Suppress keypress output to command window. + ListenChar(-1); + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Put EyeLink Host PC in Camera Setup mode for participant setup/calibration + EyelinkDoTrackerSetup(el); + + + %% STEP 5: TRIAL LOOP. + + spaceBar = KbName('space');% Identify keyboard key code for space bar to end each trial later on + imgList = {'img1.jpg' 'img2.jpg'};% Provide image list for 2 trials + for i = 1:length(imgList) + + % Reset some trial variables + Sstim = 0; % Reset initial stimulus marker + eventCount = 0; % Reset event counter + rt = 0;% Default value for first saccade reaction time + + % STEP 5.1: PREBUILD STIMULUS (GREY BACKGROUND + IMAGE + TEXT) + + % Prepare grey background on backbuffer + Screen('FillRect', window, el.backgroundcolour); + % Use 'drawBuffer' to copy unprocessed backbuffer images without additional processing. Prevents image size info issues on Retina displays + backgroundArray = Screen('GetImage', window, [], 'drawBuffer'); % Copy unprocessed backbuffer + backgroundTexture = Screen('MakeTexture', window, backgroundArray); % Convert background to texture so it is ready for drawing later on + % Prepare image on backbuffer + imgName = char(imgList(i)); % Get image file name for current trial + imgInfo = imfinfo(imgName); % Get image file info + imgData = imread(imgName); % Read image from file + imgTexture = Screen('MakeTexture',window, imgData); % Convert image file to texture + Screen('DrawTexture', window, imgTexture); % Prepare image texture on backbuffer + % Prepare text on backbuffer + Screen('TextSize', window, 30); % Specify text size + Screen('DrawText', window, 'Press space bar to end trial', 5, height-35, 0); % Prepare text on backbuffer + % Save complete backbuffer as trial*.bmp to be used as stimulus and as Host PC & DataViewer backdrop + stimName = ['trial' num2str(i) '.bmp']; % Prepare stimulus file name + stimArray = Screen('GetImage', window, [], 'drawBuffer'); % Copy backbuffer to be used as stimulus + imwrite(stimArray, stimName); % Save .bmp stimulus file in experment folder + % Convert stimulus to texture so it is ready for drawing later on + stimInfo = imfinfo(stimName); % Get stimulus info + stimTexture = Screen('MakeTexture', window, stimArray); % Convert to texture + + % STEP 5.2: START TRIAL; SHOW TRIAL INFO ON HOST PC; SHOW BACKDROP IMAGE AND/OR DRAW FEEDBACK GRAPHICS ON HOST PC; DRIFT-CHECK/CORRECTION + + % Write TRIALID message to EDF file: marks the start of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIALID %d', i); + + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Supply the trial number as a line of text on Host PC screen + Eyelink('Command', 'record_status_message "TRIAL %d/%d"', i, length(imgList)); + + % Draw graphics on the EyeLink Host PC display. See COMMANDS.INI in the Host PC's exe folder for a list of commands + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode before drawing Host PC graphics and before recording + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Optional: Send an image to the Host PC to be displayed as the backdrop image over which + % the gaze-cursor is overlayed during trial recordings. + % See Eyelink('ImageTransfer?') for information about supported syntax and compatible image formats. + % Below, we use the new option to pass image data from imread() as the imageArray parameter, which + % enables the use of many image formats. + % [status] = Eyelink('ImageTransfer', imageArray, xs, ys, width, height, xd, yd, options); + % xs, ys: top-left corner of the region to be transferred within the source image + % width, height: size of region to be transferred within the source image (note, values of 0 will include the entire width/height) + % xd, yd: location (top-left) where image region to be transferred will be presented on the Host PC + % This image transfer function works for non-resized image presentation only. If you need to resize images and use this function please resize + % the original image files beforehand + transferStatus = Eyelink('ImageTransfer', stimArray, 0, 0, 0, 0, 0, 0); + if dummymode == 0 && transferStatus ~= 0 % If connected to EyeLink and image transfer fails + fprintf('Image transfer Failed\n'); % Print some text in Matlab's Command Window + end + + % Optional: draw feedback box and lines on Host PC interface instead of (or on top of) backdrop image. + % See section 25.7 'Drawing Commands' in the EyeLink Programmers Guide manual + Eyelink('Command', 'draw_box %d %d %d %d 15', round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2)); + + % Perform a drift check/correction. + % Optionally provide x y target location, otherwise target is presented on screen centre + EyelinkDoDriftCorrection(el, round(width/2), round(height/2)); + + %STEP 5.3: START RECORDING + + % Put tracker in idle/offline mode before recording. Eyelink('SetOfflineMode') is recommended + % however if Eyelink('Command', 'set_idle_mode') is used allow 50ms before recording as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode before recording + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode before recording + Eyelink('StartRecording'); % Start tracker recording + WaitSecs(0.1); % Allow some time to record a few samples before presenting first stimulus + + % STEP 5.4: PRESENT STIMULUS; CREATE DATAVIEWER BACKDROP AND INTEREST AREA + + % Check which eye is available for gaze-contingent drawing. Returns 0 (left), 1 (right) or 2 (binocular) + eyeUsed = Eyelink('EyeAvailable'); + % Get events from right eye if binocular + if eyeUsed == 2 + eyeUsed = 1; + end + + bufferStart = GetSecs; % Start a ~100ms counter + while 1 % loop until error, space bar or button press + % Check that eye tracker is still recording. Otherwise close and transfer copy of EDF file to Display PC + err = Eyelink('CheckRecording'); + if(err ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below + cleanup; % Abort experiment (see cleanup function below) + return + end + + % Run the 'GetNextDataType'/'GetFloatData' function pair in a loop for ~100ms before presenting initial stimulus. + % This will clear old data from the buffer and allow access to the most recent online saccade event. + if GetSecs - bufferStart > 0.1 && Sstim == 0 % If 100ms have elapsed and initial trial image has not yet been presented... + % Present initial trial image without gaze-contingent dot + Screen('DrawTexture', window, stimTexture); % Prepare stimulus texture on backbuffer + Screen('Flip', window); % Present stimulus + % Write message to EDF file to mark the start time of stimulus presentation. + Eyelink('Message', 'STIM_ONSET'); + % Write !V IMGLOAD message to EDF file: creates backdrop image for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Image Commands + Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', stimName, width/2, height/2); + % Write !V IAREA message to EDF file: creates interest area around image in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Interest Area Commands + Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 1, round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2),'IMAGE_IA'); + + % Return the current EDF time (in seconds) to make sure we use only online saccades that started after stimulus presentation + StimEDFtime = (Eyelink('TrackerTime'))*1000; % Multiply by 1000 to convert to milliseconds + Sstim = 1; % Initial stimulus has been drawn + end + + % STEP 5.5: DRAW GAZE-CONTINGENT DOT; WAIT FOR KEYPRESS/BUTTON; SHOW BLANK SCREEN; STOP RECORDING + + % Get next data item (sample or event) from link buffer. + % This is equivalent to EyeLink_get_next_data() in C API. See EyeLink Programmers Guide manual > Message and Command Sending/Receiving > Functions + evtype = Eyelink('GetNextDataType'); + + % Read item type returned by getnextdatatype. Wait for end of saccade (ENDSACC) event + % 'GetFloatData' is equivalent to eyelink_get_float_data() in C API. See EyeLink Programmers Guide manual > Function Lists > Message and Command Sending/Receiving > Functions + if evtype == el.ENDSACC % if end of saccade (ENDSACC) event is returned + evt = Eyelink('GetFloatData', evtype); % access the ENDSACC event structure + if Sstim == 1 % Only draw gaze-contingent dot after initial trial image is presented (i.e. after looping for ~100ms) + % evt.eye returns the eye (0=left, 1=right) for this event. When recording binocular data both left and right eye events are available + % Use event only if it is from the expected eye and start of saccade (evt.sttime) occurred after trial image onset + if evt.eye == eyeUsed && evt.sttime > StimEDFtime + eventCount = eventCount + 1; % Add 1 to saccade event counter + % Write message to EDF file to mark time when end of saccade is detected (before drawing of gaze-contingent target) + Eyelink('Message', 'END_SACCADE_DETECTED_%d', eventCount); + % Save event fields as variables. See EyeLink Programmers Guide manual > Data Structures > FEVENT + sac_start = evt.sttime; % Saccade start EDF time + x_end = evt.genx; % Saccade end x gaze position + y_end = evt.geny; % Saccade end y gaze position + + % The following event properties are not used in this demo but are available from the ENDSACC event: + % evt.type; % Event type (STARTBLINK=3, ENDBLINK=4, STARTSACC=5, ENDSACC=6, STARTFIX=7, ENDFIX=8, FIXUPDATE=9, SAMPLE =200) + % evt.eye; % Event eye: 0=left eye, 1=right eye + % evt.entime; % Saccade end EDF time + % evt.gstx; % Saccade start x gaze position + % evt.gsty; % Saccade start y gaze position + % evt.supd_x; % Saccade start x 'pixel per deggree' value + % evt.supd_y; % Saccade start y 'pixel per degree' value + % evt.eupd_x; % Saccade end x 'pixel per degree' value + % evt.eupd_y; % Saccade end y 'pixel per degree' value + + % Draw gaze-contingent dot + Screen('DrawTexture', window, stimTexture); % Prepare stimulus texture on backbuffer + % Superimpose a red gaze-contingent dot using saccade end position coordinates + Screen('FillOval', window, [255,0,0], [x_end-20, y_end-20, x_end+20, y_end+20]); + Screen('Flip', window); % Present stimulus + % Write message to EDF file to mark time of new target drawing. + Eyelink('Message', 'TARGET_NEW_POS_%d', eventCount); + % Write !V IMGLOAD message to EDF file: redraw backdrop image for DataViewer before drawing the new target location on top + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Image Commands + Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', stimName, width/2, height/2); + % Write !V FIXPOINT message to EDF file: draws the new target location on top of the trial image in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing Commands + Eyelink('Message', '!V FIXPOINT %d %d %d %d %d %d %d %d %d %d', 255, 0, 0, 255, 0, 0, round(x_end), round(y_end), 40, 40, x_end, y_end); + % Calculate start of first saccade reaction time + if eventCount == 1 + rt = round(sac_start - StimEDFtime); + end + end + end + end + % End trial if space bar is pressed + [~, ~, keyCode] = KbCheck; + if keyCode(spaceBar) + % Write message to EDF file to mark the space bar press time + Eyelink('Message', 'KEY_PRESSED'); + break; + end + end % End of while loop + + % Draw blank screen at end of trial + Screen('DrawTexture', window, backgroundTexture); % Prepare background texture on backbuffer + Screen('Flip', window); % Present blank screen + % Write message to EDF file to mark time when blank screen is presented + Eyelink('Message', 'BLANK_SCREEN'); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Stop recording eye movements at the end of each trial + WaitSecs(0.1); % Add 100 msec of data to catch final events before stopping + Eyelink('StopRecording'); % Stop tracker recording + + % STEP 5.6: CREATE VARIABLES FOR DATAVIEWER; END TRIAL + + % Write !V TRIAL_VAR messages to EDF file: creates trial variables in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Trial Message Commands + Eyelink('Message', '!V TRIAL_VAR iteration %d', i); % Trial iteration + Eyelink('Message', '!V TRIAL_VAR image %s', imgName); % Image name + Eyelink('Message', '!V TRIAL_VAR event_count %d', eventCount);% Number of online ENDSACC events per trial + WaitSecs(0.001); % Allow some time between messages. Some messages can be lost if too many are written at the same time + Eyelink('Message', '!V TRIAL_VAR rt_firstSac %d', rt);% Reaction time: first saccade start time relative to stim onset + % Write TRIAL_RESULT message to EDF file: marks the end of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIAL_RESULT 0'); + WaitSecs(0.01); % Allow some time before ending the trial + + % Clear Screen() textures that were initialized for each trial iteration + Screen('Close', backgroundTexture); + Screen('Close', imgTexture); + Screen('Close', stimTexture); + end % End trial loop + + + %% STEP 6: CLOSE EDF FILE. TRANSFER EDF COPY TO DISPLAY PC. CLOSE EYELINK CONNECTION. FINISH UP + + % Put tracker in idle/offline mode before closing file. Eyelink('SetOfflineMode') is recommended. + % However if Eyelink('Command', 'set_idle_mode') is used, allow 50ms before closing the file as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('Command', 'clear_screen 0'); % Clear Host PC backdrop graphics at the end of the experiment + WaitSecs(0.5); % Allow some time before closing and transferring file + Eyelink('CloseFile'); % Close EDF file on Host PC + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below +catch % If syntax error is detected + cleanup; + % Print error message and line number in Matlab's Command Window + psychrethrow(psychlasterror); +end +PsychPortAudio('Close', pahandle); +PsychPortAudio('Close', pamaster); + + +% Cleanup function used throughout the script above + function cleanup + sca; % PTB's wrapper for Screen('CloseAll') & related cleanup, e.g. ShowCursor + Eyelink('Shutdown'); % Close EyeLink connection + ListenChar(0); % Restore keyboard output to Matlab + if ~IsOctave; commandwindow; end % Bring Command Window to front + end + +% Function for transferring copy of EDF file to the experiment folder on Display PC. +% Allows for optional destination path which is different from experiment folder + function transferFile + try + if dummymode ==0 % If connected to EyeLink + % Show 'Receiving data file...' text until file transfer is complete + Screen('FillRect', window, el.backgroundcolour); % Prepare background on backbuffer + Screen('DrawText', window, 'Receiving data file...', 5, height-35, 0); % Prepare text + Screen('Flip', window); % Present text + fprintf('Receiving data file ''%s.edf''\n', edfFile); % Print some text in Matlab's Command Window + + % Transfer EDF file to Host PC + % [status =] Eyelink('ReceiveFile',['src'], ['dest'], ['dest_is_path']) + status = Eyelink('ReceiveFile'); + + % Check if EDF file has been transferred successfully and print file size in Matlab's Command Window + if status > 0 + fprintf('EDF file size: %.1f KB\n', status/1024); % Divide file size by 1024 to convert bytes to KB + end + % Print transferred EDF file path in Matlab's Command Window + fprintf('Data file ''%s.edf'' can be found in ''%s''\n', edfFile, pwd); + else + fprintf('No EDF file saved in Dummy mode\n'); + end + cleanup; + catch % Catch a file-transfer error and print some text in Matlab's Command Window + fprintf('Problem receiving data file ''%s''\n', edfFile); + cleanup; + psychrethrow(psychlasterror); + end + end +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedEndSacEvents/img1.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedEndSacEvents/img1.jpg new file mode 100644 index 0000000000..00663e2e44 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedEndSacEvents/img1.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedEndSacEvents/img2.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedEndSacEvents/img2.jpg new file mode 100644 index 0000000000..3fa77e178b Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedEndSacEvents/img2.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedFixUpdateEvents/EyeLink_BufferedFixUpdateEvents.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedFixUpdateEvents/EyeLink_BufferedFixUpdateEvents.m new file mode 100644 index 0000000000..a1943d544a --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedFixUpdateEvents/EyeLink_BufferedFixUpdateEvents.m @@ -0,0 +1,518 @@ +function EyeLink_BufferedFixUpdateEvents(screenNumber) +% A simple EyeLink gaze-contingent demo showing how to retrieve online events from a buffer. +% In each trial an image is presented with a red gaze-contingent dot overlaid on top. +% The dot's location is based on the average x y coordinates of fixations updated online every 50ms via a FIXUPDATE event. +% See EyeLink Programmers Guide manual > Experiment Templates Overview > Control > Fixation Update Events +% Each trial ends when the space bar is pressed. +% +% FIXUPDATE events send updates about a current fixation at regular intervals. By default an interval of 50ms is used. +% The first update is sent one update interval after the start of the fixation, and the last is sent at +% the end of the fixation. This demo uses FIXUPDATE events to get the averaged gaze x y position across each fixation interval. +% +% Usage: +% Eyelink_BufferedFixUpdateEvents(screenNumber) +% +% screenNumber is an optional parameter which can be used to pass a specific value to PsychImaging('OpenWindow', ...) +% If screenNumber is not specified, or if isempty(screenNumber) then the default: +% screenNumber = max(Screen('Screens')); +% will be used. +% +% This demo uses the 'GetNextDataType'/'GetFloatData' function pair that allows access to the following buffered samples and events +% (See EyeLink Programmers Guide manual > Data Structures > FEVENT): +% +% STARTBLINK 3 (the start of a blink) +% ENDBLINK 4 (the end of a blink) +% STARTSACC 5 (the start of a saccade) +% ENDSACC 6 (the end of a saccade) +% STARTFIX 7 (the start of a fixation) +% ENDFIX 8 (the end of a fixation) +% FIXUPDATE 9 (a fixation update during a fixation) +% SAMPLE_TYPE 200 (a sample) +% MISSING_DATA -32768 (missing data) +% +% Use buffered data if you need to: +% a) grab every single consecutive sample online +% b) grab event data (e.g. fixation/saccade/blink events) online +% +% Note that some buffered event data take some time to be available online due to the time involved +% in calculating velocity/acceleration. If you need to retrieve online gaze +% position as fast as possible and/or you don't need to get all subsequent samples or other +% events, then use the Eyelink('NewFloatSampleAvailable') / Eyelink('NewestFloatSample') function pair, +% as illustrated in the GCfastSamples.m example. +% --------------------------------------------------------------------------------------------- +% +% Events structure and fields available via the 'GetNextDataType'/'GetFloatData' function pair: +% STARTBLINK, STARTSACC, STARTFIX: +% type (number assigned to event - STARTBLINK=3, STARTSACC=5, STARTFIX=7) +% eye (0=left eye, 1=right eye) +% sttime (event start time) +% +% ENDBLINK: +% type (number assigned to event - ENDBLINK=4) +% eye (0=left eye, 1=right eye) +% sttime (event start time) +% entime (event end time) +% +% ENDSACC: +% type (number assigned to event - ENDSACC=6) +% eye (0=left eye, 1=right eye) +% sttime (event start time) +% entime (event end time) +% gstx (Saccade start x gaze position) +% gsty (Saccade start y gaze position) +% genx (Saccade end x gaze position) +% geny (Saccade end y gaze position) +% supd_x (Saccade start x 'pixel per degree' value) +% supd_y (Saccade start y 'pixel per degree' value) +% eupd_x (Saccade end x 'pixel per degree' value) +% eupd_y (Saccade end y 'pixel per degree' value) +% +% FIXUPDATE, ENDFIX: +% type (number assigned to event - FIXUPDATE=9, ENDFIX=8) +% eye (0=left eye, 1=right eye) +% sttime (event start time) +% entime (event end time) +% gavx (average gaze x position during fixation) +% gavy (average gaze y position during fixation) +% ava (average pupil size) +% supd_x (Fixation start x 'pixel per degree' value) +% supd_y (Fixation start y 'pixel per degree' value) +% eupd_x (Fixation end x 'pixel per degree' value) +% eupd_y (Fixation end y 'pixel per degree' value) +% +% SAMPLE_TYPE +% time (sample time) +% type (SAMPLE=200) +% pa ([lef eye pupil size, right eye pupil size]) +% gx ([left gaze x, right gaze x]) +% gy ([left gaze y, right gaze y]) +% rx (x 'pixel per degree' value) +% ry (y 'pixel per degree' value) +% buttons (button state and changes) +% hdata (contains a list of 8 fields. Only the first 4 values are important: +% [uncalibrated target sticker x, uncalibrated target sticker y, target sticker distance in mm, target flags) + +% Bring the Command Window to the front if it is already open +if ~IsOctave; commandwindow; end + +PsychDefaultSetup(2); + +% Use default screenNumber if none specified +if (nargin < 1) + screenNumber = []; +end +try + %% STEP 1: INITIALIZE EYELINK CONNECTION; OPEN EDF FILE; GET EYELINK TRACKER VERSION + + % Initialize EyeLink connection (dummymode = 0) or run in "Dummy Mode" without an EyeLink connection (dummymode = 1); + dummymode = 0; + EyelinkInit(dummymode); % Initialize EyeLink connection + status = Eyelink('IsConnected'); + if status < 1 % If EyeLink not connected + dummymode = 1; + end + + % Open dialog box for EyeLink Data file name entry. File name up to 8 characters + prompt = {'Enter EDF file name (up to 8 characters)'}; + dlg_title = 'Create EDF file'; + def = {'demo'}; % Create a default edf file name + answer = inputdlg(prompt, dlg_title, 1, def); % Prompt for new EDF file name + % Print some text in Matlab's Command Window if a file name has not been entered + if isempty(answer) + fprintf('Session cancelled by user\n') + cleanup; % Abort experiment (see cleanup function below) + return + end + edfFile = answer{1}; % Save file name to a variable + % Print some text in Matlab's Command Window if file name is longer than 8 characters + if length(edfFile) > 8 + fprintf('Filename needs to be no more than 8 characters long (letters, numbers and underscores only)\n'); + cleanup; % Abort experiment (see cleanup function below) + return + end + + % Open an EDF file and name it + failOpen = Eyelink('OpenFile', edfFile); + if failOpen ~= 0 % Abort if it fails to open + fprintf('Cannot create EDF file %s', edfFile); % Print some text in Matlab's Command Window + cleanup; %see cleanup function below + return + end + + % Get EyeLink tracker and software version + % returns 0 if not connected + % returns 'EYELINK I', 'EYELINK II x.xx', 'EYELINK CL x.xx' where 'x.xx' is the software version + ELsoftwareVersion = 0; % Default EyeLink version in dummy mode + [ver, versionstring] = Eyelink('GetTrackerVersion'); + if dummymode == 0 % If connected to EyeLink + % Extract software version number. + [~, vnumcell] = regexp(versionstring,'.*?(\d)\.\d*?','Match','Tokens'); % Extract EL version before decimal point + ELsoftwareVersion = str2double(vnumcell{1}{1}); % Returns 1 for EyeLink I, 2 for EyeLink II, 3/4 for EyeLink 1K, 5 for EyeLink 1KPlus, 6 for Portable Duo + % Print some text in Matlab's Command Window + fprintf('Running experiment on %s version %d\n', versionstring, ver ); + end + % Add a line of text in the EDF file to identify the current experimemt name and session. This is optional. + % If your text starts with "RECORDED BY " it will be available in DataViewer's Inspector window by clicking + % the EDF session node in the top panel and looking for the "Recorded By:" field in the bottom panel of the Inspector. + preambleText = sprintf('RECORDED BY Psychtoolbox demo %s session name: %s', mfilename, edfFile); + Eyelink('Command', 'add_file_preamble_text "%s"', preambleText); + + + %% STEP 2: SELECT AVAILABLE SAMPLE/EVENT DATA + % See EyeLinkProgrammers Guide manual > Useful EyeLink Commands > File Data Control & Link Data Control + + % Select which events are saved in the EDF file. Include everything just in case + Eyelink('Command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); + % Select which events are available online for gaze-contingent experiments. Include everything just in case + Eyelink('Command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON,FIXUPDATE,INPUT'); + % Select which sample data is saved in EDF file or available online. Include everything just in case + if ELsoftwareVersion > 3 % Check tracker version and include 'HTARGET' to save head target sticker data for supported eye trackers + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,HTARGET,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,HTARGET,STATUS,INPUT'); + else + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); + end + + + %% STEP 3: OPEN GRAPHICS WINDOW + + % Open experiment graphics on the specified screen + if isempty(screenNumber) + screenNumber = max(Screen('Screens')); % Use default screen if none specified + end + + window = PsychImaging('OpenWindow', screenNumber, GrayIndex(screenNumber)); % Open graphics window + Screen('Flip', window); + + % Get max color value for rescaling to RGB for Host PC & Data Viewer integration + colorMaxVal = Screen('ColorRange', window); + % Return width and height of the graphics window/screen in pixels + [width, height] = Screen('WindowSize', window); + + + %% STEP 4: SET CALIBRATION SCREEN COLOURS; PROVIDE WINDOW SIZE TO EYELINK HOST & DATAVIEWER; SET CALIBRATION PARAMETERS; CALIBRATE + + % Provide EyeLink with some defaults, which are returned in the structure "el". + el = EyelinkInitDefaults(window); + % set calibration/validation/drift-check(or drift-correct) size as well as background and target colors. + % It is important that this background colour is similar to that of the stimuli to prevent large luminance-based + % pupil size changes (which can cause a drift in the eye movement data) + el.calibrationtargetsize = 3;% Outer target size as percentage of the screen + el.calibrationtargetwidth = 0.7;% Inner target size as percentage of the screen + el.backgroundcolour = repmat(GrayIndex(screenNumber),1,3); + el.calibrationtargetcolour = repmat(BlackIndex(screenNumber),1,3); + % set "Camera Setup" instructions text colour so it is different from background colour + el.msgfontcolour = repmat(BlackIndex(screenNumber),1,3); + + % Initialize PsychSound for calibration/validation audio feedback + % EyeLink Toolbox now supports PsychPortAudio integration and interop + % with legacy Snd() wrapping. Below we open the default audio device in + % output mode as master, create a slave device, and pass the device + % handle to el.ppa_pahandle. + % el.ppa_handle supports passing either standard mode handle, or as + % below one opened as a slave device. When el.ppa_handle is empty, for + % legacy support EyelinkUpdateDefaults() will open the default device + % and use that with Snd() interop, and close the device handle when + % calling Eyelink('Shutdown') at the end of the script. + InitializePsychSound(); + pamaster = PsychPortAudio('Open', [], 8+1); + PsychPortAudio('Start', pamaster); + pahandle = PsychPortAudio('OpenSlave', pamaster, 1); + el.ppa_pahandle = pahandle; + + % You must call this function to apply the changes made to the el structure above + EyelinkUpdateDefaults(el); + + % Set display coordinates for EyeLink data by entering left, top, right and bottom coordinates in screen pixels + Eyelink('Command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Write DISPLAY_COORDS message to EDF file: sets display coordinates in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Pre-trial Message Commands + Eyelink('Message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, width-1, height-1); + + % Set number of calibration/validation dots and spread: horizontal-only(H) or horizontal-vertical(HV) as H3, HV3, HV5, HV9 or HV13 + Eyelink('Command', 'calibration_type = HV9'); % horizontal-vertical 9-points + % Allow a supported EyeLink Host PC button box to accept calibration or drift-check/correction targets via button 5 + Eyelink('Command', 'button_function 5 "accept_target_fixation"'); + % Hide mouse cursor + HideCursor(window); + % Suppress keypress output to command window. + ListenChar(-1); + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Put EyeLink Host PC in Camera Setup mode for participant setup/calibration + EyelinkDoTrackerSetup(el); + + + %% STEP 5: TRIAL LOOP. + + spaceBar = KbName('space');% Identify keyboard key code for space bar to end each trial later on + imgList = {'img1.jpg' 'img2.jpg'};% Provide image list for 2 trials + for i = 1:length(imgList) + Sstim = 0; % Reset initial stimulus marker + + % STEP 5.1: PREBUILD STIMULUS (GREY BACKGROUND + IMAGE + TEXT) + + % Prepare grey background on backbuffer + Screen('FillRect', window, el.backgroundcolour); + % Use 'drawBuffer' to copy unprocessed backbuffer images without additional processing. Prevents image size info issues on Retina displays + backgroundArray = Screen('GetImage', window, [], 'drawBuffer'); % Copy unprocessed backbuffer + backgroundTexture = Screen('MakeTexture', window, backgroundArray); % Convert background to texture so it is ready for drawing later on + % Prepare image on backbuffer + imgName = char(imgList(i)); % Get image file name for current trial + imgInfo = imfinfo(imgName); % Get image file info + imgData = imread(imgName); % Read image from file + imgTexture = Screen('MakeTexture',window, imgData); % Convert image file to texture + Screen('DrawTexture', window, imgTexture); % Prepare image texture on backbuffer + % Prepare text on backbuffer + Screen('TextSize', window, 30); % Specify text size + Screen('DrawText', window, 'Press space bar to end trial', 5, height-35, 0); % Prepare text on backbuffer + % Save complete backbuffer as trial*.bmp to be used as stimulus and as Host PC & DataViewer backdrop + stimName = ['trial' num2str(i) '.bmp']; % Prepare stimulus file name + stimArray = Screen('GetImage', window, [], 'drawBuffer'); % Copy backbuffer to be used as stimulus + imwrite(stimArray, stimName); % Save .bmp stimulus file in experment folder + % Convert stimulus to texture so it is ready for drawing later on + stimInfo = imfinfo(stimName); % Get stimulus info + stimTexture = Screen('MakeTexture', window, stimArray); % Convert to texture + + % STEP 5.2: START TRIAL; SHOW TRIAL INFO ON HOST PC; SHOW BACKDROP IMAGE AND/OR DRAW FEEDBACK GRAPHICS ON HOST PC; DRIFT-CHECK/CORRECTION + + % Write TRIALID message to EDF file: marks the start of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIALID %d', i); + + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Supply the trial number as a line of text on Host PC screen + Eyelink('Command', 'record_status_message "TRIAL %d/%d"', i, length(imgList)); + + % Draw graphics on the EyeLink Host PC display. See COMMANDS.INI in the Host PC's exe folder for a list of commands + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode before drawing Host PC graphics and before recording + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Optional: Send an image to the Host PC to be displayed as the backdrop image over which + % the gaze-cursor is overlayed during trial recordings. + % See Eyelink('ImageTransfer?') for information about supported syntax and compatible image formats. + % Below, we use the new option to pass image data from imread() as the imageArray parameter, which + % enables the use of many image formats. + % [status] = Eyelink('ImageTransfer', imageArray, xs, ys, width, height, xd, yd, options); + % xs, ys: top-left corner of the region to be transferred within the source image + % width, height: size of region to be transferred within the source image (note, values of 0 will include the entire width/height) + % xd, yd: location (top-left) where image region to be transferred will be presented on the Host PC + % This image transfer function works for non-resized image presentation only. If you need to resize images and use this function please resize + % the original image files beforehand + transferStatus = Eyelink('ImageTransfer', stimArray, 0, 0, 0, 0, 0, 0); + if dummymode == 0 && transferStatus ~= 0 % If connected to EyeLink and image transfer fails + fprintf('Image transfer Failed\n'); % Print some text in Matlab's Command Window + end + + % Optional: draw feedback box and lines on Host PC interface instead of (or on top of) backdrop image. + % See section 25.7 'Drawing Commands' in the EyeLink Programmers Guide manual + Eyelink('Command', 'draw_box %d %d %d %d 15', round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2)); + + % Perform a drift check/correction. + % Optionally provide x y target location, otherwise target is presented on screen centre + EyelinkDoDriftCorrection(el, round(width/2), round(height/2)); + + %STEP 5.3: START RECORDING + + % Put tracker in idle/offline mode before recording. Eyelink('SetOfflineMode') is recommended + % however if Eyelink('Command', 'set_idle_mode') is used allow 50ms before recording as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode before recording + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode before recording + Eyelink('StartRecording'); % Start tracker recording + WaitSecs(0.1); % Allow some time to record a few samples before presenting first stimulus + + % STEP 5.4: PRESENT STIMULUS; CREATE DATAVIEWER BACKDROP AND INTEREST AREA + + % Check which eye is available for gaze-contingent drawing. Returns 0 (left), 1 (right) or 2 (binocular) + eyeUsed = Eyelink('EyeAvailable'); + % Get events from right eye if binocular + if eyeUsed == 2 + eyeUsed = 1; + end + + bufferStart = GetSecs; % Start a ~100ms counter + while 1 % loop until error or space bar is press + % Check that eye tracker is still recording. Otherwise close and transfer copy of EDF file to Display PC + err = Eyelink('CheckRecording'); + if(err ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below + cleanup; % Abort experiment (see cleanup function below) + return + end + + % Run the 'GetNextDataType'/'GetFloatData' function pair in a loop for ~100ms before presenting initial stimulus. + % This will clear old data from the buffer and allow access to the most recent online FIXUPDATE event. + if GetSecs - bufferStart > 0.1 && Sstim == 0 % If 100ms have elapsed and initial trial image has not yet been presented... + % Present initial trial image without gaze-contingent dot + Screen('DrawTexture', window, stimTexture); % Prepare stimulus texture on backbuffer + Screen('Flip', window); % Present stimulus + % Write message to EDF file to mark the start time of stimulus presentation. + Eyelink('Message', 'STIM_ONSET'); + % Write !V IMGLOAD message to EDF file: creates backdrop image for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Image Commands + Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', stimName, width/2, height/2); + % Write !V IAREA message to EDF file: creates interest area around image in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Interest Area Commands + Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 1, round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2),'IMAGE_IA'); + + % Return the current EDF time (in seconds) to make sure we use only online events that started after stimulus presentation + StimEDFtime = (Eyelink('TrackerTime'))*1000; % Multiply by 1000 to convert to milliseconds + Sstim = 1; % Initial stimulus has been drawn + end + + % STEP 5.5: DRAW GAZE-CONTINGENT DOT; WAIT FOR KEYPRESS; SHOW BLANK SCREEN; STOP RECORDING + + % Get next data item (sample or event) from link buffer. + % This is equivalent to EyeLink_get_next_data() in C API. See EyeLink Programmers Guide manual > Message and Command Sending/Receiving > Functions + evtype = Eyelink('GetNextDataType'); + + % Read item type returned by getnextdatatype. Wait for a fixation update (FIXUPDATE) event + % 'GetFloatData' is equivalent to eyelink_get_float_data() in C API. See EyeLink Programmers Guide manual > Function Lists > Message and Command Sending/Receiving > Functions + if evtype == el.FIXUPDATE % if fixation update (FIXUPDATE) event is returned + evt = Eyelink('GetFloatData', evtype); % access the FIXUPDATE event structure + if Sstim == 1 % Only draw gaze-contingent dot after initial trial image is presented (i.e. after looping for ~100ms) + % evt.eye returns the eye (0=left, 1=right) for this event. When recording binocular data both left and right eye events are available + % Use event only if it is from the expected eye and start of event (evt.sttime) occurred after trial image onset + if evt.eye == eyeUsed && evt.sttime > StimEDFtime + % Save event fields as variables. See EyeLink Programmers Guide manual > Data Structures > FEVENT + x_avg = evt.gavx; % Fixation update average x position + y_avg = evt.gavy; % Fixation update average y position + + % The following event properties are not used in this demo but are available from the FIXUPDATE event: + % type (number assigned to event - FIXUPDATE=9, ENDFIX=8) + % eye (0=left eye, 1=right eye) + % sttime (FIXUPDATE interval start time) + % entime (FIXUPDATE interval end time) + % gavx (average gaze x position during FIXUPDATE interval) + % gavy (average gaze y position during FIXUPDATE interval) + % ava (average pupil size) + % supd_x (Fixation start x 'pixel per degree' value) + % supd_y (Fixation start y 'pixel per degree' value) + % eupd_x (Fixation end x 'pixel per degree' value) + % eupd_y (Fixation end y 'pixel per degree' value) + + % Draw gaze-contingent dot + Screen('DrawTexture', window, stimTexture); % Prepare stimulus texture on backbuffer + % Superimpose a red gaze-contingent dot using FIXUPDATE avg x y gaze coordinates + Screen('FillOval', window, [255,0,0], [x_avg-20, y_avg-20, x_avg+20, y_avg+20]); + Screen('Flip', window); % Present stimulus + % Write !V IMGLOAD message to EDF file: redraw backdrop image for DataViewer before drawing the new target location on top + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Image Commands + Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', stimName, width/2, height/2); + % Write !V FIXPOINT message to EDF file: draws the new target location on top of the trial image in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing Commands + Eyelink('Message', '!V FIXPOINT %d %d %d %d %d %d %d %d %d %d', 255, 0, 0, 255, 0, 0, round(x_avg), round(y_avg), 40, 40, x_avg, y_avg); + end + end + end + % End trial if space bar is pressed + [~, ~, keyCode] = KbCheck; + if keyCode(spaceBar) + % Write message to EDF file to mark the space bar press time + Eyelink('Message', 'KEY_PRESSED'); + break; + end + end % End of while loop + + % Draw blank screen at end of trial + Screen('DrawTexture', window, backgroundTexture); % Prepare background texture on backbuffer + Screen('Flip', window); % Present blank screen + % Write message to EDF file to mark time when blank screen is presented + Eyelink('Message', 'BLANK_SCREEN'); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Stop recording eye movements at the end of each trial + WaitSecs(0.1); % Add 100 msec of data to catch final events before stopping + Eyelink('StopRecording'); % Stop tracker recording + + % STEP 5.6: CREATE VARIABLES FOR DATAVIEWER; END TRIAL + + % Write !V TRIAL_VAR messages to EDF file: creates trial variables in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Trial Message Commands + Eyelink('Message', '!V TRIAL_VAR iteration %d', i); % Trial iteration + Eyelink('Message', '!V TRIAL_VAR image %s', imgName); % Image name + % Write TRIAL_RESULT message to EDF file: marks the end of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIAL_RESULT 0'); + WaitSecs(0.01); % Allow some time before ending the trial + + % Clear Screen() textures that were initialized for each trial iteration + Screen('Close', backgroundTexture); + Screen('Close', imgTexture); + Screen('Close', stimTexture); + end % End trial loop + + + %% STEP 6: CLOSE EDF FILE. TRANSFER EDF COPY TO DISPLAY PC. CLOSE EYELINK CONNECTION. FINISH UP + + % Put tracker in idle/offline mode before closing file. Eyelink('SetOfflineMode') is recommended. + % However if Eyelink('Command', 'set_idle_mode') is used, allow 50ms before closing the file as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('Command', 'clear_screen 0'); % Clear Host PC backdrop graphics at the end of the experiment + WaitSecs(0.5); % Allow some time before closing and transferring file + Eyelink('CloseFile'); % Close EDF file on Host PC + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below +catch % If syntax error is detected + cleanup; + % Print error message and line number in Matlab's Command Window + psychrethrow(psychlasterror); +end +PsychPortAudio('Close', pahandle); +PsychPortAudio('Close', pamaster); + + +% Cleanup function used throughout the script above + function cleanup + sca; % PTB's wrapper for Screen('CloseAll') & related cleanup, e.g. ShowCursor + Eyelink('Shutdown'); % Close EyeLink connection + ListenChar(0); % Restore keyboard output to Matlab + if ~IsOctave; commandwindow; end % Bring Command Window to front + end + +% Function for transferring copy of EDF file to the experiment folder on Display PC. +% Allows for optional destination path which is different from experiment folder + function transferFile + try + if dummymode ==0 % If connected to EyeLink + % Show 'Receiving data file...' text until file transfer is complete + Screen('FillRect', window, el.backgroundcolour); % Prepare background on backbuffer + Screen('DrawText', window, 'Receiving data file...', 5, height-35, 0); % Prepare text + Screen('Flip', window); % Present text + fprintf('Receiving data file ''%s.edf''\n', edfFile); % Print some text in Matlab's Command Window + + % Transfer EDF file to Host PC + % [status =] Eyelink('ReceiveFile',['src'], ['dest'], ['dest_is_path']) + status = Eyelink('ReceiveFile'); + + % Check if EDF file has been transferred successfully and print file size in Matlab's Command Window + if status > 0 + fprintf('EDF file size: %.1f KB\n', status/1024); % Divide file size by 1024 to convert bytes to KB + end + % Print transferred EDF file path in Matlab's Command Window + fprintf('Data file ''%s.edf'' can be found in ''%s''\n', edfFile, pwd); + else + fprintf('No EDF file saved in Dummy mode\n'); + end + cleanup; + catch % Catch a file-transfer error and print some text in Matlab's Command Window + fprintf('Problem receiving data file ''%s''\n', edfFile); + cleanup; + psychrethrow(psychlasterror); + end + end +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedFixUpdateEvents/img1.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedFixUpdateEvents/img1.jpg new file mode 100644 index 0000000000..00663e2e44 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedFixUpdateEvents/img1.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedFixUpdateEvents/img2.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedFixUpdateEvents/img2.jpg new file mode 100644 index 0000000000..3fa77e178b Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/BufferedFixUpdateEvents/img2.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/Contents.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/Contents.m new file mode 100644 index 0000000000..39810e4e76 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCBufferedEvents/Contents.m @@ -0,0 +1,10 @@ +% EyelinkToolbox:EyelinkDemos:SR-ResearchDemos:GazeContingent:GCBufferedEvents +% +% Demos provided by SR-Research to demonstrate gaze contingent drawing +% using parsed gaze event data +% +% BufferedEndSacEvents - Retrieve online end-saccade events from a buffer +% BufferedFixUpdateEvents - Retrieve online fixation-update events from a buffer +% +% See also SR-RESEARCHDEMOS +% diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCFastSamples/EyeLink_GCFastSamples.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCFastSamples/EyeLink_GCFastSamples.m new file mode 100644 index 0000000000..4b4c27e091 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCFastSamples/EyeLink_GCFastSamples.m @@ -0,0 +1,426 @@ +function EyeLink_GCFastSamples(screenNumber) +% A simple EyeLink gaze-contingent demo showing how to retrieve fast online samples. +% In each trial an image is presented with a red gaze-contingent dot overlaid on top. +% The dot's location is updated online based on the x y coordinates of the latest gaze sample retrieved online. +% Each trial ends when the space bar is pressed. +% +% Usage: +% Eyelink_GCfastSamples(screenNumber) +% +% screenNumber is an optional parameter which can be used to pass a specific value to PsychImaging('OpenWindow', ...) +% If screenNumber is not specified, or if isempty(screenNumber) then the default: +% screenNumber = max(Screen('Screens')); +% will be used. +% +% The demo checks if a new sample is available online via the link. This is the most recent sample, which is faster than buffered data. +% This is equivalent to eyeLink_newest_float_sample() in C API. See EyeLink Programmers Guide manual > Function Lists > Message and Command Sending/Receiving > Functions +% It allows access to the following sample properties: +% +% time (sample time) +% type (SAMPLE=200) +% gx ([left gaze x, right gaze x]) +% gy ([left gaze y, right gaze y]) +% pa ([lef eye pupil size, right eye pupil size]) +% rx (x 'pixel per degree' value) +% ry (y 'pixel per degree' value) +% buttons (button state and changes) +% hdata (contains a list of 8 fields. Only the first 4 values are important: +% [uncalibrated target sticker x, uncalibrated target sticker y, target sticker distance in mm, target flags) + +% Bring the Command Window to the front if it is already open +if ~IsOctave; commandwindow; end + +PsychDefaultSetup(2); + +% Use default screenNumber if none specified +if (nargin < 1) + screenNumber = []; +end + +try + %% STEP 1: INITIALIZE EYELINK CONNECTION; OPEN EDF FILE; GET EYELINK TRACKER VERSION + + % Initialize EyeLink connection (dummymode = 0) or run in "Dummy Mode" without an EyeLink connection (dummymode = 1); + dummymode = 0; + EyelinkInit(dummymode); % Initialize EyeLink connection + status = Eyelink('IsConnected'); + if status < 1 % If EyeLink not connected + dummymode = 1; + end + + % Open dialog box for EyeLink Data file name entry. File name up to 8 characters + prompt = {'Enter EDF file name (up to 8 characters)'}; + dlg_title = 'Create EDF file'; + def = {'demo'}; % Create a default edf file name + answer = inputdlg(prompt, dlg_title, 1, def); % Prompt for new EDF file name + % Print some text in Matlab's Command Window if a file name has not been entered + if isempty(answer) + fprintf('Session cancelled by user\n') + cleanup; % Abort experiment (see cleanup function below) + return + end + edfFile = answer{1}; % Save file name to a variable + % Print some text in Matlab's Command Window if file name is longer than 8 characters + if length(edfFile) > 8 + fprintf('Filename needs to be no more than 8 characters long (letters, numbers and underscores only)\n'); + cleanup; % Abort experiment (see cleanup function below) + return + end + + % Open an EDF file and name it + failOpen = Eyelink('OpenFile', edfFile); + if failOpen ~= 0 % Abort if it fails to open + fprintf('Cannot create EDF file %s', edfFile); % Print some text in Matlab's Command Window + cleanup; %see cleanup function below + return + end + + % Get EyeLink tracker and software version + % returns 0 if not connected + % returns 'EYELINK I', 'EYELINK II x.xx', 'EYELINK CL x.xx' where 'x.xx' is the software version + ELsoftwareVersion = 0; % Default EyeLink version in dummy mode + [ver, versionstring] = Eyelink('GetTrackerVersion'); + if dummymode == 0 % If connected to EyeLink + % Extract software version number. + [~, vnumcell] = regexp(versionstring,'.*?(\d)\.\d*?','Match','Tokens'); % Extract EL version before decimal point + ELsoftwareVersion = str2double(vnumcell{1}{1}); % Returns 1 for EyeLink I, 2 for EyeLink II, 3/4 for EyeLink 1K, 5 for EyeLink 1KPlus, 6 for Portable Duo + % Print some text in Matlab's Command Window + fprintf('Running experiment on %s version %d\n', versionstring, ver ); + end + % Add a line of text in the EDF file to identify the current experimemt name and session. This is optional. + % If your text starts with "RECORDED BY " it will be available in DataViewer's Inspector window by clicking + % the EDF session node in the top panel and looking for the "Recorded By:" field in the bottom panel of the Inspector. + preambleText = sprintf('RECORDED BY Psychtoolbox demo %s session name: %s', mfilename, edfFile); + Eyelink('Command', 'add_file_preamble_text "%s"', preambleText); + + + %% STEP 2: SELECT AVAILABLE SAMPLE/EVENT DATA + % See EyeLinkProgrammers Guide manual > Useful EyeLink Commands > File Data Control & Link Data Control + + % Select which events are saved in the EDF file. Include everything just in case + Eyelink('Command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); + % Select which events are available online for gaze-contingent experiments. Include everything just in case + Eyelink('Command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON,FIXUPDATE,INPUT'); + % Select which sample data is saved in EDF file or available online. Include everything just in case + if ELsoftwareVersion > 3 % Check tracker version and include 'HTARGET' to save head target sticker data for supported eye trackers + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,HTARGET,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,HTARGET,STATUS,INPUT'); + else + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); + end + + + %% STEP 3: OPEN GRAPHICS WINDOW + + % Open experiment graphics on the specified screen + if isempty(screenNumber) + screenNumber = max(Screen('Screens')); % Use default screen if none specified + end + + window = PsychImaging('OpenWindow', screenNumber, GrayIndex(screenNumber)); % Open graphics window + Screen('Flip', window); + + % Get max color value for rescaling to RGB for Host PC & Data Viewer integration + colorMaxVal = Screen('ColorRange', window); + % Return width and height of the graphics window/screen in pixels + [width, height] = Screen('WindowSize', window); + + + %% STEP 4: SET CALIBRATION SCREEN COLOURS; PROVIDE WINDOW SIZE TO EYELINK HOST & DATAVIEWER; SET CALIBRATION PARAMETERS; CALIBRATE + + % Provide EyeLink with some defaults, which are returned in the structure "el". + el = EyelinkInitDefaults(window); + % set calibration/validation/drift-check(or drift-correct) size as well as background and target colors. + % It is important that this background colour is similar to that of the stimuli to prevent large luminance-based + % pupil size changes (which can cause a drift in the eye movement data) + el.calibrationtargetsize = 3;% Outer target size as percentage of the screen + el.calibrationtargetwidth = 0.7;% Inner target size as percentage of the screen + el.backgroundcolour = repmat(GrayIndex(screenNumber),1,3); + el.calibrationtargetcolour = repmat(BlackIndex(screenNumber),1,3); + % set "Camera Setup" instructions text colour so it is different from background colour + el.msgfontcolour = repmat(BlackIndex(screenNumber),1,3); + + % Initialize PsychSound for calibration/validation audio feedback + % EyeLink Toolbox now supports PsychPortAudio integration and interop + % with legacy Snd() wrapping. Below we open the default audio device in + % output mode as master, create a slave device, and pass the device + % handle to el.ppa_pahandle. + % el.ppa_handle supports passing either standard mode handle, or as + % below one opened as a slave device. When el.ppa_handle is empty, for + % legacy support EyelinkUpdateDefaults() will open the default device + % and use that with Snd() interop, and close the device handle when + % calling Eyelink('Shutdown') at the end of the script. + InitializePsychSound(); + pamaster = PsychPortAudio('Open', [], 8+1); + PsychPortAudio('Start', pamaster); + pahandle = PsychPortAudio('OpenSlave', pamaster, 1); + el.ppa_pahandle = pahandle; + + % You must call this function to apply the changes made to the el structure above + EyelinkUpdateDefaults(el); + + % Set display coordinates for EyeLink data by entering left, top, right and bottom coordinates in screen pixels + Eyelink('Command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Write DISPLAY_COORDS message to EDF file: sets display coordinates in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Pre-trial Message Commands + Eyelink('Message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Set number of calibration/validation dots and spread: horizontal-only(H) or horizontal-vertical(HV) as H3, HV3, HV5, HV9 or HV13 + Eyelink('Command', 'calibration_type = HV9'); % horizontal-vertical 9-points + % Allow a supported EyeLink Host PC button box to accept calibration or drift-check/correction targets via button 5 + Eyelink('Command', 'button_function 5 "accept_target_fixation"'); + % Hide mouse cursor + HideCursor(window); + % Suppress keypress output to command window. + ListenChar(-1); + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Put EyeLink Host PC in Camera Setup mode for participant setup/calibration + EyelinkDoTrackerSetup(el); + + + %% STEP 5: TRIAL LOOP. + + spaceBar = KbName('space');% Identify keyboard key code for space bar to end each trial later on + imgList = {'img1.jpg' 'img2.jpg'};% Provide image list for 2 trials + for i = 1:length(imgList) + + % STEP 5.1: PREBUILD STIMULUS (GREY BACKGROUND + IMAGE + TEXT) + + % Prepare grey background on backbuffer + Screen('FillRect', window, el.backgroundcolour); + % Use 'drawBuffer' to copy unprocessed backbuffer images without additional processing. Prevents image size info issues on Retina displays + backgroundArray = Screen('GetImage', window, [], 'drawBuffer'); % Copy unprocessed backbuffer + backgroundTexture = Screen('MakeTexture', window, backgroundArray); % Convert background to texture so it is ready for drawing later on + % Prepare image on backbuffer + imgName = char(imgList(i)); % Get image file name for current trial + imgInfo = imfinfo(imgName); % Get image file info + imgData = imread(imgName); % Read image from file + imgTexture = Screen('MakeTexture',window, imgData); % Convert image file to texture + Screen('DrawTexture', window, imgTexture); % Prepare image texture on backbuffer + % Prepare text on backbuffer + Screen('TextSize', window, 30); % Specify text size + Screen('DrawText', window, 'Press space bar to end trial', 5, height-35, 0); % Prepare text on backbuffer + % Save complete backbuffer as trial*.bmp to be used as stimulus and as Host PC & DataViewer backdrop + stimName = ['trial' num2str(i) '.bmp']; % Prepare stimulus file name + stimArray = Screen('GetImage', window, [], 'drawBuffer'); % Copy backbuffer to be used as stimulus + imwrite(stimArray, stimName); % Save .bmp stimulus file in experment folder + % Convert stimulus to texture so it is ready for drawing later on + stimInfo = imfinfo(stimName); % Get stimulus info + stimTexture = Screen('MakeTexture', window, stimArray); % Convert to texture + + % STEP 5.2: START TRIAL; SHOW TRIAL INFO ON HOST PC; SHOW BACKDROP IMAGE AND/OR DRAW FEEDBACK GRAPHICS ON HOST PC; DRIFT-CHECK/CORRECTION + + % Write TRIALID message to EDF file: marks the start of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIALID %d', i); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + % Supply the trial number as a line of text on Host PC screen + Eyelink('Command', 'record_status_message "TRIAL %d/%d"', i, length(imgList)); + + % Draw graphics on the EyeLink Host PC display. See COMMANDS.INI in the Host PC's exe folder for a list of commands + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode before drawing Host PC graphics and before recording + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Optional: Send an image to the Host PC to be displayed as the backdrop image over which + % the gaze-cursor is overlayed during trial recordings. + % See Eyelink('ImageTransfer?') for information about supported syntax and compatible image formats. + % Below, we use the new option to pass image data from imread() as the imageArray parameter, which + % enables the use of many image formats. + % [status] = Eyelink('ImageTransfer', imageArray, xs, ys, width, height, xd, yd, options); + % xs, ys: top-left corner of the region to be transferred within the source image + % width, height: size of region to be transferred within the source image (note, values of 0 will include the entire width/height) + % xd, yd: location (top-left) where image region to be transferred will be presented on the Host PC + % This image transfer function works for non-resized image presentation only. If you need to resize images and use this function please resize + % the original image files beforehand + transferStatus = Eyelink('ImageTransfer', stimArray, 0, 0, 0, 0, 0, 0); + if dummymode == 0 && transferStatus ~= 0 % If connected to EyeLink and image transfer fails + fprintf('Image transfer Failed\n'); % Print some text in Matlab's Command Window + end + + % Optional: draw feedback box and lines on Host PC interface instead of (or on top of) backdrop image. + % See section 25.7 'Drawing Commands' in the EyeLink Programmers Guide manual + Eyelink('Command', 'draw_box %d %d %d %d 15', round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2)); + + % Perform a drift check/correction. + % Optionally provide x y target location, otherwise target is presented on screen centre + EyelinkDoDriftCorrection(el, round(width/2), round(height/2)); + + %STEP 5.3: START RECORDING + + % Put tracker in idle/offline mode before recording. Eyelink('SetOfflineMode') is recommended + % however if Eyelink('Command', 'set_idle_mode') is used allow 50ms before recording as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode before recording + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode before recording + Eyelink('StartRecording'); % Start tracker recording + WaitSecs(0.1); % Allow some time to record a few samples before presenting first stimulus + + % Check which eye is available for gaze-contingent drawing. Returns 0 (left), 1 (right) or 2 (binocular) + eyeUsed = Eyelink('EyeAvailable'); + % Get samples from right eye if binocular + if eyeUsed == 2 + eyeUsed = 1; + end + + % STEP 5.4: PRESENT STIMULUS; CREATE DATAVIEWER BACKDROP AND INTEREST AREA + + % Present initial trial image without gaze-contingent dot + Screen('DrawTexture', window, stimTexture); % Prepare stimulus texture on backbuffer + [~, RtStart] = Screen('Flip', window); % Present stimulus + % Write message to EDF file to mark the start time of stimulus presentation. + Eyelink('Message', 'STIM_ONSET'); + % Write !V IMGLOAD message to EDF file: creates backdrop image for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Image Commands + Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', stimName, width/2, height/2); + % Write !V IAREA message to EDF file: creates interest area around image in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Interest Area Commands + Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 1, round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2),'IMAGE_IA'); + + % STEP 5.5: DRAW GAZE-CONTINGENT DOT; WAIT FOR KEYPRESS; SHOW BLANK SCREEN; STOP RECORDING + + while 1 % loop until error, space bar press + % Check that eye tracker is still recording. Otherwise close and transfer copy of EDF file to Display PC + err = Eyelink('CheckRecording'); + if(err ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below + cleanup; % Abort experiment (see cleanup function below) + return + end + % Check if a new sample is available online via the link. This is the most recent sample, which is faster than buffered data + % This is equivalent to eyeLink_newest_float_sample() in C API. See EyeLink Programmers Guide manual > Function Lists > Message and Command Sending/Receiving > Functions + if Eyelink('NewFloatSampleAvailable') > 0 + % Get sample data in a Matlab structure + % This is equivalent to eyeLink_newest_float_sample() in C API. See EyeLink Programmers Guide manual > Function Lists > Message and Command Sending/Receiving > Functions + evt = Eyelink('NewestFloatSample'); + + % Save sample properties as variables. See EyeLink Programmers Guide manual > Data Structures > FSAMPLE + x = evt.gx(eyeUsed+1); % [left eye gaze x, right eye gaze x] +1 as we're accessing a Matlab array + y = evt.gy(eyeUsed+1); % [left eye gaze y, right eye gaze y] + + % The following sample properties are also available online but are not used in this demo: + % evt.time; % Sample EDF time + % evt.type; % Event type (SAMPLE =200) + % evt.pa; %[left eye pupil size, right eye pupil size] + % evt.rx; % Gaze x 'pixel per deggree' value + % evt.ry; % Gaze y 'pixel per degree' value + % evt.hdata; % [uncalibrated target sticker x, uncalibrated target sticker y, target sticker distance in mm, target flags ...] + + % Draw gaze-contingent dot + Screen('DrawTexture', window, stimTexture); % Prepare stimulus texture on backbuffer + % Superimpose a red gaze-contingent dot using gaze coordinates + Screen('FillOval', window, [255,0,0], [x-20, y-20, x+20, y+20]); + Screen('Flip', window); % Present stimulus + end + % End trial if space bar is pressed + [~, RtEnd, keyCode] = KbCheck; + if keyCode(spaceBar) + % Write message to EDF file to mark the spacebar press time + Eyelink('Message', 'KEY_PRESSED'); + reactionTime = round((RtEnd-RtStart)*1000); % Calculate RT from stimulus onset + break; + end + end % End of while loop + + % Draw blank screen at end of trial + Screen('DrawTexture', window, backgroundTexture); % Prepare background texture on backbuffer + Screen('Flip', window); % Present blank screen + % Write message to EDF file to mark time when blank screen is presented + Eyelink('Message', 'BLANK_SCREEN'); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Stop recording eye movements at the end of each trial + WaitSecs(0.1); % Add 100 msec of data to catch final events before stopping + Eyelink('StopRecording'); % Stop tracker recording + + % STEP 5.6: CREATE VARIABLES FOR DATAVIEWER; END TRIAL + + % Write !V TRIAL_VAR messages to EDF file: creates trial variables in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Trial Message Commands + Eyelink('Message', '!V TRIAL_VAR iteration %d', i); % Trial iteration + Eyelink('Message', '!V TRIAL_VAR image %s', imgName); % Image name + WaitSecs(0.001); % Allow some time between messages. Some messages can be lost if too many are written at the same time + Eyelink('Message', '!V TRIAL_VAR rt %d', reactionTime); % Reaction time + % Write TRIAL_RESULT message to EDF file: marks the end of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIAL_RESULT 0'); + WaitSecs(0.01); % Allow some time before ending the trial + + % Clear Screen() textures that were initialized for each trial iteration + Screen('Close', backgroundTexture); + Screen('Close', imgTexture); + Screen('Close', stimTexture); + end % End trial loop + + + %% STEP 6: CLOSE EDF FILE. TRANSFER EDF COPY TO DISPLAY PC. CLOSE EYELINK CONNECTION. FINISH UP + + % Put tracker in idle/offline mode before closing file. Eyelink('SetOfflineMode') is recommended. + % However if Eyelink('Command', 'set_idle_mode') is used, allow 50ms before closing the file as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('Command', 'clear_screen 0'); % Clear Host PC backdrop graphics at the end of the experiment + WaitSecs(0.5); % Allow some time before closing and transferring file + Eyelink('CloseFile'); % Close EDF file on Host PC + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below +catch % If syntax error is detected + cleanup; + % Print error message and line number in Matlab's Command Window + psychrethrow(psychlasterror); +end +PsychPortAudio('Close', pahandle); +PsychPortAudio('Close', pamaster); + + +% Cleanup function used throughout the script above + function cleanup + sca; % PTB's wrapper for Screen('CloseAll') & related cleanup, e.g. ShowCursor + Eyelink('Shutdown'); % Close EyeLink connection + ListenChar(0); % Restore keyboard output to Matlab + if ~IsOctave; commandwindow; end % Bring Command Window to front + end + +% Function for transferring copy of EDF file to the experiment folder on Display PC. +% Allows for optional destination path which is different from experiment folder + function transferFile + try + if dummymode ==0 % If connected to EyeLink + % Show 'Receiving data file...' text until file transfer is complete + Screen('FillRect', window, el.backgroundcolour); % Prepare background on backbuffer + Screen('DrawText', window, 'Receiving data file...', 5, height-35, 0); % Prepare text + Screen('Flip', window); % Present text + fprintf('Receiving data file ''%s.edf''\n', edfFile); % Print some text in Matlab's Command Window + + % Transfer EDF file to Host PC + % [status =] Eyelink('ReceiveFile',['src'], ['dest'], ['dest_is_path']) + status = Eyelink('ReceiveFile'); + + % Check if EDF file has been transferred successfully and print file size in Matlab's Command Window + if status > 0 + fprintf('EDF file size: %.1f KB\n', status/1024); % Divide file size by 1024 to convert bytes to KB + end + % Print transferred EDF file path in Matlab's Command Window + fprintf('Data file ''%s.edf'' can be found in ''%s''\n', edfFile, pwd); + else + fprintf('No EDF file saved in Dummy mode\n'); + end + cleanup; + catch % Catch a file-transfer error and print some text in Matlab's Command Window + fprintf('Problem receiving data file ''%s''\n', edfFile); + cleanup; + psychrethrow(psychlasterror); + end + end +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCFastSamples/img1.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCFastSamples/img1.jpg new file mode 100644 index 0000000000..00663e2e44 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCFastSamples/img1.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCFastSamples/img2.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCFastSamples/img2.jpg new file mode 100644 index 0000000000..3fa77e178b Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/GazeContingent/GCFastSamples/img2.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/MRI_BlockRecord/EyeLink_MRI_BlockRecord.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/MRI_BlockRecord/EyeLink_MRI_BlockRecord.m new file mode 100644 index 0000000000..84325da7b7 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/MRI_BlockRecord/EyeLink_MRI_BlockRecord.m @@ -0,0 +1,452 @@ +function EyeLink_MRI_BlockRecord(screenNumber) +% Simple MRI demo with EyeLink integration. +% 6 trials are presented in 2 blocks of 3 trials. Trial duration is 5.5s during which a 4s stimulus is presented. +% A block starts with a drift-check followed by presentation of central crosshairs. Eye movements are recorded while +% waiting for an MRI trigger (keyboard key 't' in this demo). The stimulus is presented when trigger is received. +% A fixed ITI is maintained by presenting crosshairs between each 4s stimulus. Eye movements are recorded throughout +% an entire block rather than on a trial-by-trial basis. +% +% In STEP 5 it is shown how to: +% - shrink the spread of the calibration/validation targets so they are all visible if the MRI bore blocks part of the screen +% - apply an optional online drift correction (see EyeLink 1000 Plus User Manual section 3.11.2) +% +% Usage: +% Eyelink_MRI_BlockRecord(screenNumber) +% +% screenNumber is an optional parameter which can be used to pass a specific value to PsychImaging('OpenWindow', ...) +% If screenNumber is not specified, or if isempty(screenNumber) then the default: +% screenNumber = max(Screen('Screens')); +% will be used. + +%% STEP 1: PROVIDE SOME SESSION PARAMETERS + +stimDur = 4.0; % stimulus duration in seconds +trialDur = 5.5; % trial duration in seconds + +% Bring the Command Window to the front if it is already open +if ~IsOctave; commandwindow; end + +PsychDefaultSetup(2); + +% Use default screenNumber if none specified +if (nargin < 1) + screenNumber = []; +end + +try + %% STEP 2: INITIALIZE EYELINK CONNECTION; OPEN EDF FILE; GET EYELINK TRACKER VERSION + + % Initialize EyeLink connection (dummymode = 0) or run in "Dummy Mode" without an EyeLink connection (dummymode = 1); + dummymode = 0; + EyelinkInit(dummymode); % Initialize EyeLink connection + status = Eyelink('IsConnected'); + if status < 1 % If EyeLink not connected + dummymode = 1; + end + + % Open dialog box for EyeLink Data file name entry. File name up to 8 characters + prompt = {'Enter EDF file name (up to 8 characters)'}; + dlg_title = 'Create EDF file'; + def = {'demo'}; % Create a default edf file name + answer = inputdlg(prompt, dlg_title, 1, def); % Prompt for new EDF file name + % Print some text in Matlab's Command Window if a file name has not been entered + if isempty(answer) + fprintf('Session cancelled by user\n') + cleanup; % Abort experiment (see cleanup function below) + return + end + edfFile = answer{1}; % Save file name to a variable + % Print some text in Matlab's Command Window if file name is longer than 8 characters + if length(edfFile) > 8 + fprintf('Filename needs to be no more than 8 characters long (letters, numbers and underscores only)\n'); + cleanup; % Abort experiment (see cleanup function below) + return + end + + % Open an EDF file and name it + failOpen = Eyelink('OpenFile', edfFile); + if failOpen ~= 0 % Abort if it fails to open + fprintf('Cannot create EDF file %s', edfFile); % Print some text in Matlab's Command Window + cleanup; %see cleanup function below + return + end + + % Get EyeLink tracker and software version + % returns 0 if not connected + % returns 'EYELINK I', 'EYELINK II x.xx', 'EYELINK CL x.xx' where 'x.xx' is the software version + ELsoftwareVersion = 0; % Default EyeLink version in dummy mode + [ver, versionstring] = Eyelink('GetTrackerVersion'); + if dummymode == 0 % If connected to EyeLink + % Extract software version number. + [~, vnumcell] = regexp(versionstring,'.*?(\d)\.\d*?','Match','Tokens'); % Extract EL version before decimal point + ELsoftwareVersion = str2double(vnumcell{1}{1}); % Returns 1 for EyeLink I, 2 for EyeLink II, 3/4 for EyeLink 1K, 5 for EyeLink 1KPlus, 6 for Portable Duo + % Print some text in Matlab's Command Window + fprintf('Running experiment on %s version %d\n', versionstring, ver ); + end + % Add a line of text in the EDF file to identify the current experimemt name and session. This is optional. + % If your text starts with "RECORDED BY " it will be available in DataViewer's Inspector window by clicking + % the EDF session node in the top panel and looking for the "Recorded By:" field in the bottom panel of the Inspector. + preambleText = sprintf('RECORDED BY Psychtoolbox demo %s session name: %s', mfilename, edfFile); + Eyelink('Command', 'add_file_preamble_text "%s"', preambleText); + + + %% STEP 3: SELECT AVAILABLE SAMPLE/EVENT DATA + % See EyeLinkProgrammers Guide manual > Useful EyeLink Commands > File Data Control & Link Data Control + + % Select which events are saved in the EDF file. Include everything just in case + Eyelink('Command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); + % Select which events are available online for gaze-contingent experiments. Include everything just in case + Eyelink('Command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON,FIXUPDATE,INPUT'); + % Select which sample data is saved in EDF file or available online. Include everything just in case + if ELsoftwareVersion > 3 % Check tracker version and include 'HTARGET' to save head target sticker data for supported eye trackers + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,HTARGET,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,HTARGET,STATUS,INPUT'); + else + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); + end + + + %% STEP 4: OPEN GRAPHICS WINDOW + + % Open experiment graphics on the specified screen + if isempty(screenNumber) + screenNumber = max(Screen('Screens')); % Use default screen if none specified + end + + window = PsychImaging('OpenWindow', screenNumber, GrayIndex(screenNumber)); % Open graphics window + Screen('Flip', window); + + ifi = Screen('GetFlipInterval', window); % Return an estimate of the monitor flip interval + + % Get max color value for rescaling to RGB for Host PC & Data Viewer integration + colorMaxVal = Screen('ColorRange', window); + % Return width and height of the graphics window/screen in pixels + [width, height] = Screen('WindowSize', window); + + + %% STEP 5: SET CALIBRATION SCREEN COLOURS; PROVIDE WINDOW SIZE TO EYELINK HOST & DATAVIEWER; SET CALIBRATION PARAMETERS; CALIBRATE + + % Provide EyeLink with some defaults, which are returned in the structure "el". + el = EyelinkInitDefaults(window); + % set calibration/validation/drift-check(or drift-correct) size as well as background and target colors. + % It is important that this background colour is similar to that of the stimuli to prevent large luminance-based + % pupil size changes (which can cause a drift in the eye movement data) + el.calibrationtargetsize = 3;% Outer target size as percentage of the screen + el.calibrationtargetwidth = 0.7;% Inner target size as percentage of the screen + el.backgroundcolour = repmat(GrayIndex(screenNumber),1,3); + el.calibrationtargetcolour = repmat(BlackIndex(screenNumber),1,3); + % set "Camera Setup" instructions text colour so it is different from background colour + el.msgfontcolour = repmat(BlackIndex(screenNumber),1,3); + + % Initialize PsychSound for calibration/validation audio feedback + % EyeLink Toolbox now supports PsychPortAudio integration and interop + % with legacy Snd() wrapping. Below we open the default audio device in + % output mode as master, create a slave device, and pass the device + % handle to el.ppa_pahandle. + % el.ppa_handle supports passing either standard mode handle, or as + % below one opened as a slave device. When el.ppa_handle is empty, for + % legacy support EyelinkUpdateDefaults() will open the default device + % and use that with Snd() interop, and close the device handle when + % calling Eyelink('Shutdown') at the end of the script. + InitializePsychSound(); + pamaster = PsychPortAudio('Open', [], 8+1); + PsychPortAudio('Start', pamaster); + pahandle = PsychPortAudio('OpenSlave', pamaster, 1); + el.ppa_pahandle = pahandle; + + % You must call this function to apply the changes made to the el structure above + EyelinkUpdateDefaults(el); + + % Set display coordinates for EyeLink data by entering left, top, right and bottom coordinates in screen pixels + Eyelink('Command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Write DISPLAY_COORDS message to EDF file: sets display coordinates in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Pre-trial Message Commands + Eyelink('Message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Set number of calibration/validation dots and spread: horizontal-only(H) or horizontal-vertical(HV) as H3, HV3, HV5, HV9 or HV13 + Eyelink('Command', 'calibration_type = HV9'); % horizontal-vertical 9-points + + % Optional: shrink the spread of the calibration/validation targets + % if default outermost targets are not all visible in the bore. + % Default spread is 0.88, 0.83 (88% of the display horizontally and 83% vertically) + Eyelink('command', 'calibration_area_proportion 0.88 0.83'); + Eyelink('command', 'validation_area_proportion 0.88 0.83'); + + %------------------------------------------------------------------------------------------- + % Optional: online drift correction. See section 3.11.2 in the EyeLink 1000 / EyeLink 1000 Plus User Manual + % % Online drift correction to mouse-click position: + % Eyelink('Command', 'driftcorrect_cr_disable = OFF'); + % Eyelink('Command', 'normal_click_dcorr = ON'); + % + % % Online drift correction to a fixed location: + % Eyelink('Command', 'driftcorrect_cr_disable = OFF'); + % Eyelink('Command', 'online_dcorr_refposn 512,384'); + % Eyelink('Command', 'online_dcorr_button = ON'); + % Eyelink('Command', 'normal_click_dcorr = OFF'); + %------------------------------------------------------------------------------------------- + + % Hide mouse cursor + HideCursor(window); + % Suppress keypress output to command window. + ListenChar(-1); + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Put EyeLink Host PC in Camera Setup mode for participant setup/calibration + EyelinkDoTrackerSetup(el); + + + %% STEP 6: START BLOCK LOOP; DRAW FEEDBACK GRAPHICS/TEXT ON HOST PC; DRIFT-CHECK; START RECORDING; DRAW CROSSHAIRS ON SCREEN + + % Provide list of images for each block (3 images, 2 blocks) + imgList = {{'img1.jpg', 'img2.jpg', 'img3.jpg'}, {'img1.jpg', 'img2.jpg', 'img3.jpg'}}; + trialCount = 0; % Trial counter + for iBlock = 1:length(imgList) + + % Put tracker in idle/offline mode before Host PC feedback graphics drawing + Eyelink('SetOfflineMode'); + + % Optional: draw feedback graphics on Host PC interface + % See section 25.7 'Drawing Commands' in the EyeLink Programmers Guide manual + imgSize = [1024, 768]; % Use stimulus image size for drawing feedback graphics + Eyelink('Command', 'clear_screen 0'); %Clear Host screen to black + Eyelink('Command', 'draw_box %d %d %d %d 15', round(width/2-imgSize(1)/2), round(height/2-imgSize(2)/2), round(width/2+imgSize(1)/2), round(height/2+imgSize(2)/2)); + Eyelink('Command', 'draw_line %d %d %d %d 15', width/2, 1, width/2, height); + Eyelink('Command', 'draw_line %d %d %d %d 15', 1, height/2, width, height/2); + % Supply the block number as a line of text on Host PC screen + Eyelink('Command', 'record_status_message "BLOCK %d/%d"', iBlock, length(imgList)); + + % Perform a drift check/correction. EyeLink 1000 and 1000 Plus perform a drift-check by default + % Optionally provide x y target location, otherwise target is presented at screen centre + EyelinkDoDriftCorrection(el, round(width/2), round(height/2)); + + % Write TRIALID message to EDF file: marks the start of first trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial. + % TRIALID before StartRecording prevents extra initial trial in DataViewer when recording continuously + trialCount = trialCount + 1; % Add 1 to trial counter + Eyelink('Message', 'TRIALID %d', trialCount); + + % Put tracker in idle/offline mode before recording. Eyelink('SetOfflineMode') is recommended + % however if Eyelink('Command', 'set_idle_mode') is used allow 50ms before recording as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode before recording + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode before recording + Eyelink('StartRecording'); % Start tracker recording + WaitSecs(0.1); % Allow some time to record a few samples before presenting first stimulus + + % Draw crosshairs centrally on the screen and present some text + Screen('FillRect', window, el.backgroundcolour); + Screen('DrawLine', window, 0, round(width/2-20), round(height/2), round(width/2+20), round(height/2), 5); + Screen('DrawLine', window, 0, round(width/2), round(height/2-20), round(width/2), round(height/2+20), 5); + Screen('DrawText', window, 'Waiting for MRI trigger (t)', 5, height-35, 0); % Prepare text + Screen('Flip', window); + + + %% STEP 7: TRIAL LOOP + + for i = 1:length(imgList{iBlock}) + + % STEP 7.1: PREBUILD STIMULUS AND SAVE .BMP FOR DATAVIEWER + + % Prepare grey background on backbuffer + Screen('FillRect', window, el.backgroundcolour); + % Use 'drawBuffer' to copy unprocessed backbuffer images without additional processing. Prevents image size info issues on Retina displays + backgroundArray = Screen('GetImage', window, [], 'drawBuffer'); % Copy unprocessed backbuffer + backgroundTexture = Screen('MakeTexture', window, backgroundArray); % Convert background to texture so it is ready for drawing later on + % Prepare image on backbuffer + imgName = char(imgList{iBlock}{i});% Get image file name for current trial + imgInfo = imfinfo(imgName); % Get image file info + imgData = imread(imgName); % Read image from file + imgTexture = Screen('MakeTexture',window, imgData); % Convert image file to texture + Screen('DrawTexture', window, imgTexture); % Prepare image texture on backbuffer + % Save complete backbuffer as trial*.bmp to be used as stimulus & DataViewer backdrop + stimName = ['blk' num2str(iBlock) 'trial' num2str(i) '.bmp']; % Prepare stimulus file name + stimArray = Screen('GetImage', window, [], 'drawBuffer'); % Copy backbuffer to be used as stimulus + imwrite(stimArray, stimName); % Save .bmp stimulus file in experment folder + % Convert stimulus to texture so it is ready for drawing later on + stimTexture = Screen('MakeTexture', window, stimArray); % Convert to texture + + % STEP 7.2: START TRIAL AND DRAW CROSSHAIRS FOR DATAVIEWER + + if i ~= 1 % If not first trial in a block + % Write TRIALID message to EDF file: marks the start of a trial for DataViewer + % TRIALID message for first trial in the block was written before recording began + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + trialCount = trialCount + 1; % Add 1 to trial counter + Eyelink('Message', 'TRIALID %d', trialCount); + end + + % Write messages to EDF: prepare backdrop and draw central crosshairs for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + Eyelink('Message', '!V DRAWLINE 0 0 0 %d %d %d %d', round(width/2-20), round(height/2), round(width/2+20), round(height/2)); + Eyelink('Message', '!V DRAWLINE 0 0 0 %d %d %d %d', round(width/2), round(height/2-20), round(width/2), round(height/2+20)); + + % STEP 7.3: WAIT FOR MRI TRIGGER AND PRESENT STIMULUS; CREATE DATAVIEWER STIMULUS BACKDROP AND INTEREST AREA + + % Present initial trial image + Screen('DrawTexture', window, stimTexture); % Prepare stimulus texture on backbuffer + % Wait for MRI trigger (keyboard key "t" in this demo) + if i == 1 % If first trial in a block + KbReleaseWait; % Wait until user releases keys on keyboard: + keyTrigger = KbName('t');% Identify key code for keyboard key "t" + while 1 + [~, ~, keyCode] = KbCheck; + if keyCode(keyTrigger) + % Write message to EDF file to mark the time when the trigger is received + Eyelink('Message', 'TRIGGER_RECEIVED'); + blockOnset = GetSecs; % Block onset time + vbl = Screen('Flip', window); % Present stimulus + break; + end + end + else % All subsequent trials in block + vbl = Screen('Flip', window, blockOnset + (trialDur*(i-1)) - 0.5*ifi); % Present stimulus. Allow half flip interval for precise flip timing) + end + % Write message to EDF file to mark the start time of stimulus presentation. + Eyelink('Message', 'STIM_ONSET'); + % Write !V IMGLOAD message to EDF file: creates backdrop image for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Image Commands + Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', stimName, width/2, height/2); + % Write !V IAREA message to EDF file: creates interest area around image in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Interest Area Commands + Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 1, round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2),'IMAGE_IA'); + + % STEP 7.4: WAIT FOR STIMULUS TIMER; CLEAR SCREEN AND SHOW CROSSHAIRS + + % Prepare crosshairs for end of trial + Screen('DrawTexture', window, backgroundTexture); % Prepare background texture on backbuffer + Screen('DrawLine', window, 0, round(width/2-20), round(height/2), round(width/2+20), round(height/2), 5); + Screen('DrawLine', window, 0, round(width/2), round(height/2-20), round(width/2), round(height/2+20), 5); + % Check that eye tracker is still recording. Otherwise close and transfer copy of EDF file to Display PC + err = Eyelink('CheckRecording'); + if(err ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below + cleanup; % Abort experiment (see cleanup function below) + return + end + Screen('Flip', window, stimDur + vbl - 0.5*ifi); % Present crosshairs. Allow half flip interval for precise flip timing) + % Write message to EDF file to mark time when blank screen is presented + Eyelink('Message', 'BLANK_SCREEN'); + + % STEP 7.5: CREATE VARIABLES FOR DATAVIEWER; WAIT FOR END OF LAST TRIAL IN BLOCK + + if i == length(imgList{iBlock}) % If last trial in block + % Clear screen and draw crosshairs for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + Eyelink('Message', '!V DRAWLINE 0 0 0 %d %d %d %d', round(width/2-20), round(height/2), round(width/2+20), round(height/2)); + Eyelink('Message', '!V DRAWLINE 0 0 0 %d %d %d %d', round(width/2), round(height/2-20), round(width/2), round(height/2+20)); + while 1 + % Check that eye tracker is still recording. Otherwise close and transfer copy of EDF file to Display PC + err = Eyelink('CheckRecording'); + if(err ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below + cleanup; % Abort experiment (see cleanup function below) + return + end + % Wait for end of last trial in block (duration relative to MRI trigger time) + if GetSecs-blockOnset >= (trialDur*(i)) + % Write message to EDF file to mark time when block has ended + Eyelink('Message', 'BLOCK_END'); + break; + end + end + end + + % Write !V TRIAL_VAR messages to EDF file: creates trial variables in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Trial Message Commands + Eyelink('Message', '!V TRIAL_VAR block %d', iBlock); % Block iteration + Eyelink('Message', '!V TRIAL_VAR iteration %d', i); % Trial iteration + Eyelink('Message', '!V TRIAL_VAR image %s', imgName); % Image name + % Write TRIAL_RESULT message to EDF file: marks the end of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIAL_RESULT 0'); + WaitSecs(0.01); % Allow some time before ending the trial + + % Clear Screen() textures that were initialized for each trial iteration + Screen('Close', backgroundTexture); + Screen('Close', imgTexture); + Screen('Close', stimTexture); + end % End trial loop + + % STEP 7.6: STOP TRACKER RECORDING AT THE END OF EACH BLOCK + + WaitSecs(0.1); % Add 100 msec of data to catch final events before stopping + Eyelink('StopRecording'); % Stop tracker recording + end % End block looop + + + %% STEP 8: CLOSE EDF FILE. TRANSFER EDF COPY TO DISPLAY PC. CLOSE EYELINK CONNECTION. FINISH UP + + % Put tracker in idle/offline mode before closing file. Eyelink('SetOfflineMode') is recommended. + % However if Eyelink('Command', 'set_idle_mode') is used, allow 50ms before closing the file as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('Command', 'clear_screen 0'); % Clear Host PC backdrop graphics at the end of the experiment + WaitSecs(0.5); % Allow some time before closing and transferring file + Eyelink('CloseFile'); % Close EDF file on Host PC + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below +catch % If syntax error is detected + cleanup; + % Print error message and line number in Matlab's Command Window + psychrethrow(psychlasterror); +end +PsychPortAudio('Close', pahandle); +PsychPortAudio('Close', pamaster); + + +% Cleanup function used throughout the script above + function cleanup + sca; % PTB's wrapper for Screen('CloseAll') & related cleanup, e.g. ShowCursor + Eyelink('Shutdown'); % Close EyeLink connection + ListenChar(0); % Restore keyboard output to Matlab + if ~IsOctave; commandwindow; end % Bring Command Window to front + end + +% Function for transferring copy of EDF file to the experiment folder on Display PC. +% Allows for optional destination path which is different from experiment folder + function transferFile + try + if dummymode ==0 % If connected to EyeLink + % Show 'Receiving data file...' text until file transfer is complete + Screen('FillRect', window, el.backgroundcolour); % Prepare background on backbuffer + Screen('DrawText', window, 'Receiving data file...', 5, height-35, 0); % Prepare text + Screen('Flip', window); % Present text + fprintf('Receiving data file ''%s.edf''\n', edfFile); % Print some text in Matlab's Command Window + + % Transfer EDF file to Host PC + % [status =] Eyelink('ReceiveFile',['src'], ['dest'], ['dest_is_path']) + status = Eyelink('ReceiveFile'); + + % Check if EDF file has been transferred successfully and print file size in Matlab's Command Window + if status > 0 + fprintf('EDF file size: %.1f KB\n', status/1024); % Divide file size by 1024 to convert bytes to KB + end + % Print transferred EDF file path in Matlab's Command Window + fprintf('Data file ''%s.edf'' can be found in ''%s''\n', edfFile, pwd); + else + fprintf('No EDF file saved in Dummy mode\n'); + end + cleanup; + catch % Catch a file-transfer error and print some text in Matlab's Command Window + fprintf('Problem receiving data file ''%s''\n', edfFile); + cleanup; + psychrethrow(psychlasterror); + end + end +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/MRI_BlockRecord/img1.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/MRI_BlockRecord/img1.jpg new file mode 100644 index 0000000000..00663e2e44 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/MRI_BlockRecord/img1.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/MRI_BlockRecord/img2.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/MRI_BlockRecord/img2.jpg new file mode 100644 index 0000000000..3fa77e178b Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/MRI_BlockRecord/img2.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/MRI_BlockRecord/img3.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/MRI_BlockRecord/img3.jpg new file mode 100644 index 0000000000..42fa951f8c Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/MRI_BlockRecord/img3.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/PursuitTarget/EyeLink_PursuitTarget.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/PursuitTarget/EyeLink_PursuitTarget.m new file mode 100644 index 0000000000..6891a0e2e4 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/PursuitTarget/EyeLink_PursuitTarget.m @@ -0,0 +1,407 @@ +function EyeLink_PursuitTarget(screenNumber) +% A smooth pursuit EyeLink integration demo that records eye movements +% while a target moves sinusoidally across the screen. Each trial ends after 5s. +% +% Illustrates how to: +% - change the drift-check/correction target location before each trial +% - create a moving target for Data Viewer's Play Back Animation view +% - create dynamic target location for Data Viewer's Temporal Graph view and sample reports +% - create target dynamic interest areas for Data Viewer +% +% Usage: +% Eyelink_PursuitTarget(screenNumber) +% +% screenNumber is an optional parameter which can be used to pass a specific value to PsychImaging('OpenWindow', ...) +% If screenNumber is not specified, or if isempty(screenNumber) then the default: +% screenNumber = max(Screen('Screens')); +% will be used. + +% Bring the Command Window to the front if it is already open +if ~IsOctave; commandwindow; end + +PsychDefaultSetup(2); + +% Use default screenNumber if none specified +if (nargin < 1) + screenNumber = []; +end + +try + %% STEP 1: INITIALIZE EYELINK CONNECTION; OPEN EDF FILE; GET EYELINK TRACKER VERSION + + % Initialize EyeLink connection (dummymode = 0) or run in "Dummy Mode" without an EyeLink connection (dummymode = 1); + dummymode = 0; + EyelinkInit(dummymode); % Initialize EyeLink connection + status = Eyelink('IsConnected'); + if status < 1 % If EyeLink not connected + dummymode = 1; + end + + % Open dialog box for EyeLink Data file name entry. File name up to 8 characters + prompt = {'Enter EDF file name (up to 8 characters)'}; + dlg_title = 'Create EDF file'; + def = {'demo'}; % Create a default edf file name + answer = inputdlg(prompt, dlg_title, 1, def); % Prompt for new EDF file name + % Print some text in Matlab's Command Window if a file name has not been entered + if isempty(answer) + fprintf('Session cancelled by user\n') + cleanup; % Abort experiment (see cleanup function below) + return + end + edfFile = answer{1}; % Save file name to a variable + % Print some text in Matlab's Command Window if file name is longer than 8 characters + if length(edfFile) > 8 + fprintf('Filename needs to be no more than 8 characters long (letters, numbers and underscores only)\n'); + cleanup; % Abort experiment (see cleanup function below) + return + end + + % Open an EDF file and name it + failOpen = Eyelink('OpenFile', edfFile); + if failOpen ~= 0 % Abort if it fails to open + fprintf('Cannot create EDF file %s', edfFile); % Print some text in Matlab's Command Window + cleanup; %see cleanup function below + return + end + + % Get EyeLink tracker and software version + % returns 0 if not connected + % returns 'EYELINK I', 'EYELINK II x.xx', 'EYELINK CL x.xx' where 'x.xx' is the software version + ELsoftwareVersion = 0; % Default EyeLink version in dummy mode + [ver, versionstring] = Eyelink('GetTrackerVersion'); + if dummymode == 0 % If connected to EyeLink + % Extract software version number. + [~, vnumcell] = regexp(versionstring,'.*?(\d)\.\d*?','Match','Tokens'); % Extract EL version before decimal point + ELsoftwareVersion = str2double(vnumcell{1}{1}); % Returns 1 for EyeLink I, 2 for EyeLink II, 3/4 for EyeLink 1K, 5 for EyeLink 1KPlus, 6 for Portable Duo + % Print some text in Matlab's Command Window + fprintf('Running experiment on %s version %d\n', versionstring, ver ); + end + % Add a line of text in the EDF file to identify the current experimemt name and session. This is optional. + % If your text starts with "RECORDED BY " it will be available in DataViewer's Inspector window by clicking + % the EDF session node in the top panel and looking for the "Recorded By:" field in the bottom panel of the Inspector. + preambleText = sprintf('RECORDED BY Psychtoolbox demo %s session name: %s', mfilename, edfFile); + Eyelink('Command', 'add_file_preamble_text "%s"', preambleText); + + + %% STEP 2: SELECT AVAILABLE SAMPLE/EVENT DATA + % See EyeLinkProgrammers Guide manual > Useful EyeLink Commands > File Data Control & Link Data Control + + % Select which events are saved in the EDF file. Include everything just in case + Eyelink('Command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); + % Select which events are available online for gaze-contingent experiments. Include everything just in case + Eyelink('Command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON,FIXUPDATE,INPUT'); + % Select which sample data is saved in EDF file or available online. Include everything just in case + if ELsoftwareVersion > 3 % Check tracker version and include 'HTARGET' to save head target sticker data for supported eye trackers + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,HTARGET,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,HTARGET,STATUS,INPUT'); + else + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); + end + + + %% STEP 3: OPEN GRAPHICS WINDOW + + % Open experiment graphics on the specified screen + if isempty(screenNumber) + screenNumber = max(Screen('Screens')); % Use default screen if none specified + end + + window = PsychImaging('OpenWindow', screenNumber, GrayIndex(screenNumber)); % Open graphics window + % Enable alpha blending for drawing of smooth points + Screen(window,'BlendFunction',GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Screen('Flip', window); + + % Get max color value for rescaling to RGB for Host PC & Data Viewer integration + colorMaxVal = Screen('ColorRange', window); + % Return width and height of the graphics window/screen in pixels + [width, height] = Screen('WindowSize', window); + + + %% STEP 4: SET CALIBRATION SCREEN COLOURS; PROVIDE WINDOW SIZE TO EYELINK HOST & DATAVIEWER; SET CALIBRATION PARAMETERS; CALIBRATE + + % Provide EyeLink with some defaults, which are returned in the structure "el". + el = EyelinkInitDefaults(window); + % set calibration/validation/drift-check(or drift-correct) size as well as background and target colors. + % It is important that this background colour is similar to that of the stimuli to prevent large luminance-based + % pupil size changes (which can cause a drift in the eye movement data) + el.calibrationtargetsize = 3;% Outer target size as percentage of the screen + el.calibrationtargetwidth = 0.7;% Inner target size as percentage of the screen + el.backgroundcolour = repmat(GrayIndex(screenNumber),1,3); + el.calibrationtargetcolour = repmat(BlackIndex(screenNumber),1,3); + % set "Camera Setup" instructions text colour so it is different from background colour + el.msgfontcolour = repmat(BlackIndex(screenNumber),1,3); + + % Initialize PsychSound for calibration/validation audio feedback + % EyeLink Toolbox now supports PsychPortAudio integration and interop + % with legacy Snd() wrapping. Below we open the default audio device in + % output mode as master, create a slave device, and pass the device + % handle to el.ppa_pahandle. + % el.ppa_handle supports passing either standard mode handle, or as + % below one opened as a slave device. When el.ppa_handle is empty, for + % legacy support EyelinkUpdateDefaults() will open the default device + % and use that with Snd() interop, and close the device handle when + % calling Eyelink('Shutdown') at the end of the script. + InitializePsychSound(); + pamaster = PsychPortAudio('Open', [], 8+1); + PsychPortAudio('Start', pamaster); + pahandle = PsychPortAudio('OpenSlave', pamaster, 1); + el.ppa_pahandle = pahandle; + + % You must call this function to apply the changes made to the el structure above + EyelinkUpdateDefaults(el); + + % Set display coordinates for EyeLink data by entering left, top, right and bottom coordinates in screen pixels + Eyelink('Command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Write DISPLAY_COORDS message to EDF file: sets display coordinates in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Pre-trial Message Commands + Eyelink('Message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Set number of calibration/validation dots and spread: horizontal-only(H) or horizontal-vertical(HV) as H3, HV3, HV5, HV9 or HV13 + Eyelink('Command', 'calibration_type = HV9'); % horizontal-vertical 9-points + % Allow a supported EyeLink Host PC button box to accept calibration or drift-check/correction targets via button 5 + Eyelink('Command', 'button_function 5 "accept_target_fixation"'); + % Hide mouse cursor + HideCursor(window); + % Suppress keypress output to command window. + ListenChar(-1); + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Put EyeLink Host PC in Camera Setup mode for participant setup/calibration + EyelinkDoTrackerSetup(el); + + + %% STEP 5: TRIAL LOOP. + + % Specify some initial parameters: + targetSize = 10; % radius of pursuit target [pixels] + IAsize = 60; % radius of dynamic IA ellipse [pixels] + targetDuration = 5000; % Trial duration [ms] + sinePlotX = round(width/2); % X centre of target sinusoidal pattern (screen centre by default) + sinePlotY = round(height/2); % Y centre of target sinusoidal pattern (screen centre by default) + amplitudeX = round(width/3); % X amplitude of sinusoidal pattern [pixels] + amplitudeY = round(height/3); % Y amplitude of sinusoidal pattern [pixels] + % Assign a label for each trial. Used later on for the Data Viewer variable "type" + type = {'HorizontalSlow' 'HorizontalFast' 'VerticalSlow' 'VerticalFast' 'EllipticSlow' 'EllipticFast'}; + % Assign the freq_x freq_y phase_x phase_y for each trial's sinusoidal pattern + trials = [[0.2;0;270;0] [0.5;0;90;0] [0;0.2;0;0] [0;0.3;0;180] [0.2;0.2;270;180] [0.3;0.3;270;0]]; + + for i = 1:length(trials) + + % STEP 5.1: OPEN DYNAMIC IA TEXT FILE; PREPARE TARGET SINUSOIDAL PATTERN + + frameNo = 0; % Reset frame counter variable at each trial + % Open text file for each trial. This will be used later on to write each instance of a dynamic interest area + % See DataViewer manual section: Working with Events, Samples and Interest Areas > Interest Areas + IAfileName = [edfFile '_' 'IA_' num2str(i) '.ias']; + f = fopen(IAfileName, 'w'); % Open text file before saving dynamic interest area data info in it + + % Prepare sinusoidal pattern and target + phaseX = (trials(3,i)/360 + ((0)) * trials(1,i)) * 2*pi; % Start phase x in radians + phaseY = (trials(4,i)/360 + ((0)) * trials(2,i))* 2*pi; % Start phase y in radians + x = sinePlotX + amplitudeX * sin(phaseX); % Sine pattern x + y = sinePlotY + amplitudeY * sin(phaseY); % Sine pattern y + targ([1 3]) = [x-targetSize x+targetSize]; % Target x + targ([2 4]) = [y-targetSize y+targetSize]; % Target y + + % STEP 5.2: START TRIAL; SHOW TRIAL INFO ON HOST PC; DRAW FEEDBACK GRAPHICS ON HOST PC; DRIFT-CHECK/CORRECTION + + % Write TRIALID message to EDF file: marks the start of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIALID %d', i); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + % Supply the trial number and type as a line of text on Host PC screen + Eyelink('Command', 'record_status_message "TRIAL %d/%d %s"', i, length(trials), char(type(i))); + + % Draw target trajectory lines on the EyeLink Host PC display. + % See section 25.7 'Drawing Commands' in the EyeLink Programmers Guide manual + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode before drawing Host PC graphics and before recording + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + Eyelink('Command', 'draw_line %d %d %d %d 15', (width/2)-amplitudeX, height/2, (width/2)+amplitudeX, height/2); + Eyelink('Command', 'draw_line %d %d %d %d 15', (width/2), height/2-amplitudeY, (width/2), height/2+amplitudeY); + WaitSecs(0.1); % Allow some time for drawing + + % Perform a drift check/correction. + % Present the drift-check/correction target at each trial's start x y pursuit target location + EyelinkDoDriftCorrection(el, round(x), round(y)); + + %STEP 5.3: START RECORDING + + % Put tracker in idle/offline mode before recording. Eyelink('SetOfflineMode') is recommended + % however if Eyelink('Command', 'set_idle_mode') is used allow 50ms before recording as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode before recording + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode before recording + Eyelink('StartRecording'); % Start tracker recording + WaitSecs(0.1); % Allow some time to record a few samples before presenting first stimulus + + % STEP 5.4: PRESENT TARGET; CREATE PURSUIT TARGET AND DYNAMIC IA FOR DATAVIEWER; STOP RECORDING + + while 1 % loop until error, or timeout + % Check that eye tracker is still recording. Otherwise close and transfer copy of EDF file to Display PC + err = Eyelink('CheckRecording'); + if(err ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below + cleanup; % Abort experiment (see cleanup function below) + return + end + % Prepare and show the screen. + Screen('FillRect', window, el.backgroundcolour); + Screen('FillOval', window,[255 0 0], targ); + [~, stTime] = Screen('Flip', window); % Present stimulus + frameNo = frameNo + 1; % Update frame counter + if frameNo == 1 % If first frame... + % Write message to EDF file to mark the start time of stimulus presentation + Eyelink('Message', 'TARGET_ONSET'); + % Write !V IAREA FILE message to EDF file: allows the correct dynamic IA file to be used + % by Data Viewer. Start of dynamic IA will be synced to this message + % See DataViewer manual section: Working with Events, Samples and Interest Areas > Interest Areas + Eyelink('Message', '!V IAREA FILE %s', IAfileName); + stStart = stTime; % Pursuit target start time + else + % Write an instance of dynamic IA in the text file for each frame + % See DataViewer manual section: Working with Events, Samples and Interest Areas > Interest Areas + fprintf(f,'%d %d ELLIPSE 1 %d %d %d %d TARGET\n', round((previousTime-stStart)*1000)*-1, round(((stTime-stStart)*1000)-1)*-1,... + round(previousX)-IAsize, round(previousY)-IAsize, round(previousX)+IAsize, round(previousY)+IAsize); + end + + % Write !V TARGET_POS message to EDF file: allows target location at each frame to be available in Data Viewer's + % Temporal Graph view and sample reports. + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Target Position Commands + Eyelink('Message', '!V TARGET_POS TARG1 (%d, %d) 1 0', round(x), round(y)); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer before drawing target location in + % Data Viewer's Play Back Animation view. + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + % Write !V FIXPOINT message to EDF file: draws the new target location in DataViewer's Play Back Animation View + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing Commands + Eyelink('Message', '!V FIXPOINT %d %d %d %d %d %d %d %d %d %d', 255, 0, 0, 255, 0, 0, round(x), round(y), targetSize*2, targetSize*2); + + % Keep some target information available for next frame. Used in creating an instance of dynamic IA at every frame + previousTime = stTime; + previousX = x; + previousY = y; + + % Prepare next frame's target location + phaseX = (trials(3,i)/360 + ((GetSecs-stStart)) * trials(1,i))*2*pi; % Current phase x in radians + phaseY = (trials(4,i)/360 + ((GetSecs-stStart)) * trials(2,i))*2*pi; % Current phase y in radians + x = sinePlotX + amplitudeX * sin(phaseX); % Sine pattern x + y = sinePlotY + amplitudeY * sin(phaseY); % Sine pattern y + targ([1 3]) = [x-targetSize x+targetSize]; % Target x + targ([2 4]) = [y-targetSize y+targetSize]; % Target y + + % Break loop when target duration reached + if GetSecs-stStart >= targetDuration/1000 + break + end + end + + % Draw blank screen at end of trial + Screen('FillRect', window, el.backgroundcolour); + [~, blankTime] = Screen('Flip', window); % Present blank screen + % Write message to EDF file to mark time when blank screen is presented + Eyelink('Message', 'BLANK_SCREEN'); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Finish writing last instance of dynamic IA in the text file for each trial + % See DataViewer manual section: Working with Events, Samples and Interest Areas > Interest Areas + fprintf(f,'%d %d ELLIPSE 1 %d %d %d %d TARGET\n', round((stTime-stStart)*1000)*-1, round(((blankTime-stStart)*1000))*-1,... + round(previousX)-IAsize, round(previousY)-IAsize, round(x)+IAsize, round(y)+IAsize); + + % Stop recording eye movements at the end of each trial + WaitSecs(0.1); % Add 100 msec of data to catch final events before stopping + Eyelink('StopRecording'); % Stop tracker recording + + % STEP 5.5: CREATE VARIABLES FOR DATAVIEWER; END TRIAL; CLOSE DYNAMIC IA FILE + + % Write !V TRIAL_VAR messages to EDF file: creates trial variables in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Trial Message Commands + Eyelink('Message', '!V TRIAL_VAR iteration %d', i); % Trial iteration + Eyelink('Message', '!V TRIAL_VAR type %s', char(type(i))); % Image name + Eyelink('Message', '!V TRIAL_VAR frequency_x %s', num2str(trials(1,i))); + Eyelink('Message', '!V TRIAL_VAR frequency_y %s', num2str(trials(2,i))); + WaitSecs(0.001); % Allow some time between messages. Some messages can be lost if too many are written at the same time + Eyelink('Message', '!V TRIAL_VAR amplitude_x %d', round(amplitudeX)); + Eyelink('Message', '!V TRIAL_VAR amplitude_y %d', round(amplitudeY)); + Eyelink('Message', '!V TRIAL_VAR phase_x %d', trials(3,i)); + Eyelink('Message', '!V TRIAL_VAR phase_y %d', trials(4,i)); + + + % Write TRIAL_RESULT message to EDF file: marks the end of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIAL_RESULT 0'); + WaitSecs(0.01); % Allow some time before ending the trial + fclose(f); % Close dynamic IA file + end % End trial loop + + + %% STEP 6: CLOSE EDF FILE. TRANSFER EDF COPY TO DISPLAY PC. CLOSE EYELINK CONNECTION. FINISH UP + + % Put tracker in idle/offline mode before closing file. Eyelink('SetOfflineMode') is recommended. + % However if Eyelink('Command', 'set_idle_mode') is used, allow 50ms before closing the file as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('Command', 'clear_screen 0'); % Clear Host PC backdrop graphics at the end of the experiment + WaitSecs(0.5); % Allow some time before closing and transferring file + Eyelink('CloseFile'); % Close EDF file on Host PC + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below +catch % If syntax error is detected + cleanup; + % Print error message and line number in Matlab's Command Window + psychrethrow(psychlasterror); +end +PsychPortAudio('Close', pahandle); +PsychPortAudio('Close', pamaster); + + +% Cleanup function used throughout the script above + function cleanup + sca; % PTB's wrapper for Screen('CloseAll') & related cleanup, e.g. ShowCursor + Eyelink('Shutdown'); % Close EyeLink connection + ListenChar(0); % Restore keyboard output to Matlab + if ~IsOctave; commandwindow; end % Bring Command Window to front + end + +% Function for transferring copy of EDF file to the experiment folder on Display PC. +% Allows for optional destination path which is different from experiment folder + function transferFile + try + if dummymode ==0 % If connected to EyeLink + % Show 'Receiving data file...' text until file transfer is complete + Screen('FillRect', window, el.backgroundcolour); % Prepare background on backbuffer + Screen('DrawText', window, 'Receiving data file...', 5, height-35, 0); % Prepare text + Screen('Flip', window); % Present text + fprintf('Receiving data file ''%s.edf''\n', edfFile); % Print some text in Matlab's Command Window + + % Transfer EDF file to Host PC + % [status =] Eyelink('ReceiveFile',['src'], ['dest'], ['dest_is_path']) + status = Eyelink('ReceiveFile'); + + % Check if EDF file has been transferred successfully and print file size in Matlab's Command Window + if status > 0 + fprintf('EDF file size: %.1f KB\n', status/1024); % Divide file size by 1024 to convert bytes to KB + end + % Print transferred EDF file path in Matlab's Command Window + fprintf('Data file ''%s.edf'' can be found in ''%s''\n', edfFile, pwd); + else + fprintf('No EDF file saved in Dummy mode\n'); + end + cleanup; + catch % Catch a file-transfer error and print some text in Matlab's Command Window + fprintf('Problem receiving data file ''%s''\n', edfFile); + cleanup; + psychrethrow(psychlasterror); + end + end +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimplePicture/EyeLink_SimplePicture.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimplePicture/EyeLink_SimplePicture.m new file mode 100644 index 0000000000..c32da847e1 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimplePicture/EyeLink_SimplePicture.m @@ -0,0 +1,383 @@ +function EyeLink_SimplePicture(screenNumber) +% A simple EyeLink integration demo that records eye movements passively +% while an image is presented on the screen. Each trial ends when the +% space bar or a button is pressed. +% +% Usage: +% Eyelink_SimplePicture(screenNumber) +% +% screenNumber is an optional parameter which can be used to pass a specific value to PsychImaging('OpenWindow', ...) +% If screenNumber is not specified, or if isempty(screenNumber) then the default: +% screenNumber = max(Screen('Screens')); +% will be used. + +% Bring the Command Window to the front if it is already open +if ~IsOctave; commandwindow; end + +PsychDefaultSetup(2); + +% Use default screenNumber if none specified +if (nargin < 1) + screenNumber = []; +end + +try + %% STEP 1: INITIALIZE EYELINK CONNECTION; OPEN EDF FILE; GET EYELINK TRACKER VERSION + + % Initialize EyeLink connection (dummymode = 0) or run in "Dummy Mode" without an EyeLink connection (dummymode = 1); + dummymode = 0; + + % Optional: Set IP address of eyelink tracker computer to connect to. + % Call this before initializing an EyeLink connection if you want to use a non-default IP address for the Host PC. + %Eyelink('SetAddress', '10.10.10.240'); + + EyelinkInit(dummymode); % Initialize EyeLink connection + status = Eyelink('IsConnected'); + if status < 1 % If EyeLink not connected + dummymode = 1; + end + + % Open dialog box for EyeLink Data file name entry. File name up to 8 characters + prompt = {'Enter EDF file name (up to 8 characters)'}; + dlg_title = 'Create EDF file'; + def = {'demo'}; % Create a default edf file name + answer = inputdlg(prompt, dlg_title, 1, def); % Prompt for new EDF file name + % Print some text in Matlab's Command Window if a file name has not been entered + if isempty(answer) + fprintf('Session cancelled by user\n') + error('Session cancelled by user'); % Abort experiment (see cleanup function below) + end + edfFile = answer{1}; % Save file name to a variable + % Print some text in Matlab's Command Window if file name is longer than 8 characters + if length(edfFile) > 8 + fprintf('Filename needs to be no more than 8 characters long (letters, numbers and underscores only)\n'); + error('Filename needs to be no more than 8 characters long (letters, numbers and underscores only)'); + end + + % Open an EDF file and name it + failOpen = Eyelink('OpenFile', edfFile); + if failOpen ~= 0 % Abort if it fails to open + fprintf('Cannot create EDF file %s', edfFile); % Print some text in Matlab's Command Window + error('Cannot create EDF file %s', edfFile); % Print some text in Matlab's Command Window + end + + % Get EyeLink tracker and software version + % returns 0 if not connected + % returns 'EYELINK I', 'EYELINK II x.xx', 'EYELINK CL x.xx' where 'x.xx' is the software version + ELsoftwareVersion = 0; % Default EyeLink version in dummy mode + [ver, versionstring] = Eyelink('GetTrackerVersion'); + if dummymode == 0 % If connected to EyeLink + % Extract software version number. + [~, vnumcell] = regexp(versionstring,'.*?(\d)\.\d*?','Match','Tokens'); % Extract EL version before decimal point + ELsoftwareVersion = str2double(vnumcell{1}{1}); % Returns 1 for EyeLink I, 2 for EyeLink II, 3/4 for EyeLink 1K, 5 for EyeLink 1KPlus, 6 for Portable Duo + % Print some text in Matlab's Command Window + fprintf('Running experiment on %s version %d\n', versionstring, ver ); + end + % Add a line of text in the EDF file to identify the current experimemt name and session. This is optional. + % If your text starts with "RECORDED BY " it will be available in DataViewer's Inspector window by clicking + % the EDF session node in the top panel and looking for the "Recorded By:" field in the bottom panel of the Inspector. + preambleText = sprintf('RECORDED BY Psychtoolbox demo %s session name: %s', mfilename, edfFile); + Eyelink('Command', 'add_file_preamble_text "%s"', preambleText); + + + %% STEP 2: SELECT AVAILABLE SAMPLE/EVENT DATA + % See EyeLinkProgrammers Guide manual > Useful EyeLink Commands > File Data Control & Link Data Control + + % Select which events are saved in the EDF file. Include everything just in case + Eyelink('Command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); + % Select which events are available online for gaze-contingent experiments. Include everything just in case + Eyelink('Command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON,FIXUPDATE,INPUT'); + % Select which sample data is saved in EDF file or available online. Include everything just in case + if ELsoftwareVersion > 3 % Check tracker version and include 'HTARGET' to save head target sticker data for supported eye trackers + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,HTARGET,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,HTARGET,STATUS,INPUT'); + else + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); + end + + %% STEP 3: OPEN GRAPHICS WINDOW + + % Open experiment graphics on the specified screen + if isempty(screenNumber) + screenNumber = max(Screen('Screens')); % Use default screen if none specified + end + + window = PsychImaging('OpenWindow', screenNumber, GrayIndex(screenNumber)); % Open graphics window + Screen('Flip', window); + + % Get max color value for rescaling to RGB for Host PC & Data Viewer integration + colorMaxVal = Screen('ColorRange', window); + % Return width and height of the graphics window/screen in pixels + [width, height] = Screen('WindowSize', window); + + + %% STEP 4: SET CALIBRATION SCREEN COLOURS/SOUNDS; PROVIDE WINDOW SIZE TO EYELINK HOST & DATAVIEWER; SET CALIBRATION PARAMETERS; CALIBRATE + + % Provide EyeLink with some defaults, which are returned in the structure "el". + el = EyelinkInitDefaults(window); + % set calibration/validation/drift-check(or drift-correct) size as well as background and target colors. + % It is important that this background colour is similar to that of the stimuli to prevent large luminance-based + % pupil size changes (which can cause a drift in the eye movement data) + el.calibrationtargetsize = 3;% Outer target size as percentage of the screen + el.calibrationtargetwidth = 0.7;% Inner target size as percentage of the screen + el.backgroundcolour = repmat(GrayIndex(window),1,3); + el.calibrationtargetcolour = repmat(BlackIndex(window),1,3); + % set "Camera Setup" instructions text colour so it is different from background colour + el.msgfontcolour = repmat(BlackIndex(window),1,3); + + % Use an image file instead of the default calibration bull's eye targets. + % Commenting out the following two lines will use default targets: + el.calTargetType = 'image'; + el.calImageTargetFilename = [pwd '/' 'fixTarget.jpg']; + + % Set calibration beeps (0 = sound off, 1 = sound on) + el.targetbeep = 1; % sound a beep when a target is presented + el.feedbackbeep = 1; % sound a beep after calibration or drift check/correction + + % Initialize PsychSound for calibration/validation audio feedback + % EyeLink Toolbox now supports PsychPortAudio integration and interop + % with legacy Snd() wrapping. Below we open the default audio device in + % output mode as master, create a slave device, and pass the device + % handle to el.ppa_pahandle. + % el.ppa_handle supports passing either standard mode handle, or as + % below one opened as a slave device. When el.ppa_handle is empty, for + % legacy support EyelinkUpdateDefaults() will open the default device + % and use that with Snd() interop, and close the device handle when + % calling Eyelink('Shutdown') at the end of the script. + InitializePsychSound(); + pamaster = PsychPortAudio('Open', [], 8+1); + PsychPortAudio('Start', pamaster); + pahandle = PsychPortAudio('OpenSlave', pamaster, 1); + el.ppa_pahandle = pahandle; + + % You must call this function to apply the changes made to the el structure above + EyelinkUpdateDefaults(el); + + % Set display coordinates for EyeLink data by entering left, top, right and bottom coordinates in screen pixels + Eyelink('Command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Write DISPLAY_COORDS message to EDF file: sets display coordinates in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Pre-trial Message Commands + Eyelink('Message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Set number of calibration/validation dots and spread: horizontal-only(H) or horizontal-vertical(HV) as H3, HV3, HV5, HV9 or HV13 + Eyelink('Command', 'calibration_type = HV9'); % horizontal-vertical 9-points + % Allow a supported EyeLink Host PC button box to accept calibration or drift-check/correction targets via button 5 + Eyelink('Command', 'button_function 5 "accept_target_fixation"'); + % Hide mouse cursor + HideCursor(window); + % Suppress keypress output to command window. + ListenChar(-1); + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + + % Put EyeLink Host PC in Camera Setup mode for participant setup/calibration + EyelinkDoTrackerSetup(el); + + %% STEP 5: TRIAL LOOP. + spaceBar = KbName('space');% Identify keyboard key code for spacebar to end each trial later on + imgList = {'img1.jpg' 'img2.jpg'};% Provide image list for 2 trials + for i = 1:length(imgList) + + % STEP 5.1: START TRIAL; SHOW TRIAL INFO ON HOST PC; SHOW BACKDROP IMAGE AND/OR DRAW FEEDBACK GRAPHICS ON HOST PC; DRIFT-CHECK/CORRECTION + + % Write TRIALID message to EDF file: marks the start of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIALID %d', i); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + % Supply the trial number as a line of text on Host PC screen + Eyelink('Command', 'record_status_message "TRIAL %d/%d"', i, length(imgList)); + + % Draw graphics on the EyeLink Host PC display. See COMMANDS.INI in the Host PC's exe folder for a list of commands + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode before drawing Host PC graphics and before recording + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Optional: Send an image to the Host PC to be displayed as the backdrop image over which + % the gaze-cursor is overlayed during trial recordings. + % See Eyelink('ImageTransfer?') for information about supported syntax and compatible image formats. + % Below, we use the new option to pass image data from imread() as the imageArray parameter, which + % enables the use of many image formats. + % [status] = Eyelink('ImageTransfer', imageArray, xs, ys, width, height, xd, yd, options); + % xs, ys: top-left corner of the region to be transferred within the source image + % width, height: size of region to be transferred within the source image (note, values of 0 will include the entire width/height) + % xd, yd: location (top-left) where image region to be transferred will be presented on the Host PC + % This image transfer function works for non-resized image presentation only. If you need to resize images and use this function please resize + % the original image files beforehand + imgName = char(imgList(i)); % Get image file name for current trial + imgInfo = imfinfo(imgName); % Get image file info + imgData = imread(imgName); % Get image file data + transferStatus = Eyelink('ImageTransfer', imgData, 0, 0, 0, 0, round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2)); + + if dummymode == 0 && transferStatus ~= 0 % If connected to EyeLink and image transfer fails + fprintf('Image transfer Failed\n'); % Print some text in Matlab's Command Window + end + + % Optional: draw feedback box and lines on Host PC interface instead of (or on top of) backdrop image. + % See section 25.7 'Drawing Commands' in the EyeLink Programmers Guide manual + Eyelink('Command', 'draw_box %d %d %d %d 15', round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2)); + + % Perform a drift check/correction. + % Optionally provide x y target location, otherwise target is presented on screen centre + EyelinkDoDriftCorrection(el, round(width/2), round(height/2)); + + %STEP 5.2: START RECORDING + + % Put tracker in idle/offline mode before recording. Eyelink('SetOfflineMode') is recommended + % however if Eyelink('Command', 'set_idle_mode') is used allow 50ms before recording as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode before recording + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode before recording + Eyelink('StartRecording'); % Start tracker recording + WaitSecs(0.1); % Allow some time to record a few samples before presenting first stimulus + + % STEP 5.3: PRESENT STIMULUS; CREATE DATAVIEWER BACKDROP AND INTEREST AREA + + % Prepare and present stimulus + Screen('FillRect', window, el.backgroundcolour);% Prepare grey background on backbuffer + imgTexture = Screen('MakeTexture',window, imgData); % Convert image file to texture + Screen('DrawTexture', window, imgTexture); % Prepare image texture on backbuffer + Screen('TextSize', window, 30); % Specify text size + Screen('DrawText', window, 'Press space or button to end trial', 5, height-35, 0); % Prepare text on backbuffer + [~, RtStart] = Screen('Flip', window); % Present stimulus + % Write message to EDF file to mark the start time of stimulus presentation. + Eyelink('Message', 'STIM_ONSET'); + % Write !V IMGLOAD message to EDF file: provides instructions for DataViewer so it will show trial stimulus as backdrop + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Image Commands + Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', imgName, width/2, height/2); + % Write !V IAREA message to EDF file: creates interest area around image in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Interest Area Commands + Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 1, round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2),'IMAGE_IA'); + + % STEP 5.4: WAIT FOR KEYPRESS/BUTTON; SHOW BLANK SCREEN; STOP RECORDING + + while 1 % loop until error, space bar or button press + % Check that eye tracker is still recording. Otherwise close and transfer copy of EDF file to Display PC + err = Eyelink('CheckRecording'); + if(err ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below) + error('EyeLink is not in record mode when it should be. Unknown error. EDF transferred from Host PC to Display PC, please check its integrity.'); + end + % End trial if spacebar is pressed + [~, RtEnd, keyCode] = KbCheck; + if keyCode(spaceBar) + % Write message to EDF file to mark the spacebar press time + Eyelink('Message', 'KEY_PRESSED'); + reactionTime = round((RtEnd-RtStart)*1000); % Calculate RT from stimulus onset + break; % Exit while loop + end + % End trial if button 5 on a supported Host PC button box is pressed + % Use (button number * -1) + 1 to determine bitshift value + % (e.g., button 5 should use bitshift value of -4) + buttonResult = Eyelink('ButtonStates'); + if buttonResult + if bitshift(buttonResult, -4) == 1 + % Write message to EDF file to mark the button press time + Eyelink('Message', 'BUTTON_PRESSED'); + reactionTime = round((GetSecs-RtStart)*1000); % Calculate RT from stimulus onset + break; % Exit while loop + end + end + end % End of while loop + + % Draw blank screen at end of trial + Screen('FillRect', window, el.backgroundcolour); % Prepare grey background on backbuffer + Screen('Flip', window); % Present blank screen + % Write message to EDF file to mark time when blank screen is presented + Eyelink('Message', 'BLANK_SCREEN'); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Stop recording eye movements at the end of each trial + WaitSecs(0.1); % Add 100 msec of data to catch final events before stopping + Eyelink('StopRecording'); % Stop tracker recording + + % STEP 5.5: CREATE VARIABLES FOR DATAVIEWER; END TRIAL + + % Write !V TRIAL_VAR messages to EDF file: creates trial variables in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Trial Message Commands + Eyelink('Message', '!V TRIAL_VAR iteration %d', i); % Trial iteration + Eyelink('Message', '!V TRIAL_VAR image %s', imgName); % Image name + WaitSecs(0.001); % Allow some time between messages. Some messages can be lost if too many are written at the same time + Eyelink('Message', '!V TRIAL_VAR rt %d', reactionTime); % Reaction time + % Write TRIAL_RESULT message to EDF file: marks the end of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIAL_RESULT 0'); + WaitSecs(0.01); % Allow some time before ending the trial + + % Clear Screen() textures that were initialized for each trial iteration + Screen('Close', imgTexture); + end % End trial loop + + + %% STEP 6: CLOSE EDF FILE. TRANSFER EDF COPY TO DISPLAY PC. CLOSE EYELINK CONNECTION. FINISH UP + + % Put tracker in idle/offline mode before closing file. Eyelink('SetOfflineMode') is recommended. + % However if Eyelink('Command', 'set_idle_mode') is used, allow 50ms before closing the file as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('Command', 'clear_screen 0'); % Clear Host PC backdrop graphics at the end of the experiment + WaitSecs(0.5); % Allow some time before closing and transferring file + Eyelink('CloseFile'); % Close EDF file on Host PC + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below +catch % If syntax error is detected + % Print error message and line number in Matlab's Command Window + psychrethrow(psychlasterror); +end +PsychPortAudio('Close', pahandle); +PsychPortAudio('Close', pamaster); +cleanup; + +% Cleanup function used throughout the script above + function cleanup + sca; % PTB's wrapper for Screen('CloseAll') & related cleanup, e.g. ShowCursor + Eyelink('Shutdown'); % Close EyeLink connection + ListenChar(0); % Restore keyboard output to Matlab + if ~IsOctave; commandwindow; end % Bring Command Window to front + end + +% Function for transferring copy of EDF file to the experiment folder on Display PC. +% Allows for optional destination path which is different from experiment folder + function transferFile + try + if dummymode ==0 % If connected to EyeLink + % Show 'Receiving data file...' text until file transfer is complete + Screen('FillRect', window, el.backgroundcolour); % Prepare background on backbuffer + Screen('DrawText', window, 'Receiving data file...', 5, height-35, 0); % Prepare text + Screen('Flip', window); % Present text + fprintf('Receiving data file ''%s.edf''\n', edfFile); % Print some text in Matlab's Command Window + + % Transfer EDF file to Host PC + % [status =] Eyelink('ReceiveFile',['src'], ['dest'], ['dest_is_path']) + status = Eyelink('ReceiveFile'); + % Optionally uncomment below to change edf file name when a copy is transferred to the Display PC + % % If is omitted, tracker will send last opened data file. + % % If is omitted, creates local file with source file name. + % % Else, creates file using as name. If is supplied and non-zero + % % uses source file name but adds as directory path. + % newName = ['Test_',char(datetime('now','TimeZone','local','Format','y_M_d_HH_mm')),'.edf']; + % status = Eyelink('ReceiveFile', [], newName, 0); + + % Check if EDF file has been transferred successfully and print file size in Matlab's Command Window + if status > 0 + fprintf('EDF file size: %.1f KB\n', status/1024); % Divide file size by 1024 to convert bytes to KB + end + % Print transferred EDF file path in Matlab's Command Window + fprintf('Data file ''%s.edf'' can be found in ''%s''\n', edfFile, pwd); + else + fprintf('No EDF file saved in Dummy mode\n'); + end + catch % Catch a file-transfer error and print some text in Matlab's Command Window + fprintf('Problem receiving data file ''%s''\n', edfFile); + psychrethrow(psychlasterror); + end + end +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimplePicture/fixTarget.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimplePicture/fixTarget.jpg new file mode 100644 index 0000000000..b9972931b1 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimplePicture/fixTarget.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimplePicture/img1.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimplePicture/img1.jpg new file mode 100644 index 0000000000..00663e2e44 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimplePicture/img1.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimplePicture/img2.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimplePicture/img2.jpg new file mode 100644 index 0000000000..3fa77e178b Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimplePicture/img2.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/EyeLink_SimpleVideo.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/EyeLink_SimpleVideo.m new file mode 100644 index 0000000000..6253742c66 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/EyeLink_SimpleVideo.m @@ -0,0 +1,391 @@ +function EyeLink_SimpleVideo(screenNumber) +% Simple video demo with EyeLink integration and animated calibration / drift-check/correction targets. +% In each trial eye movements are recorded while a video stimulus is presented on the screen. +% Each trial ends when the space bar is pressed or the video stops playing. A different drift-check/correction +% animated target is used in each of the 2 trials. +% +% Illustrates how a video file can be added for trial play back in Data Viewer's "Trial Play Back Animation" view. +% +% Usage: +% Eyelink_SimpleVideo(screenNumber) +% +% screenNumber is an optional parameter which can be used to pass a specific value to PsychImaging('OpenWindow', ...) +% If screenNumber is not specified, or if isempty(screenNumber) then the default: +% screenNumber = max(Screen('Screens')); +% will be used. + +% Bring the Command Window to the front if it is already open +if ~IsOctave; commandwindow; end + +PsychDefaultSetup(2); + +% Initialize PsychSound for calibration/validation audio feedback +InitializePsychSound(); + +% Use default screenNumber if none specified +if (nargin < 1) + screenNumber = []; +end + +try + %% STEP 1: INITIALIZE EYELINK CONNECTION; OPEN EDF FILE; GET EYELINK TRACKER VERSION + + % Initialize EyeLink connection (dummymode = 0) or run in "Dummy Mode" without an EyeLink connection (dummymode = 1); + dummymode = 0; + EyelinkInit(dummymode); % Initialize EyeLink connection + status = Eyelink('IsConnected'); + if status < 1 % If EyeLink not connected + dummymode = 1; + end + + % Open dialog box for EyeLink Data file name entry. File name up to 8 characters + prompt = {'Enter EDF file name (up to 8 characters)'}; + dlg_title = 'Create EDF file'; + def = {'demo'}; % Create a default edf file name + answer = inputdlg(prompt, dlg_title, 1, def); % Prompt for new EDF file name + % Print some text in Matlab's Command Window if a file name has not been entered + if isempty(answer) + fprintf('Session cancelled by user\n') + cleanup; % Abort experiment (see cleanup function below) + return + end + edfFile = answer{1}; % Save file name to a variable + % Print some text in Matlab's Command Window if file name is longer than 8 characters + if length(edfFile) > 8 + fprintf('Filename needs to be no more than 8 characters long (letters, numbers and underscores only)\n'); + cleanup; % Abort experiment (see cleanup function below) + return + end + + % Open an EDF file and name it + if Eyelink('IsConnected') == 1 % if we have a live connection to a Host PC + failOpen = Eyelink('OpenFile', edfFile); + if failOpen ~= 0 % Abort if it fails to open + fprintf('Cannot create EDF file %s', edfFile); % Print some text in Matlab's Command Window + cleanup; %see cleanup function below + return + end + end + + % Get EyeLink tracker and software version + % returns 0 if not connected + % returns 'EYELINK I', 'EYELINK II x.xx', 'EYELINK CL x.xx' where 'x.xx' is the software version + ELsoftwareVersion = 0; % Default EyeLink version in dummy mode + [ver, versionstring] = Eyelink('GetTrackerVersion'); + if dummymode == 0 % If connected to EyeLink + % Extract software version number. + [~, vnumcell] = regexp(versionstring,'.*?(\d)\.\d*?','Match','Tokens'); % Extract EL version before decimal point + ELsoftwareVersion = str2double(vnumcell{1}{1}); % Returns 1 for EyeLink I, 2 for EyeLink II, 3/4 for EyeLink 1K, 5 for EyeLink 1KPlus, 6 for Portable Duo + % Print some text in Matlab's Command Window + fprintf('Running experiment on %s version %d\n', versionstring, ver ); + end + % Add a line of text in the EDF file to identify the current experimemt name and session. This is optional. + % If your text starts with "RECORDED BY " it will be available in DataViewer's Inspector window by clicking + % the EDF session node in the top panel and looking for the "Recorded By:" field in the bottom panel of the Inspector. + preambleText = sprintf('RECORDED BY Psychtoolbox demo %s session name: %s', mfilename, edfFile); + Eyelink('Command', 'add_file_preamble_text "%s"', preambleText); + + + %% STEP 2: SELECT AVAILABLE SAMPLE/EVENT DATA + % See EyeLinkProgrammers Guide manual > Useful EyeLink Commands > File Data Control & Link Data Control + + % Select which events are saved in the EDF file. Include everything just in case + Eyelink('Command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); + % Select which events are available online for gaze-contingent experiments. Include everything just in case + Eyelink('Command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON,FIXUPDATE,INPUT'); + % Select which sample data is saved in EDF file or available online. Include everything just in case + if ELsoftwareVersion > 3 % Check tracker version and include 'HTARGET' to save head target sticker data for supported eye trackers + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,HTARGET,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,HTARGET,STATUS,INPUT'); + else + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); + end + + + %% STEP 3: OPEN GRAPHICS WINDOW + + % Open experiment graphics on the specified screen + if isempty(screenNumber) + screenNumber = max(Screen('Screens')); % Use default screen if none specified + end + + window = PsychImaging('OpenWindow', screenNumber, GrayIndex(screenNumber)); % Open graphics window + Screen('Flip', window); + + % Get max color value for rescaling to RGB for Host PC & Data Viewer integration + colorMaxVal = Screen('ColorRange', window); + % Return width and height of the graphics window/screen in pixels + [width, height] = Screen('WindowSize', window); + + + %% STEP 4: SET CALIBRATION SCREEN COLOURS/SOUNDS; PROVIDE WINDOW SIZE TO EYELINK HOST & DATAVIEWER; SET CALIBRATION PARAMETERS; CALIBRATE + + % Provide EyeLink with some defaults, which are returned in the structure "el". + el = EyelinkInitDefaults(window); + % set calibration/validation/drift-check(or drift-correct) background color. + % It is important that this background colour is similar to that of the stimuli to prevent large luminance-based + % pupil size changes (which can cause a drift in the eye movement data) + el.backgroundcolour = repmat(0.45,1,3); % 0.45 == gray matching video stimuli background in normalized color space units + % set "Camera Setup" instructions text colour so it is different from background colour + el.msgfontcolour = repmat(BlackIndex(screenNumber),1,3); + + % Set calibration beeps (0 = sound off, 1 = sound on) + % Setting beeps to off (0) for video targets + el.targetbeep = 0; % sound a beep when a target is presented + el.feedbackbeep = 0; % sound a beep after calibration or drift check/correction + + % Required for macOS Catalina users (/w PTB 3.0.17.11) to disable audio + % with animated calibration targets and trial video stimuli to + % avoid freezing in video playback + spcf1 = 0; % Used both here in el struct for animated target movies, and also below when loading trial-stimulus movies + if IsOSX + [status, result] = system('sw_vers'); + if regexp(result,'ProductVersion\D*10\.15') + spcf1 = 2; + el.calAnimationOpenSpecialFlags1 = spcf1; % for Screen('OpenMovie', ..., specialFlags1) see http://psychtoolbox.org/docs/Screen-OpenMovie + end + end + % N.B. el.calAnimationOpenSpecialFlags1 = 2 may also be required with certain versions of + % GStreamer on Ubuntu Linux. In certain cases on Linux (e.g. when PsychPortAudio('GetOpenDeviceCount') > 0), + % disabling the audio is required to avoid a crash, or more generally a brief delay oafter the first + % frame of the target video is displayed on screen and the eventual continuation of playback + + + % Configure animated calibration target path and properties + el.calTargetType = 'video'; + calMovieName = ('calibVid.mov'); + + el.calAnimationTargetFilename = [pwd '/' calMovieName]; + el.calAnimationResetOnTargetMove = true; % false by default, set to true to rewind/replay video from start every time target moves + el.calAnimationAudioVolume = 0.4; % default volume is 1.0, but too loud on some systems. Setting volume lower to 0.4 (minimum is 0.0) + + % You must call this function to apply the changes made to the el structure above + EyelinkUpdateDefaults(el); + + % Set display coordinates for EyeLink data by entering left, top, right and bottom coordinates in screen pixels + Eyelink('Command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Write DISPLAY_COORDS message to EDF file: sets display coordinates in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Pre-trial Message Commands + Eyelink('Message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Set number of calibration/validation dots and spread: horizontal-only(H) or horizontal-vertical(HV) as H3, HV3, HV5, HV9 or HV13 + Eyelink('Command', 'calibration_type = HV5'); % horizontal-vertical 5-points + % Allow a supported EyeLink Host PC button box to accept calibration or drift-check/correction targets via button 5 + Eyelink('Command', 'button_function 5 "accept_target_fixation"'); + % Hide mouse cursor + HideCursor(window); + % Suppress keypress output to command window. + ListenChar(-1); + % Clear Host PC display from any previus drawing + Eyelink('Command', 'clear_screen 0'); + % Put EyeLink Host PC in Camera Setup mode for participant setup/calibration + EyelinkDoTrackerSetup(el); + + + %% STEP 5: TRIAL LOOP. + + driftVidList = {'dotsGrey.mov' 'wheelGrey.mov'};% Provide drift-check video file list for 2 trials + vidList = {'expected.mov' 'disappear.mov'};% Provide trial video file list for 2 trials + + spaceBar = KbName('space');% Identify keyboard key code for space bar to end each trial later on + for i = 1:length(vidList) + + % Change animated calibration target path for drift-check/correction + calMovieName = char(driftVidList(i)); + el.calAnimationTargetFilename = [pwd '/' calMovieName]; + % You must call this function to apply the changes made to the el structure above + EyelinkUpdateDefaults(el); + + % STEP 5.0 DRIFT-CHECK/CORRECTION + % Perform a drift check/correction. + % Optionally provide x y target location, otherwise target is presented on screen centre + EyelinkDoDriftCorrection(el, round(width/2), round(height/2)); + + % STEP 5.1: START TRIAL; SHOW TRIAL INFO ON HOST PC; SHOW BACKDROP IMAGE AND/OR DRAW FEEDBACK GRAPHICS ON HOST PC + % Open movie file: + movieName = char(vidList(i)); + moviePath = [ pwd '/' movieName ]; + [movie, ~, ~, Movx, Movy] = Screen('OpenMovie', window, moviePath, [], [], spcf1); % spcf1 required to disable audio on macOS Catalina and avoid playback freezing issues + + % Write TRIALID message to EDF file: marks the start of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIALID %d', i); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + % Supply the trial number as a line of text on Host PC screen + Eyelink('Command', 'record_status_message "TRIAL %d %s"', i, movieName); + % Draw graphics on the EyeLink Host PC display. See COMMANDS.INI in the Host PC's exe folder for a list of commands + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode before drawing Host PC graphics and before recording + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Optional: draw feedback box and lines on Host PC interface + % See section 25.7 'Drawing Commands' in the EyeLink Programmers Guide manual + Eyelink('Command', 'draw_box %d %d %d %d 15', round(width/2-Movx/2), round(height/2-Movy/2), round(width/2+Movx/2), round(height/2+Movy/2)); + Eyelink('Command', 'draw_box %d %d %d %d 15', round(width/2-80), round(height/2-70), round(width/2+80), round(height/2+90)); + Eyelink('Command', 'draw_line %d %d %d %d 15', round(width/2-Movx/2), round(height/2)+40, round(width/2+Movx/2), round(height/2)+40); + + %STEP 5.2: START RECORDING + + % Put tracker in idle/offline mode before recording. Eyelink('SetOfflineMode') is recommended + % however if Eyelink('Command', 'set_idle_mode') is used allow 50ms before recording as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode before recording + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode before recording + Eyelink('StartRecording'); % Start tracker recording + WaitSecs(0.1); % Allow some time to record a few samples before presenting first stimulus + + % STEP 5.3: PRESENT VIDEO; CREATE DATAVIEWER BACKDROP AND INTEREST AREA; STOP RECORDING + + timeOut = 'yes'; % Variable set to a default value. Changes to 'no' if key pressed to end video early + % Start playback engine: + Screen('PlayMovie', movie, 1); + frameNum = 0; + % Wait until user releases keys on keyboard: + KbReleaseWait; + % Playback loop: Runs until end of movie or keypress: + while 1 + % Check that eye tracker is still recording. Otherwise close and transfer copy of EDF file to Display PC + error = Eyelink('CheckRecording'); + if(error ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below + cleanup; % Abort experiment (see cleanup function below) + return + end + % Wait for next movie frame, retrieve texture handle to it + tex = Screen('GetMovieImage', window, movie); + if tex<=0 % Valid texture returned? A negative value means end of movie reached + break; + end + % Draw the new texture immediately to screen: + Screen('DrawTexture', window, tex); + % Update display: + Screen('Flip', window); + frameNum = frameNum + 1; + if frameNum == 1 + % Write message to EDF file to mark the start time of stimulus presentation. + Eyelink('Message', 'STIM_ONSET'); + % Write !V IAREA message to EDF file: creates interest areas in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Interest Area Commands + Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 1, round(width/2-80), round(height/2-70), round(width/2+80), round(height/2+90), 'BOX_IA'); + vidStart = GetSecs; % Start a timer + end + % Write message to EDF file to mark the time of each video frame + Eyelink('Message', 'Frame to be displayed %d', frameNum); + % Write a !V VFRAME message to the data file specifying the frame number, location and file name so DataViewer can play back the video + Eyelink('Message', '%d !V VFRAME %d %d %d %s', 0, frameNum, round(width/2-Movx/2), round(height/2-Movy/2), movieName); + % End trial if space bar is pressed + [~, kbSecs, keyCode] = KbCheck; + if keyCode(spaceBar) + % Write message to EDF file to mark the space bar press time + Eyelink('Message', 'KEY_PRESSED'); + timeOut = 'no'; + % Release texture: + Screen('Close', tex); + break; + end + Screen('Close', tex); % Release texture if no key is pressed + end % End while loop + Screen('PlayMovie', movie, 0); % Stop playback + Screen('CloseMovie', movie); % Close movie + + % Draw blank screen at end of trial + Screen('FillRect', window, el.backgroundcolour); + [~, vidEnd] = Screen('Flip', window); % Present blank screen + % Write message to EDF file to mark time when blank screen is presented + Eyelink('Message', 'BLANK_SCREEN'); + + % Calculate video duration + if strcmp(timeOut, 'yes') % If no key pressed during video + vidDur = round((vidEnd-vidStart)*1000); % Duration of video until BLANK_SCREEN + else % If key pressed during video + vidDur = round((kbSecs-vidStart)*1000); % Duration of video until key is pressed + end + + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Stop recording eye movements at the end of each trial + WaitSecs(0.1); % Add 100 msec of data to catch final events before stopping + Eyelink('StopRecording'); % Stop tracker recording + + % STEP 5.4: CREATE VARIABLES FOR DATAVIEWER; END TRIAL + + % Write !V TRIAL_VAR messages to EDF file: creates trial variables in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Trial Message Commands + Eyelink('Message', '!V TRIAL_VAR iteration %d', i); % Trial iteration + Eyelink('Message', '!V TRIAL_VAR video_file %s', movieName); % Video name + Eyelink('Message', '!V TRIAL_VAR video_duration %d', vidDur); % Video duration until key press or end of video + Eyelink('Message', '!V TRIAL_VAR timeout %s', timeOut); % Key pressed to end trial early? 'yes' or 'no' + % Write TRIAL_RESULT message to EDF file: marks the end of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIAL_RESULT 0'); + WaitSecs(0.01); % Allow some time before ending the trial + end % End trial loop + + + %% STEP 6: CLOSE EDF FILE. TRANSFER EDF COPY TO DISPLAY PC. CLOSE EYELINK CONNECTION. FINISH UP + + % Put tracker in idle/offline mode before closing file. Eyelink('SetOfflineMode') is recommended. + % However if Eyelink('Command', 'set_idle_mode') is used, allow 50ms before closing the file as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('Command', 'clear_screen 0'); % Clear Host PC backdrop graphics at the end of the experiment + WaitSecs(0.5); % Allow some time before closing and transferring file + Eyelink('CloseFile'); % Close EDF file on Host PC + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below +catch % If syntax error is detected + cleanup; + % Print error message and line number in Matlab's Command Window + psychrethrow(psychlasterror); +end + +% Cleanup function used throughout the script above + function cleanup + sca; % PTB's wrapper for Screen('CloseAll') & related cleanup, e.g. ShowCursor + Eyelink('Shutdown'); % Close EyeLink connection + ListenChar(0); % Restore keyboard output to Matlab + if ~IsOctave; commandwindow; end % Bring Command Window to front + end + +% Function for transferring copy of EDF file to the experiment folder on Display PC. +% Allows for optional destination path which is different from experiment folder + function transferFile + try + if dummymode ==0 % If connected to EyeLink + % Show 'Receiving data file...' text until file transfer is complete + Screen('FillRect', window, el.backgroundcolour); % Prepare background on backbuffer + Screen('DrawText', window, 'Receiving data file...', 5, height-35, 0); % Prepare text + Screen('Flip', window); % Present text + fprintf('Receiving data file ''%s.edf''\n', edfFile); % Print some text in Matlab's Command Window + + % Transfer EDF file to Host PC + % [status =] Eyelink('ReceiveFile',['src'], ['dest'], ['dest_is_path']) + status = Eyelink('ReceiveFile'); + + % Check if EDF file has been transferred successfully and print file size in Matlab's Command Window + if status > 0 + fprintf('EDF file size: %.1f KB\n', status/1024); % Divide file size by 1024 to convert bytes to KB + end + % Print transferred EDF file path in Matlab's Command Window + fprintf('Data file ''%s.edf'' can be found in ''%s''\n', edfFile, pwd); + else + fprintf('No EDF file saved in Dummy mode\n'); + end + cleanup; + catch % Catch a file-transfer error and print some text in Matlab's Command Window + fprintf('Problem receiving data file ''%s''\n', edfFile); + cleanup; + psychrethrow(psychlasterror); + end + end +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/calibVid.mov b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/calibVid.mov new file mode 100644 index 0000000000..3ebee93d4e Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/calibVid.mov differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/disappear.mov b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/disappear.mov new file mode 100644 index 0000000000..81ad5deb56 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/disappear.mov differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/dotsGrey.mov b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/dotsGrey.mov new file mode 100644 index 0000000000..2e5ae9c85a Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/dotsGrey.mov differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/expected.mov b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/expected.mov new file mode 100644 index 0000000000..81d8918641 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/expected.mov differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/wheelGrey.mov b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/wheelGrey.mov new file mode 100644 index 0000000000..408c7ef4d3 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/SimpleVideo/wheelGrey.mov differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/EyeLink_StereoPicture.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/EyeLink_StereoPicture.m new file mode 100644 index 0000000000..cb1f316bea --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/EyeLink_StereoPicture.m @@ -0,0 +1,427 @@ +function EyeLink_StereoPicture(stereoMode, screenNumber) +% EyeLink integration demo for stereo presentation. +% Records eye movements passively while presenting a stereo stimulus. Supports both split-screen mode +% and dual-monitor setup. +% Each trial ends when the space bar is pressed. +% Data Viewer integration with both left and right eyes superimposed on the same eye window view +% +% Usage: +% Eyelink_StereoPicture(stereoMode, screenNumber) +% +% ------------------------------------------------------------ +% Supported stereoMode parameters: +% +% Default: 4 == Split-screen mode. Free fusion (lefteye=left, righteye=right): This - together with a screenid of zero - is what you'll want +% to use on MS-Windows with dual-display setups for stereo output. +% +% 5 == Split-screen mode. Cross fusion (lefteye=right ...) +% +% 10 == Dual-Window stereo: Open two onscreen windows on two monitors, first one will +% display left-eye view, 2nd one right-eye view. Direct all drawing and +% flip commands to the first window, PTB will take care of the rest. This +% mode is mostly useful for dual-display stereo on MacOS/X. It only works +% on reasonably modern graphics hardware, will abort with an error on +% unsupported hardware. +% ------------------------------------------------------------ +% +% screenNumber is an optional parameter which can be used to pass a specific value to PsychImaging('OpenWindow', ...) +% If screenNumber is not specified, or if isempty(screenNumber) then the default: +% screenNumber = max(Screen('Screens')); +% will be used. + +PsychDefaultSetup(2); + +% Set default stereoMode if required +if (nargin < 1) || ((nargin >= 1) && isempty(stereoMode)) + stereoMode = 4; +end + +% Use default screenNumber +if (nargin < 2) + screenNumber = []; +end + +% Bring the Command Window to the front if it is already open +if ~IsOctave; commandwindow; end + +try + %% STEP 1: INITIALIZE EYELINK CONNECTION; OPEN EDF FILE; GET EYELINK TRACKER VERSION + + % Initialize EyeLink connection (dummymode = 0) or run in "Dummy Mode" without an EyeLink connection (dummymode = 1); + dummymode = 0; + EyelinkInit(dummymode); % Initialize EyeLink connection + status = Eyelink('IsConnected'); + if status < 1 % If EyeLink not connected + dummymode = 1; + end + + % Open dialog box for EyeLink Data file name entry. File name up to 8 characters + prompt = {'Enter EDF file name (up to 8 characters)'}; + dlg_title = 'Create EDF file'; + def = {'demo'}; % Create a default edf file name + answer = inputdlg(prompt, dlg_title, 1, def); % Prompt for new EDF file name + % Print some text in Matlab's Command Window if a file name has not been entered + if isempty(answer) + fprintf('Session cancelled by user\n') + cleanup; % Abort experiment (see cleanup function below) + return + end + edfFile = answer{1}; % Save file name to a variable + % Print some text in Matlab's Command Window if file name is longer than 8 characters + if length(edfFile) > 8 + fprintf('Filename needs to be no more than 8 characters long (letters, numbers and underscores only)\n'); + cleanup; % Abort experiment (see cleanup function below) + return + end + + % Open an EDF file and name it + failOpen = Eyelink('OpenFile', edfFile); + if failOpen ~= 0 % Abort if it fails to open + fprintf('Cannot create EDF file %s', edfFile); % Print some text in Matlab's Command Window + cleanup; %see cleanup function below + return + end + + % Get EyeLink tracker and software version + % returns 0 if not connected + % returns 'EYELINK I', 'EYELINK II x.xx', 'EYELINK CL x.xx' where 'x.xx' is the software version + ELsoftwareVersion = 0; % Default EyeLink version in dummy mode + [ver, versionstring] = Eyelink('GetTrackerVersion'); + if dummymode == 0 % If connected to EyeLink + % Extract software version number. + [~, vnumcell] = regexp(versionstring,'.*?(\d)\.\d*?','Match','Tokens'); % Extract EL version before decimal point + ELsoftwareVersion = str2double(vnumcell{1}{1}); % Returns 1 for EyeLink I, 2 for EyeLink II, 3/4 for EyeLink 1K, 5 for EyeLink 1KPlus, 6 for Portable Duo + % Print some text in Matlab's Command Window + fprintf('Running experiment on %s version %d\n', versionstring, ver ); + end + % Add a line of text in the EDF file to identify the current experimemt name and session. This is optional. + % If your text starts with "RECORDED BY " it will be available in DataViewer's Inspector window by clicking + % the EDF session node in the top panel and looking for the "Recorded By:" field in the bottom panel of the Inspector. + preambleText = sprintf('RECORDED BY Psychtoolbox demo %s session name: %s', mfilename, edfFile); + Eyelink('Command', 'add_file_preamble_text "%s"', preambleText); + + + %% STEP 2: SELECT AVAILABLE SAMPLE/EVENT DATA + % See EyeLinkProgrammers Guide manual > Useful EyeLink Commands > File Data Control & Link Data Control + + % Select which events are saved in the EDF file. Include everything just in case + Eyelink('Command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); + % Select which events are available online for gaze-contingent experiments. Include everything just in case + Eyelink('Command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON,FIXUPDATE,INPUT'); + % Select which sample data is saved in EDF file or available online. Include everything just in case + if ELsoftwareVersion > 3 % Check tracker version and include 'HTARGET' to save head target sticker data for supported eye trackers + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,HTARGET,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,HTARGET,STATUS,INPUT'); + else + Eyelink('Command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,RAW,AREA,GAZERES,BUTTON,STATUS,INPUT'); + Eyelink('Command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); + end + + + %% STEP 3: OPEN GRAPHICS WINDOW IN STEREO MODE + + % Open experiment graphics + if isempty(screenNumber) + screenNumber = max(Screen('Screens')); + end + + if stereoMode == 10 + % Yes. Do we have at least two separate displays for both views? + if length(Screen('Screens')) < 2 + error('Sorry, for stereoMode 10 you''ll need at least 2 separate display screens in non-mirrored mode.'); + end + if ~IsWin % Assign left-eye view (the master window) to main display: + screenNumber = 0; + else + screenNumber = 1; + end + end + + [window, ~] = PsychImaging('OpenWindow', screenNumber, GrayIndex(screenNumber), [], [], [], stereoMode); + + if stereoMode == 10 + if IsWin % Assign right-eye view (the slave window) to secondary display: + slaveScreen = 2; + else + slaveScreen = 1; + end + Screen('PsychImaging', slaveScreen, [128 128 128], [], [], [], stereoMode); + end + Screen('Flip', window); + + % Get max color value for rescaling to RGB for Host PC & Data Viewer integration + colorMaxVal = Screen('ColorRange', window); + % Return width and height of the graphics window/screen in pixels + [width, height] = Screen('WindowSize', window); + + + %% STEP 4: SET CALIBRATION SCREEN COLOURS; PROVIDE WINDOW SIZE TO EYELINK HOST & DATAVIEWER; SET CALIBRATION PARAMETERS; CALIBRATE + + % Provide EyeLink with some defaults, which are returned in the structure "el". + el = EyelinkInitDefaults(window); + % set calibration/validation/drift-check(or drift-correct) size as well as background and target colors. + % It is important that this background colour is similar to that of the stimuli to prevent large luminance-based + % pupil size changes (which can cause a drift in the eye movement data) + el.calibrationtargetsize = 3;% Outer target size as percentage of the screen + el.calibrationtargetwidth = 0.7;% Inner target size as percentage of the screen + el.backgroundcolour = repmat(GrayIndex(screenNumber),1,3); + el.calibrationtargetcolour = repmat(BlackIndex(screenNumber),1,3); + % set "Camera Setup" instructions text colour so it is different from background colour + el.msgfontcolour = repmat(BlackIndex(screenNumber),1,3); + + % Initialize PsychSound for calibration/validation audio feedback + % EyeLink Toolbox now supports PsychPortAudio integration and interop + % with legacy Snd() wrapping. Below we open the default audio device in + % output mode as master, create a slave device, and pass the device + % handle to el.ppa_pahandle. + % el.ppa_handle supports passing either standard mode handle, or as + % below one opened as a slave device. When el.ppa_handle is empty, for + % legacy support EyelinkUpdateDefaults() will open the default device + % and use that with Snd() interop, and close the device handle when + % calling Eyelink('Shutdown') at the end of the script. + InitializePsychSound(); + pamaster = PsychPortAudio('Open', [], 8+1); + PsychPortAudio('Start', pamaster); + pahandle = PsychPortAudio('OpenSlave', pamaster, 1); + el.ppa_pahandle = pahandle; + + % You must call this function to apply the changes made to the el structure above + EyelinkUpdateDefaults(el); + + % Set display coordinates for EyeLink data by entering left, top, right and bottom coordinates in screen pixels + Eyelink('Command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Write DISPLAY_COORDS message to EDF file: sets display coordinates in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Pre-trial Message Commands + Eyelink('Message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, width-1, height-1); + % Set number of calibration/validation dots and spread: horizontal-only(H) or horizontal-vertical(HV) as H3, HV3, HV5, HV9 or HV13 + Eyelink('Command', 'calibration_type = HV9'); % horizontal-vertical 9-points + % Allow a supported EyeLink Host PC button box to accept calibration or drift-check/correction targets via button 5 + Eyelink('Command', 'button_function 5 "accept_target_fixation"'); + % Hide mouse cursor + HideCursor(window); + % Hide mouse cursor of a secondary monitor + if stereoMode == 10 + HideCursor(slaveScreen); + end + % Suppress keypress output to command window. + ListenChar(-1); + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Put EyeLink Host PC in Camera Setup mode for participant setup/calibration + EyelinkDoTrackerSetup(el); + + + %% STEP 5: TRIAL LOOP. + + spaceBar = KbName('space');% Identify keyboard key code for spacebar to end each trial later on + imgList = {'img1Left.jpg' 'img1Right.jpg'; 'img2Left.jpg' 'img2Right.jpg'};% Provide image list for 2 trials + for i = 1:size(imgList,1) + + % STEP 5.1: START TRIAL; SHOW TRIAL INFO ON HOST PC; SHOW BACKDROP IMAGE AND/OR DRAW FEEDBACK GRAPHICS ON HOST PC; DRIFT-CHECK/CORRECTION + + % Write TRIALID message to EDF file: marks the start of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIALID %d', i); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + % Supply the trial number as a line of text on Host PC screen + Eyelink('Command', 'record_status_message "TRIAL %d/%d"', i, length(imgList)); + + % Get info from left image to use for Host PC + imgNameLeft = char(imgList(i,1)); % Get left image file name for current trial + imgNameRight = char(imgList(i,2)); % Get right image file name + imgInfo = imfinfo(imgNameLeft); % Get left image file info + + % Draw graphics on the EyeLink Host PC display. See COMMANDS.INI in the Host PC's exe folder for a list of commands + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode before drawing Host PC graphics and before recording + Eyelink('Command', 'clear_screen 0'); % Clear Host PC display from any previus drawing + % Optional: Send an image to the Host PC to be displayed as the backdrop image over which + % the gaze-cursor is overlayed during trial recordings. + % See Eyelink('ImageTransfer?') for information about supported syntax and compatible image formats. + % Below, we use the new option to pass image data from imread() as the imageArray parameter, which + % enables the use of many image formats. + % [status] = Eyelink('ImageTransfer', imageArray, xs, ys, width, height, xd, yd, options); + % xs, ys: top-left corner of the region to be transferred within the source image + % width, height: size of region to be transferred within the source image (note, values of 0 will include the entire width/height) + % xd, yd: location (top-left) where image region to be transferred will be presented on the Host PC + % This image transfer function works for non-resized image presentation only. If you need to resize images and use this function please resize + % the original image files beforehand + imgData = imread(imgNameLeft); % Get image file data + transferStatus = Eyelink('ImageTransfer', imgData, 0, 0, 0, 0, round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2)); + if dummymode == 0 && transferStatus ~= 0 % If connected to EyeLink and image transfer fails + fprintf('Image transfer Failed\n'); % Print some text in Matlab's Command Window + end + + % Optional: draw feedback box on Host PC interface instead of (or on top of) backdrop image. + % See section 25.7 'Drawing Commands' in the EyeLink Programmers Guide manual + Eyelink('Command', 'draw_box %d %d %d %d 15', round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2)); + + % Perform a drift check/correction. If using an EyeLink I or II a drift correction is performed by default + % Optionally provide x y target location, otherwise target is presented on screen centre + EyelinkDoDriftCorrection(el, round(width/2), round(height/2)); + + %STEP 5.2: START RECORDING + + % Put tracker in idle/offline mode before recording. Eyelink('SetOfflineMode') is recommended + % however if Eyelink('Command', 'set_idle_mode') is used allow 50ms before recording as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode before recording + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode');% Put tracker in idle/offline mode before recording + Eyelink('StartRecording'); % Start tracker recording + WaitSecs(0.1); % Allow some time to record a few samples before presenting first stimulus + + % STEP 5.3: PRESENT STIMULUS; CREATE DATAVIEWER BACKDROP AND INTEREST AREA + + % Present initial trial image + imgTexture = zeros(1,2); % Preallocate variable + Screen('FillRect', window, el.backgroundcolour);% Prepare grey background on backbuffer + for itScr = 0:1 % iterate through left/right eye windows + Screen('SelectStereoDrawBuffer', window, itScr); % select left or right eye window + if itScr == 0 + % imgData = imread(imgData); % Read left eye image done above, don't need to repeat + elseif itScr == 1 + imgData = imread(imgNameRight); % Read image from file + end + imgTexture(itScr+1) = Screen('MakeTexture',window, imgData); % Convert image file to texture + Screen('DrawTexture', window, imgTexture(itScr+1)); % Prepare image texture on backbuffer + Screen('TextSize', window, 30); % Specify text size + Screen('DrawText', window, 'Press space bar to end trial', 5, height-35, 0); % Prepare text on backbuffer + end + [~, RtStart] = Screen('Flip', window); % Present stimulus + % Write message to EDF file to mark the start time of stimulus presentation. + Eyelink('Message', 'STIM_ONSET'); + % Write !V IMGLOAD message to EDF file: provides instructions for DataViewer so it will show trial stimulus as backdrop + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Image Commands + Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', imgNameLeft, width/2, height/2); + % Write !V IAREA message to EDF file: creates interest area around image in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Interest Area Commands + Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 1, round(width/2-imgInfo.Width/2), round(height/2-imgInfo.Height/2), round(width/2+imgInfo.Width/2), round(height/2+imgInfo.Height/2),'IMAGE_IA'); + + % STEP 5.4: WAIT FOR KEYPRESS; SHOW BLANK SCREEN; STOP RECORDING + + while 1 % loop until error or space bar press + % Check that eye tracker is still recording. Otherwise close and transfer copy of EDF file to Display PC + err = Eyelink('CheckRecording'); + if(err ~= 0) + fprintf('EyeLink Recording stopped!\n'); + % Transfer a copy of the EDF file to Display PC + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('CloseFile'); % Close EDF file on Host PC + Eyelink('Command', 'clear_screen 0'); % Clear trial image on Host PC at the end of the experiment + WaitSecs(0.1); % Allow some time for screen drawing + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below + cleanup; % Abort experiment (see cleanup function below) + return + end + % End trial if spacebar is pressed + [~, RtEnd, keyCode] = KbCheck; + if keyCode(spaceBar) + % Write message to EDF file to mark the spacebar press time + Eyelink('Message', 'KEY_PRESSED'); + reactionTime = round((RtEnd-RtStart)*1000); % Calculate RT from stimulus onset + break; + end + end % End of while loop + + % Draw blank screen at end of trial + for itScr = 0:1 + Screen('SelectStereoDrawBuffer', window, itScr); + Screen('FillRect', window, el.backgroundcolour); % Prepare grey background on backbuffer +% Screen('DrawTexture', window, backgroundTexture(itScr+1)); % Prepare background texture on backbuffer + end + Screen('Flip', window); % Present blank screen + % Write message to EDF file to mark time when blank screen is presented + Eyelink('Message', 'BLANK_SCREEN'); + % Write !V CLEAR message to EDF file: creates blank backdrop for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Simple Drawing + Eyelink('Message', '!V CLEAR %d %d %d', round(el.backgroundcolour(1)/colorMaxVal*255), round(el.backgroundcolour(2)/colorMaxVal*255), round(el.backgroundcolour(3)/colorMaxVal*255)); + + % Stop recording eye movements at the end of each trial + WaitSecs(0.1); % Add 100 msec of data to catch final events before stopping + Eyelink('StopRecording'); % Stop tracker recording + WaitSecs(0.001); % Allow some time for recording to stop + + % STEP 5.5: CREATE VARIABLES FOR DATAVIEWER; END TRIAL + + % Write !V TRIAL_VAR messages to EDF file: creates trial variables in DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Trial Message Commands + Eyelink('Message', '!V TRIAL_VAR iteration %d', i); % Trial iteration + Eyelink('Message', '!V TRIAL_VAR leftImage %s', imgNameLeft); % Image name + Eyelink('Message', '!V TRIAL_VAR rightImage %s', imgNameRight); % Image name + WaitSecs(0.001); % Allow some time between messages. Some messages can be lost if too many are written at the same time + Eyelink('Message', '!V TRIAL_VAR rt %d', reactionTime); % Reaction time + % Write TRIAL_RESULT message to EDF file: marks the end of a trial for DataViewer + % See DataViewer manual section: Protocol for EyeLink Data to Viewer Integration > Defining the Start and End of a Trial + Eyelink('Message', 'TRIAL_RESULT 0'); + WaitSecs(0.01); % Allow some time before ending the trial + + % Clear Screen() textures that were initialized for each trial iteration + for itScr = 0:1 + % Screen('Close', backgroundTexture(itScr+1)); + Screen('Close', imgTexture(itScr+1)); + end + end % End trial loop + + + %% STEP 6: CLOSE EDF FILE. TRANSFER EDF COPY TO DISPLAY PC. CLOSE EYELINK CONNECTION. FINISH UP + + % Put tracker in idle/offline mode before closing file. Eyelink('SetOfflineMode') is recommended. + % However if Eyelink('Command', 'set_idle_mode') is used, allow 50ms before closing the file as shown in the commented code: + % Eyelink('Command', 'set_idle_mode');% Put tracker in idle/offline mode + % WaitSecs(0.05); % Allow some time for transition + Eyelink('SetOfflineMode'); % Put tracker in idle/offline mode + Eyelink('Command', 'clear_screen 0'); % Clear Host PC backdrop graphics at the end of the experiment + WaitSecs(0.5); % Allow some time before closing and transferring file + Eyelink('CloseFile'); % Close EDF file on Host PC + % Transfer a copy of the EDF file to Display PC + transferFile; % See transferFile function below +catch % If syntax error is detected + cleanup; + % Print error message and line number in Matlab's Command Window + psychrethrow(psychlasterror); +end +PsychPortAudio('Close', pahandle); +PsychPortAudio('Close', pamaster); + + +% Cleanup function used throughout the script above + function cleanup + sca; % PTB's wrapper for Screen('CloseAll') & related cleanup, e.g. ShowCursor + Eyelink('Shutdown'); % Close EyeLink connection + ListenChar(0); % Restore keyboard output to Matlab + if ~IsOctave; commandwindow; end % Bring Command Window to front + end + +% Function for transferring copy of EDF file to the experiment folder on Display PC. +% Allows for optional destination path which is different from experiment folder + function transferFile + try + if dummymode ==0 % If connected to EyeLink + % Show 'Receiving data file...' text until file transfer is complete + Screen('FillRect', window, el.backgroundcolour); % Prepare background on backbuffer + Screen('DrawText', window, 'Receiving data file...', 5, height-35, 0); % Prepare text + Screen('Flip', window); % Present text + fprintf('Receiving data file ''%s.edf''\n', edfFile); % Print some text in Matlab's Command Window + + % Transfer EDF file to Host PC + % [status =] Eyelink('ReceiveFile',['src'], ['dest'], ['dest_is_path']) + status = Eyelink('ReceiveFile'); + + % Check if EDF file has been transferred successfully and print file size in Matlab's Command Window + if status > 0 + fprintf('EDF file size: %.1f KB\n', status/1024); % Divide file size by 1024 to convert bytes to KB + end + % Print transferred EDF file path in Matlab's Command Window + fprintf('Data file ''%s.edf'' can be found in ''%s''\n', edfFile, pwd); + else + fprintf('No EDF file saved in Dummy mode\n'); + end + cleanup; + catch % Catch a file-transfer error and print some text in Matlab's Command Window + fprintf('Problem receiving data file ''%s''\n', edfFile); + cleanup; + psychrethrow(psychlasterror); + end + end +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/img1Left.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/img1Left.jpg new file mode 100644 index 0000000000..0d49132183 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/img1Left.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/img1Right.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/img1Right.jpg new file mode 100644 index 0000000000..8d954e5879 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/img1Right.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/img2Left.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/img2Left.jpg new file mode 100644 index 0000000000..0d49132183 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/img2Left.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/img2Right.jpg b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/img2Right.jpg new file mode 100644 index 0000000000..8d954e5879 Binary files /dev/null and b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/SR-ResearchDemos/StereoPicture/img2Right.jpg differ diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/Contents.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/Contents.m new file mode 100644 index 0000000000..b98e522058 --- /dev/null +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/Contents.m @@ -0,0 +1,5 @@ +% EyelinkToolbox:EyelinkOneLiners +% function wrappers for use with the eyelinktoolbox +% +% See also SR-RESEARCHDEMOS +% diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/EyelinkDummyModeDlg.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/EyelinkDummyModeDlg.m index 02cb48632b..306416713c 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/EyelinkDummyModeDlg.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/EyelinkDummyModeDlg.m @@ -7,7 +7,7 @@ % 280606 fwc changed name for OSX version % 10-04-09 mk Made portable to old Matlabs. De-Uglified. -if exist('questdlg') %#ok +if exist('questdlg') %#ok drawnow; ButtonName=questdlg('Run in dummy mode?', ... 'Eyelink not connected', ... diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/contents.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/contents.m deleted file mode 100644 index 3885c5746f..0000000000 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/contents.m +++ /dev/null @@ -1,2 +0,0 @@ -% EyelinkToolbox:EyelinkBasic -% essential functions for use with the eyelinktoolbox diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/getmodestrs.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/getmodestrs.m index 08b164fdaa..4104fc30b7 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/getmodestrs.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkOneLiners/getmodestrs.m @@ -1,50 +1,50 @@ -function strs=getmodestrs(el) -strs={}; -mode = Eyelink('CurrentMode'); -switch mode - case -1 - strs{end+1}='disconnected'; - case 0 - strs{end+1}='unknown'; - otherwise - if bitand(mode,el.IN_DISCONNECT_MODE) - strs{end+1}='IN_DISCONNECT_MODE'; - end - - if bitand(mode,el.IN_UNKNOWN_MODE) - strs{end+1}='IN_UNKNOWN_MODE'; - end - - if bitand(mode,el.IN_IDLE_MODE) - strs{end+1}='IN_OFFLINE_MODE'; - end - - if bitand(mode,el.IN_SETUP_MODE) - strs{end+1}='IN_SETUP_OR_CAL_VAL_DCORR_MODE'; - end - - if bitand(mode,el.IN_RECORD_MODE) - strs{end+1}='IN_RECORD_MODE'; - end - - if bitand(mode,el.IN_TARGET_MODE) - strs{end+1}='IN_FIXATION_TARGETS_NEEDED_MODE'; - end - - if bitand(mode,el.IN_DRIFTCORR_MODE) - strs{end+1}='IN_DRIFTCORR_MODE'; - end - - if bitand(mode,el.IN_USER_MENU) - strs{end+1}='IN_USER_MENU_MODE'; - end - - if bitand(mode,el.IN_PLAYBACK_MODE) - strs{end+1}='IN_PLAYBACK_MODE'; - end - - if bitand(mode,el.IN_IMAGE_MODE) - strs{end+1}='IN_IMAGE_MODE'; - end -end +function strs=getmodestrs(el) +strs={}; +mode = Eyelink('CurrentMode'); +switch mode + case -1 + strs{end+1}='disconnected'; + case 0 + strs{end+1}='unknown'; + otherwise + if bitand(mode,el.IN_DISCONNECT_MODE) + strs{end+1}='IN_DISCONNECT_MODE'; + end + + if bitand(mode,el.IN_UNKNOWN_MODE) + strs{end+1}='IN_UNKNOWN_MODE'; + end + + if bitand(mode,el.IN_IDLE_MODE) + strs{end+1}='IN_OFFLINE_MODE'; + end + + if bitand(mode,el.IN_SETUP_MODE) + strs{end+1}='IN_SETUP_OR_CAL_VAL_DCORR_MODE'; + end + + if bitand(mode,el.IN_RECORD_MODE) + strs{end+1}='IN_RECORD_MODE'; + end + + if bitand(mode,el.IN_TARGET_MODE) + strs{end+1}='IN_FIXATION_TARGETS_NEEDED_MODE'; + end + + if bitand(mode,el.IN_DRIFTCORR_MODE) + strs{end+1}='IN_DRIFTCORR_MODE'; + end + + if bitand(mode,el.IN_USER_MENU) + strs{end+1}='IN_USER_MENU_MODE'; + end + + if bitand(mode,el.IN_PLAYBACK_MODE) + strs{end+1}='IN_PLAYBACK_MODE'; + end + + if bitand(mode,el.IN_IMAGE_MODE) + strs{end+1}='IN_IMAGE_MODE'; + end +end end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkTests/contents.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkTests/Contents.m similarity index 73% rename from Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkTests/contents.m rename to Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkTests/Contents.m index b7acb898e3..8f9bf7a9f1 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkTests/contents.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkTests/Contents.m @@ -1,3 +1,5 @@ % EyelinkToolbox:EyelinkTests % Non-exhaustive tests of eyelink toolbox functions % +% See also SR-RESEARCHDEMOS +% diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkGetTrackerImageDemo.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkTests/EyelinkGetTrackerImageDemo.m similarity index 54% rename from Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkGetTrackerImageDemo.m rename to Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkTests/EyelinkGetTrackerImageDemo.m index af66a52d49..8b97653d39 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkDemos/EyelinkShortDemos/EyelinkGetTrackerImageDemo.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/EyelinkTests/EyelinkGetTrackerImageDemo.m @@ -1,61 +1,82 @@ -function EyelinkGetTrackerImageDemo -% This shows you how to register a callback m-file that can display the tracker's eye image in PTB - -PsychDefaultSetup(1); - -try - % Disable key output to Matlab window: - ListenChar(-1); - - % Open a window for eye image display: - w = Screen('OpenWindow', max(Screen('Screens')), [255 255 0], [0 0 800 600]); - - % Initialize 'el' eyelink struct with proper defaults for output to - % window 'w': - el=EyelinkInitDefaults(w); - - % Initialize Eyelink connection (real or dummy). The flag '1' requests - % use of callback function and eye camera image display: - if ~EyelinkInit([], 1) - fprintf('Eyelink Init aborted.\n'); - cleanup; - return; - end - - % This would display additional debug output, if commented in: - % Eyelink('Verbosity',10); - - % Run synthetic test suite for the fun of it, if commented in: - Eyelink('TestSuite'); - - % Perform tracker setup: The flag 1 requests interactive setup with - % video display: - result = Eyelink('StartSetup',1); - - % Perform drift correction: The special flags 1,1,1 request - % interactive correction with video display: - % You have to hit esc before return. - result = Eyelink('DriftCorrStart',30,30,1,1,1); - - % Done. -catch - % In case of error, be tidy: - cleanup; -end - -% Shutdown everything at regular end: -cleanup; - -end - -% Cleanup routine: -function cleanup - % Shutdown Eyelink: - Eyelink('Shutdown'); - - % Close window: - sca; - - % Restore keyboard output to Matlab: - ListenChar(0); -end +function EyelinkGetTrackerImageDemo(sndinit) +% EyelinkGetTrackerImageDemo([sndinit = 0]) +% This shows you how to register a callback m-file that can display the +% tracker's eye image in PTB. +% +% sndinit = 0: No sound init / legacy behavior. +% 1: EyelinkUpdateDefaults for default init. +% 2: Pass in our own PsychPortAudio sound handle for optimal method. +% + +if nargin < 1 || isempty(sndinit) + sndinit = 0; +end + +PsychDefaultSetup(1); + +try + % Disable key output to Matlab window: + ListenChar(-1); + + % Open a window for eye image display: + w = Screen('OpenWindow', max(Screen('Screens')), [255 255 0], [0 0 800 600]); + + % Initialize 'el' eyelink struct with proper defaults for output to + % window 'w': + el = EyelinkInitDefaults(w); + + % Initialize Eyelink connection (real or dummy). The flag '1' requests + % use of callback function and eye camera image display: + if ~EyelinkInit([], 1) + fprintf('Eyelink Init aborted.\n'); + cleanup; + return; + end + + if sndinit == 2 + el.ppa_pahandle = PsychPortAudio('Open', [], 1, 0, [], 2); + end + + % This would display additional debug output, if commented in: + % Eyelink('Verbosity',10); + + if sndinit > 0 + EyelinkUpdateDefaults(el); + end + + % Run synthetic test suite for the fun of it, if commented in: + Eyelink('TestSuite'); + + % Perform tracker setup - interactive setup with video display: + result = EyelinkDoTrackerSetup(el); + + % Perform drift correction: The special flags 1,1 request interactive + % correction with video display: You have to hit esc before return. + result = EyelinkDoDriftCorrection(el, 30, 30, 1, 1); + + % Done. +catch + % In case of error, be tidy: + cleanup; +end + +% Shutdown everything at regular end: +cleanup; + +if sndinit == 2 + PsychPortAudio('Close', el.ppa_pahandle); +end + +end + +% Cleanup routine: +function cleanup + % Shutdown Eyelink: + Eyelink('Shutdown'); + + % Close window: + sca; + + % Restore keyboard output to Matlab: + ListenChar(0); +end diff --git a/Psychtoolbox/PsychHardware/EyelinkToolbox/changes.m b/Psychtoolbox/PsychHardware/EyelinkToolbox/eyelinktoolboxchangelog.m similarity index 86% rename from Psychtoolbox/PsychHardware/EyelinkToolbox/changes.m rename to Psychtoolbox/PsychHardware/EyelinkToolbox/eyelinktoolboxchangelog.m index 5e4e5b7afb..0d3f9d1675 100644 --- a/Psychtoolbox/PsychHardware/EyelinkToolbox/changes.m +++ b/Psychtoolbox/PsychHardware/EyelinkToolbox/eyelinktoolboxchangelog.m @@ -1,6 +1,25 @@ % EyelinkToolbox. % History and list of changes % +% February 2024 +% Various improvements and fixes all over the place +% Updates to EyelinkBasic +% Support for +% - Static calibration target images from file +% - Animated calibration targets from media file +% - Stereomodes supported by Screen() +% Updates to EyelinkDemos/SR-ResearchDemos/ +% - Deprecated old examples +% - Added improved examples: +% - GazeContingent/FixWindowBufferedSamples +% - GazeContingent/FixWindowFastSamples +% - GazeContingent/GCBufferedEvents +% - GazeContingent/GCFastSamples +% - MRI_BlockRecord +% - PursuitTarget +% - SimplePicture +% - SimpleVideo +% % July 2010 % Enabled use of callbacks by default % Revampled demos, made to work with new callback version