This is example 3. This example demonstrates the following:
-
Using the waitable interface to monitor the eye-tracker
-
Opening/closing the eye-tracker
-
Printing eye-tracker mode-change events
This example and example 4 are two versions of the same program; this version uses the waitable interface, whereas example 4 uses the threaded interface. For an introduction please see The threaded v.s. waitable interface in the General procedure page.
Source code
Source file: example3.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_waitable()
. This is called ahead of eyevec_initialize()
.
To clean up we call eyevec_destroy_waitable()
. 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 waitable.
EyeVecResult err = eyevec_create_waitable(eyevec); (2)
printError("eyevec_create_waitable()", 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 waitable.
err = eyevec_destroy_waitable(eyevec); (4)
printError("eyevec_destroy_waitable()", 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 waitable to watch in inputLoop() . |
3 | Pass EyeVec object and client data to inputLoop() . |
4 | Destroy the waitable. |
inputLoop()
In the input loop we watch both the eye-tracker descriptor and the console
input descriptor. Eye-tracker events are read using eyevec_retrieve_event()
and then passed to handleEvent()
for processing. The user input handling
remains the same as in example 2.
static int inputLoop(EyeVec* eyevec, ClientData* clientdata)
{
#if defined(EYEVEC_PLATFORM_WINDOWS)
HANDLE console = GetStdHandle(STD_INPUT_HANDLE);
HANDLE waitable = eyevec_get_waitable(eyevec); (1)
#else
int console = fileno(stdin);
int waitable = eyevec_get_waitable(eyevec); (1)
#endif
int ret = 0;
while (true) {
ret = waitForInput2(waitable, console, -1); (2)
if (quit_by_signal) break; // Interrupted by user, quit.
if (ret == 0) continue; // Timeout, keep going.
if (ret < 0) break; // Error.
if (ret == 1) {
// The eye-tracker waitable has become signalled. Reset it to
// indicate we've seen it.
eyevec_reset_waitable(eyevec); (3)
}
// Process eye-tracker data if any.
EyeVecEvent* event;
while ((event = eyevec_retrieve_event(eyevec)) != NULL) { (4)
handleEvent(eyevec, event, clientdata); (5)
eyevec_release_event(event); (6)
}
// Get user input (single character).
.
.
.
1 | Get the waitable descriptor. |
2 | Wait for eye-tracker data or user input; no timeout set. |
3 | Reset the waitable to prevent it staying in signalled state. |
4 | Read eye-tracker event if available. |
5 | Call handleEvent() to handle the event. |
6 | Release the eye-tracker event read, meaning it is handled. |
handleEvent()
The handleEvent()
function checks the id of the event structure passed to
it and then calls the appropriate handling funtion to process the event. For
brevity in this example we only show handling mode-change events, ignoring
anything else.
static void handleEvent(EyeVec* eyevec, EyeVecEvent* event,
ClientData* clientdata)
{
EyeVecEventId id = eyevec_event_id(event); (1)
int64_t eventtime = eyevec_event_time(event); (2)
switch (id) {
case EYEVEC_EVENT_NONE:
break;
case EYEVEC_EVENT_MODE_CHANGE:
{
const EyeVecModeChangeEventData* modedata =
eyevec_mode_event_data(event); (3)
int ret = onModeChange(eyevec, eventtime, modedata, (4)
clientdata);
(void)ret;
}
break;
.
.
.
}
}
1 | Get event id. |
2 | Get event time. |
3 | Get mode-change event data. See EyeVecModeChangeEventData. |
4 | Call onModeChange() to handle the mode-change event. |
onModeChange()
The onModeChange()
function is called by handleEvent()
when a mode-change
event is received. In this case we’ll just print a mode-change message.
static int onModeChange(EyeVec* eyevec, int64_t eventtime,
const EyeVecModeChangeEventData* modedata, ClientData* clientdata)
{
(void)eyevec;
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;
}
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.
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_waitable(): OK
eyevec_initialize(): OK
Type q to quit, ? for help.
[o]
eyevec_open(): OK
onModeChange:
eventtime: 1750410493759530
oldmode: TRACKER_MODE_OFF
newmode: TRACKER_MODE_IDLE
[o]
eyevec_close(): OK
onModeChange:
eventtime: 1750410495830530
oldmode: TRACKER_MODE_IDLE
newmode: TRACKER_MODE_OFF
[q]
eyevec_cleanup(): OK
eyevec_destroy_waitable(): 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.