This is example 19. This example demonstrates the following:

  1. Processing online eye samples

This example is based on example 10. This example adds an onEyeData() event handler function to print eye sample events.

Source code

Source file: example19.c

Initialization

In the eyevec_create_thread() function in the initialization part we provide an eye sample event callback function.

int main(void)
{
    .
    .

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

    .
    .
1 Callback function for eye sample 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
e: toggle eye sample events

When you press e then eyevec_set_eye_data_enabled() will be called to toggle (enable or disable) sending eye sample events. The default is not sending them.

static int processInput(EyeVec* eyevec, ClientData* clientdata, int ch)
{
    .
    .
    .
    else if (ch == 'e') {
        uint32_t enabled = eyevec_get_eye_data_enabled(eyevec); (1)
        if ((enabled & EYEVEC_SEND_IN_RECORDING_MODE) != 0) {
            err = eyevec_set_eye_data_enabled(eyevec,
                EYEVEC_SEND_IN_RECORDING_MODE, 0); (2)
            printError("eyevec_set_eye_data_enabled(0x1, 0)", err);
        }
        else {
            err = eyevec_set_eye_data_enabled(eyevec,
                0, EYEVEC_SEND_IN_RECORDING_MODE); (3)
            printError("eyevec_set_eye_data_enabled(0, 0x1)", err);
        }
    }
    .
    .
    .
}
1 Check if eye sample events are currently enabled.
2 Disable sending eye sample events.
3 Enable sending eye sample events.

If for some reason you need to receive eye sample events not only when in recording mode, but also when in drift-check mode, then call eyevec_set_eye_data_enabled() with the EYEVEC_SEND_IN_DRIFT_CHECK_MODE flag set.

To try this add the following to processInput():

    else if (ch == 'E') {
        uint32_t enabled = eyevec_get_eye_data_enabled(eyevec);
        if ((enabled & EYEVEC_SEND_IN_DRIFT_CHECK_MODE) != 0) {
            err = eyevec_set_eye_data_enabled(eyevec,
                EYEVEC_SEND_IN_DRIFT_CHECK_MODE, 0);
            printError("eyevec_set_eye_data_enabled(0x2, 0)", err);
        }
        else {
            err = eyevec_set_eye_data_enabled(eyevec,
                0, EYEVEC_SEND_IN_DRIFT_CHECK_MODE);
            printError("eyevec_set_eye_data_enabled(0, 0x2)", err);
        }
    }

If instead you simply want to receive eye data events when in data mode or above, so all green circles in the the state diagram, then in the above code use the EYEVEC_SEND_IN_DATA_MODE flag instead.

onEyeData()

This function prints all EyeVecEyeSampleEventData attributes defined for the eye sample event.

static int onEyeData(const EyeVec* eyevec, int64_t eventtime,
    const EyeVecEyeSampleEventData* eyedata, void* cldata)
{
    (void)eyevec;

    const ClientData* clientdata = (const ClientData*)cldata;
    if (clientdata == NULL) return 0;

    printf("onEyeData:\n");
    printf("    eventtime:                  %" PRId64 "\n",
        eventtime);

    if ((eyedata->side & 1) != 0 && (1)
            eyedata->eye[0].pupil.status != EYEVEC_EYE_STATUS_SKIPPED) { (1)
        // Right eye data present.
        printf("    right:\n");
        printEye(&eyedata->eye[0]); (2)
    }
    if ((eyedata->side & 2) != 0 && (3)
            eyedata->eye[1].pupil.status != EYEVEC_EYE_STATUS_SKIPPED) { (3)
        // Left eye data present.
        printf("    left:\n");
        printEye(&eyedata->eye[1]); (4)
    }

//    return -1;  // -1 means disable sending eye sample events
    return 0;   // 0 means keep going
}
1 Check if right eye data present in eyedata.
2 Print right eye data. Print functions omitted for brevity. See source file.
3 Check if left eye data present in eyedata.
4 Print left eye data.

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 e to call eyevec_set_eye_data_events_enabled(0, EYEVEC_SEND_IN_RECORDING_MODE). This enables the sending of eye sample events.

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

  5. Press r to call eyevec_stop_recording(). Eye-tracker should go from recording mode to data mode. No more eye sample 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:                  1750701837312353
    oldmode:                    TRACKER_MODE_OFF
    newmode:                    TRACKER_MODE_IDLE
eyevec_open(): OK

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

[e]
eyevec_set_eye_data_enabled(0, 0x1): OK

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

onEyeData:
    eventtime:                  1750701877149496
    right:
      pupil.status:             1
      pupil.position:           1212.227, 1187.328
      pupil.position_raw:       1212.287, 1187.392
      pupil.area:               864.613
      pupil.visibility:         1.045
      pupil.ellipse:            17.864, 17.017, 0.347
      cr.status:                1
      cr.position:              1212.684, 1194.926
      cr.position_raw:          1212.729, 1194.999
      cr.area:                  42.847
      gaze.status:              1
      gaze.position:            0.289, 0.738
      gaze.position_raw:        0.289, 0.737
      gaze.position_avg:        0.000, 0.000
      gaze.velocity:            1000.000, 0.000
      state:                    4
      pxpermm:                  7.936
    left:
      pupil.status:             1
      pupil.position:           1683.379, 1204.849
      pupil.position_raw:       1683.411, 1204.925
      pupil.area:               983.516
      pupil.visibility:         1.038
      pupil.ellipse:            18.594, 18.161, 0.013
      cr.status:                1
      cr.position:              1678.714, 1210.330
      cr.position_raw:          1678.723, 1210.412
      cr.area:                  35.693
      gaze.status:              1
      gaze.position:            0.306, 0.713
      gaze.position_raw:        0.305, 0.713
      gaze.position_avg:        0.000, 0.000
      gaze.velocity:            1000.000, 0.000
      state:                    4
      pxpermm:                  7.936
onEyeData:
    eventtime:                  1750701877151497
    right:
      pupil.status:             1
      pupil.position:           1212.187, 1187.354
      pupil.position_raw:       1212.214, 1187.442
      pupil.area:               867.258
      pupil.visibility:         1.048
      pupil.ellipse:            17.825, 17.013, 0.354
      cr.status:                1
      cr.position:              1212.653, 1194.948
      cr.position_raw:          1212.709, 1195.016
      cr.area:                  42.630
      gaze.status:              1
      gaze.position:            0.289, 0.738
      gaze.position_raw:        0.291, 0.739
      gaze.position_avg:        0.000, 0.000
      gaze.velocity:            1000.000, 0.000
      state:                    4
      pxpermm:                  7.936
    left:
      pupil.status:             1
      pupil.position:           1683.342, 1204.824
      pupil.position_raw:       1683.378, 1204.889
      pupil.area:               976.250
      pupil.visibility:         1.031
      pupil.ellipse:            18.567, 18.189, -0.014
      cr.status:                1
      cr.position:              1678.671, 1210.304
      cr.position_raw:          1678.704, 1210.396
      cr.area:                  35.696
      gaze.status:              1
      gaze.position:            0.306, 0.713
      gaze.position_raw:        0.306, 0.712
      gaze.position_avg:        0.000, 0.000
      gaze.velocity:            1000.000, 0.000
      state:                    4
      pxpermm:                  7.936
.
.
.

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