This is example 4. This example demonstrates the following:
-
Using the threaded interface to monitor the eye-tracker
-
Opening/closing the eye-tracker
-
Printing eye-tracker mode-change events
This example and example 3 are two versions of the same program; this version uses the threaded interface, whereas example 3 uses the waitable interface. For an introduction please see The threaded v.s. waitable interface in the General procedure page.
Source code
Source file: example4.c
ClientData
We’re defining a client data struct to be passed all event handling functions. In some of the other example programs extra attributes are added; check the source.
typedef struct _ClientData {
EyeVecTrackerMode trackermode; // Current tracker mode.
int showmodechanges; // If set, show tracker mode changes.
} ClientData;
Initialization and clean-up
The initialization part (adapted from
example 1) now includes a function call
eyevec_create_thread(). This is called ahead of eyevec_initialize().
To clean up we call eyevec_destroy_thread(). This is called following
eyevec_cleanup().
int main(void)
{
// Create an idle EyeVec object.
EyeVec* eyevec = eyevec_create();
// Setup a client data struct to be passed to all event handling functions.
ClientData clientdata; (1)
clientdata.trackermode = EYEVEC_TRACKER_MODE_OFF;
clientdata.showmodechanges = 1;
// Create event queue monitoring thread. In this example we're providing
// a callback function for mode changes only.
EyeVecResult err = eyevec_create_thread(eyevec, (2)
onModeChange,
NULL, // onDisplayData,
NULL, // onEyeData,
NULL, // onBlinkEvent,
NULL, // onSaccadeEvent,
NULL, // onFixationEvent,
NULL, // onTestItemEvent,
&clientdata);
printError("eyevec_create_thread()", err);
if (err) return EXIT_FAILURE;
// Setup communication with the eyevec-control/server application.
err = eyevec_initialize(eyevec, true);
printError("eyevec_initialize()", err);
if (err) return EXIT_FAILURE;
.
.
printf("Type q to quit, ? for help.\n");
int ret = inputLoop(eyevec, &clientdata); (3)
.
.
// Terminate communication with the eyevec-control/server application.
err = eyevec_cleanup(eyevec);
printError("eyevec_cleanup()", err);
// Destroy event queue monitoring thread.
err = eyevec_destroy_thread(eyevec); (4)
printError("eyevec_destroy_thread()", err);
// Destroy the EyeVec object.
eyevec_destroy(eyevec);
return (ret >= 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
| 1 | Setup client data to be passed to event handling functions. |
| 2 | Create event queue monitoring thread. |
| 3 | Pass EyeVec object and client data to inputLoop(). |
| 4 | Destroy the event queue monitoring thread. |
inputLoop()
The input loop is basically the same as in
example 2 except that we call
the processInput() function with the EyeVec object and client data struct.
static int inputLoop(EyeVec* eyevec, ClientData* clientdata)
{
#if defined(EYEVEC_PLATFORM_WINDOWS)
HANDLE console = GetStdHandle(STD_INPUT_HANDLE);
#else
int console = fileno(stdin);
#endif
int ret = 0;
while (true) {
ret = waitForInput1(console, -1);
if (quit_by_signal) break; // Interrupted by user, quit.
if (ret == 0) continue; // Timeout, keep going.
if (ret < 0) break; // Error.
// Get user input (single character).
int ch = 0;
if (kbhit()) {
ch = getch();
if (ch < 0) break;
}
if (ch == '\n') printf("\n");
if (ch == 0 || !isprint(ch)) continue; // Ignore non-printables.
printf("[%c]\n", ch);
ret = processInput(eyevec, clientdata, ch); (1)
if (ret != 0) break;
}
return (ret >= 0) ? 0 : ret;
}
| 1 | Call processInput() with EyeVec object and client data. |
onModeChange()
The onModeChange() function is called by the event queue monitoring thread
when a mode-change event is received. In this case we’ll just print a
mode-change message. Apart from a cast (see below) this function is the same
as in example 3.
| This function is called from the event queue monitoring thread which means that interacting with the application might require the use of a synchronization primitive such as a mutex. |
static int onModeChange(EyeVec* eyevec, int64_t eventtime,
const EyeVecModeChangeEventData* modedata, void* cldata) (1)
{
(void)eyevec;
ClientData* clientdata = (ClientData*)cldata; (1)
if (clientdata == NULL) return 0;
clientdata->trackermode = modedata->newmode;
if (clientdata->showmodechanges) {
printf("onModeChange:\n");
printf(" eventtime: %" PRId64 "\n",
eventtime);
printf(" oldmode: %s\n",
eyevec_tracker_mode_string(modedata->oldmode));
printf(" newmode: %s\n",
eyevec_tracker_mode_string(modedata->newmode));
}
// Handle change from oldmode to newmode.
// .
// .
// .
return 0;
}
| 1 | Client data is passed as a void pointer so requires cast to ClientData. |
processInput()
The following single character commands are implemented here:
q: quit
o: open/close device
m: toggle show mode-change events
When you press o then eyevec_open() or eyevec_close() will be called
depending on whether the device is currently open or not. Doing so should
result in onModeChange() function being called on each mode-change. No
change compared to example 3.
static int processInput(EyeVec* eyevec, ClientData* clientdata, int ch)
{
.
.
.
else if (ch == 'o') {
if (eyevec_is_open(eyevec)) { (1)
err = eyevec_close(eyevec); (2)
printError("eyevec_close()", err);
}
else {
err = eyevec_open(eyevec); (3)
printError("eyevec_open()", err);
}
}
else if (ch == 'm') {
clientdata->showmodechanges = !clientdata->showmodechanges;
printf("showmodechanges=%d\n", clientdata->showmodechanges);
}
.
.
.
}
| 1 | Check if eye-tracker is currently open. |
| 2 | Close eye-tracker. |
| 3 | Open eye-tracker. |
Running
This example uses the utility functions from the utils directory. Make sure
to build those first before compiling this example program.
After a succesful build run the program:
-
Press
oto calleyevec_open(). Eye-tracker should go from off mode to idle mode. -
Press
oto calleyevec_close(). Eye-tracker should go from idle mode to off mode. -
Press
qto 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: 1750411073779373
oldmode: TRACKER_MODE_OFF
newmode: TRACKER_MODE_IDLE
eyevec_open(): OK
[o]
onModeChange:
eventtime: 1750411075119864
oldmode: TRACKER_MODE_IDLE
newmode: TRACKER_MODE_OFF
eyevec_close(): OK
[q]
eyevec_cleanup(): OK
eyevec_destroy_thread(): OK
As you can see on opening the device the program shows the eye-tracker mode
changing from TRACKER_MODE_OFF to TRACKER_MODE_IDLE. On closure it
goes from from TRACKER_MODE_IDLE to TRACKER_MODE_OFF. So in the
state diagram we’re going from the gray circle to
the blue circle on open, and from the from the blue circle back to the gray
circle on close.