This is example 16. This example demonstrates the following:

  1. Processing online fixation events

This example is based on example 10. This example adds an onFixationEvent() event handler function to print fixation start, end and update events.

Source code

Source file: example16.c

Initialization

In the eyevec_create_thread() function in the initialization part we provide a fixation event callback function.

int main(void)
{
    .
    .

    EyeVecResult err = eyevec_create_thread(eyevec,
        onModeChange,
        NULL,   // onDisplayData,
        NULL,   // onEyeData,
        NULL,   // onBlinkEvent,
        NULL,   // onSaccadeEvent,
        onFixationEvent, (1)
        NULL,   // onTestItemEvent,
        &clientdata);
    printError("eyevec_create_thread()", err);
    if (err) return EXIT_FAILURE;

    .
    .
1 Callback function for fixation start, end and update events.

processInput()

The following single character commands are implemented here:
q: quit
o: open/close device
u: show/hide control window
x: toggle show gui on setup mode
X: toggle show gui on data mode
i: enter idle mode
s: enter setup mode
z: enter data mode
r: start/stop recording
m: toggle show mode-change events
g: toggle gaze analysis events

When you press g then eyevec_set_gaze_events_enabled() will be called to toggle (enable or disable) sending gaze analysis events. The default is not sending them.

static int processInput(EyeVec* eyevec, ClientData* clientdata, int ch)
{
    .
    .
    .
    else if (ch == 'g') {
        uint32_t enabled = eyevec_get_gaze_events_enabled(eyevec); (1)
        if ((enabled & EYEVEC_ENABLE_SEND_WHILE_IN_RECORDING_MODE) != 0) {
            err = eyevec_set_gaze_events_enabled(eyevec,
                EYEVEC_ENABLE_SEND_WHILE_IN_RECORDING_MODE, 0); (2)
            printError("eyevec_set_gaze_events_enabled(0x1, 0)", err);
        }
        else {
            err = eyevec_set_gaze_events_enabled(eyevec,
                0, EYEVEC_ENABLE_SEND_WHILE_IN_RECORDING_MODE); (3)
            printError("eyevec_set_gaze_events_enabled(0, 0x1)", err);
        }
    }
    .
    .
    .
}
1 Check if gaze events are currently enabled.
2 Disable sending gaze events.
3 Enable sending gaze events.

If for some reason you need to receive gaze events not only when in recording mode, but also when in data mode and drift-check mode (the green circles in the eye-tracker mode state diagram), then call eyevec_set_gaze_events_enabled().

To try this add the following to processInput():

    else if (ch == 'G') {
        uint32_t enabled = eyevec_get_gaze_events_enabled(eyevec);
        if ((enabled & EYEVEC_ENABLE_SEND_WHILE_IN_DATA_MODE) != 0) {
            err = eyevec_set_gaze_events_enabled(eyevec,
                EYEVEC_ENABLE_SEND_WHILE_IN_DATA_MODE, 0);
            printError("eyevec_set_gaze_events_enabled(0x2, 0)", err);
        }
        else {
            err = eyevec_set_gaze_events_enabled(eyevec,
                0, EYEVEC_ENABLE_SEND_WHILE_IN_DATA_MODE);
            printError("eyevec_set_gaze_events_enabled(0, 0x2)", err);
        }
    }

onFixationEvent()

In the onFixationEvent() event handler function we call onFixationStart(), onFixationEnd() or onFixationUpdate() depending on the flag parameter provided to the onFixationEvent() function.

static int onFixationEvent(const EyeVec* eyevec, int64_t eventtime,
    const EyeVecFixationEventData* fixationdata, int flag, void* cldata)
{
    const ClientData* clientdata = (const ClientData*)cldata;
    if (clientdata == NULL) return 0;

    if (flag == 0)
        return onFixationStart(eyevec, eventtime, fixationdata, clientdata); (1)

    if (flag > 0)
        return onFixationEnd(eyevec, eventtime, fixationdata, clientdata); (2)

    return onFixationUpdate(eyevec, eventtime, fixationdata, clientdata); (3)
}
1 Call onFixationStart() with provided fixation data.
2 Call onFixationEnd() with provided fixation data.
3 Call onFixationUpdate() with provided fixation data.

onFixationStart()

This function prints the relevant EyeVecFixationEventData attributes (excluding variances) defined for the fixation start event.

static int onFixationStart(const EyeVec* eyevec, int64_t eventtime,
    const EyeVecFixationEventData* fixationdata, const ClientData* clientdata)
{
    (void)eyevec;

    if (clientdata->showgazeevents) {
        printf("onFixationStart:\n");
        printf("    eventtime:                  %" PRId64 "\n",
            eventtime);
        printf("    side:                       %s\n",
            side_string(fixationdata->side));
        printf("    starttime:                  %" PRId64 "\n",
            fixationdata->starttime);
        printf("    gaze_start:                 %.3f, %.3f\n",
            (double)fixationdata->gaze_start.x,
            (double)fixationdata->gaze_start.y);
        printf("    pupil_start:                %.3f\n",
            (double)fixationdata->pupil_start);
    }

    return 0;
}

onFixationEnd()

This function prints the relevant EyeVecFixationEventData attributes (excluding variances) defined for the fixation end event.

static int onFixationEnd(const EyeVec* eyevec, int64_t eventtime,
    const EyeVecFixationEventData* fixationdata, const ClientData* clientdata)
{
    (void)eyevec;

    if (clientdata->showgazeevents) {
        printf("onFixationEnd:\n");
        printf("    eventtime:                  %" PRId64 "\n",
            eventtime);
        printf("    side:                       %s\n",
            side_string(fixationdata->side));
        printf("    starttime:                  %" PRId64 "\n",
            fixationdata->starttime);
        if (fixationdata->endtime >= 0) {
            printf("    duration:                   %d\n",
                (int)(fixationdata->endtime - fixationdata->starttime));
        }
        printf("    gaze_end:                   %.3f, %.3f\n",
            (double)fixationdata->gaze_end.x,
            (double)fixationdata->gaze_end.y);
        printf("    pupil_end:                  %.3f\n",
            (double)fixationdata->pupil_end);
        printf("    gaze_avg:                   %.3f, %.3f\n",
            (double)fixationdata->gaze_avg.x,
            (double)fixationdata->gaze_avg.y);
        printf("    pupil_avg:                  %.3f\n",
            (double)fixationdata->pupil_avg);
    }

    return 0;
}

onFixationUpdate()

This function prints the relevant EyeVecFixationEventData attributes (excluding variances) defined for the fixation update event.

static int onFixationUpdate(const EyeVec* eyevec, int64_t eventtime,
    const EyeVecFixationEventData* fixationdata, const ClientData* clientdata)
{
    (void)eyevec;

    if (clientdata->showgazeevents) {
        printf("onFixationUpdate:\n");
        printf("    eventtime:                  %" PRId64 "\n",
            eventtime);
        printf("    side:                       %s\n",
            side_string(fixationdata->side));
        printf("    starttime:                  %" PRId64 "\n",
            fixationdata->starttime);
        printf("    duration:                   %d\n",
            (int)(fixationdata->endtime - fixationdata->starttime));
        printf("    gaze_end:                   %.3f, %.3f\n",
            (double)fixationdata->gaze_end.x,
            (double)fixationdata->gaze_end.y);
        printf("    pupil_end:                  %.3f\n",
            (double)fixationdata->pupil_end);
        printf("    gaze_avg:                   %.3f, %.3f\n",
            (double)fixationdata->gaze_avg.x,
            (double)fixationdata->gaze_avg.y);
        printf("    pupil_avg:                  %.3f\n",
            (double)fixationdata->pupil_avg);
    }

    return 0;
}

Running

After a succesful build run the program:

  1. Press o to call eyevec_open(). Eye-tracker should go from off mode to idle mode.

  2. Press z to call eyevec_enter_data_mode(). Eye-tracker should go from idle mode to data mode. Note, we’re skipping setup and calibration here; still make sure the eye-tracker can see your eyes.

  3. Press g to call eyevec_set_gaze_event_enabled(0, EYEVEC_ENABLE_SEND_WHILE_IN_RECORDING_MODE).

  4. Press r to call eyevec_start_recording(). Eye-tracker should go from data mode to recording mode. Fixation events should be printed.

  5. Press r to call eyevec_stop_recording(). Eye-tracker should go from recording mode to data mode. No more fixation events will be printed.

  6. Press q to quit.

Output might look like this (empty lines added for clarity):

eyevec_create_thread(): OK
eyevec_initialize(): OK
Type q to quit, ? for help.

[o]
onModeChange:
    eventtime:                  1750689363293557
eyevec_open(): OK
    oldmode:                    TRACKER_MODE_OFF
    newmode:                    TRACKER_MODE_IDLE

[z]
eyevec_enter_data_mode(): OK
onModeChange:
    eventtime:                  1750689367838031
    oldmode:                    TRACKER_MODE_IDLE
    newmode:                    TRACKER_MODE_DATA

[g]
eyevec_set_gaze_event_enabled(0, 0x1): OK

[r]
eyevec_start_recording(): OK
onModeChange:
    eventtime:                  1750689402060288
    oldmode:                    TRACKER_MODE_DATA
    newmode:                    TRACKER_MODE_RECORDING

onFixationEnd:
    eventtime:                  1750689402868187
    side:                       RIGHT
    starttime:                  1750689398107252
    duration:                   4760935
    gaze_end:                   0.077, 0.913
    pupil_end:                  4.353
    gaze_avg:                   0.087, 0.879
    pupil_avg:                  4.266
onFixationEnd:
    eventtime:                  1750689402868187
    side:                       LEFT
    starttime:                  1750689398107252
    duration:                   4760935
    gaze_end:                   0.052, 0.927
    pupil_end:                  4.483
    gaze_avg:                   0.068, 0.884
    pupil_avg:                  4.381
onFixationEnd:
    eventtime:                  1750689402868187
    side:                       MEAN
    starttime:                  1750689398107252
    duration:                   4760935
    gaze_end:                   0.064, 0.920
    pupil_end:                  4.418
    gaze_avg:                   0.077, 0.882
    pupil_avg:                  4.323
onFixationStart:
    eventtime:                  1750689402940200
    side:                       RIGHT
    starttime:                  1750689402940200
    gaze_start:                 0.356, 0.674
    pupil_start:                4.298
onFixationStart:
    eventtime:                  1750689402942200
    side:                       LEFT
    starttime:                  1750689402942200
    gaze_start:                 0.361, 0.678
    pupil_start:                4.559
onFixationStart:
    eventtime:                  1750689402942200
    side:                       MEAN
    starttime:                  1750689402942200
    gaze_start:                 0.358, 0.676
    pupil_start:                4.431
onFixationEnd:
    eventtime:                  1750689403002211
    side:                       RIGHT
    starttime:                  1750689402940200
    duration:                   62011
    gaze_end:                   0.356, 0.677
    pupil_end:                  4.317
    gaze_avg:                   0.358, 0.672
    pupil_avg:                  4.311
onFixationEnd:
    eventtime:                  1750689403002211
    side:                       LEFT
    starttime:                  1750689402942200
    duration:                   60011
    gaze_end:                   0.357, 0.679
    pupil_end:                  4.566
    gaze_avg:                   0.359, 0.676
    pupil_avg:                  4.571
onFixationEnd:
    eventtime:                  1750689403002211
    side:                       MEAN
    starttime:                  1750689402942200
    duration:                   60011
    gaze_end:                   0.356, 0.678
    pupil_end:                  4.442
    gaze_avg:                   0.358, 0.674
    pupil_avg:                  4.441
.
.
.

[q]
eyevec_cleanup(): OK
eyevec_destroy_thread(): OK