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
o
to calleyevec_open()
. Eye-tracker should go from off mode to idle mode. -
Press
o
to calleyevec_close()
. Eye-tracker should go from idle mode to off mode. -
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: 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.