Home › Forums › Software Development › Accessing gaze data stream with AutoHotkey › Reply To: Accessing gaze data stream with AutoHotkey
02/05/2016 at 22:55 #5140
Cliff
Participant
Here’s the complete MinimalGazeDataStream.ahk that works for me, with either 32-bit or 64-bit Unicode AHK. Just make sure you use the corresponding 32-bit or 64-bit Tobii.EyeX.Client.dll.
#NoEnv
#SingleInstance force
#EscapeChar ~ ; avoid default of backtick to prevent clash with tobii.com developer forums
; ----------------------------------------------------------------------------------------------------------------------
; AHK version of MinimalGazeDataStream from EyeX SDK sample
; ----------------------------------------------------------------------------------------------------------------------
; values of enum TX_RESULT from EyeXFrameworkTypes.h
TX_RESULT_UNKNOWN := 1
TX_RESULT_OK := 2
TX_RESULT_CANCELLED := 28
; values of enum TX_BEHAVIORTYPE from EyeXFrameworkTypes.h
TX_BEHAVIORTYPE_GAZEPOINTDATA := 1
TX_BEHAVIORTYPE_EYEPOSITIONDATA := 2
TX_BEHAVIORTYPE_FIXATIONDATA := 6
; values of enum TX_GAZEPOINTDATAMODE from EyeXFrameworkTypes.h
TX_GAZEPOINTDATAMODE_UNFILTERED := 1
TX_GAZEPOINTDATAMODE_LIGHTLYFILTERED := 2
; values of enum TX_EYEXCOMPONENTOVERRIDEFLAGS from EyeXClientTypes.h
TX_EYEXCOMPONENTOVERRIDEFLAG_NONE := 0
; values of enum TX_CONNECTIONSTATE from EyeXClientTypes.h
TX_CONNECTIONSTATE_CONNECTED := 1
TX_CONNECTIONSTATE_DISCONNECTED := 2
TX_CONNECTIONSTATE_TRYINGTOCONNECT := 3
TX_CONNECTIONSTATE_SERVERVERSIONTOOLOW := 4
TX_CONNECTIONSTATE_SERVERVERSIONTOOHIGH := 5
; values from EyeXClientTypes.h
TX_EMPTY_HANDLE := 0
TX_INVALID_TICKET := 0
TX_TRUE := 1
TX_FALSE := 0
TX_CLEANUPTIMEOUT_DEFAULT := 500
dllFile := A_ScriptDir . "\Tobii.EyeX.Client.dll"
InteractorId := "Twilight Sparkle"
g_hGlobalInteractorSnapshot := TX_EMPTY_HANDLE
; AutoExecute
main()
Return
InitializeGlobalInteractorSnapshot(hContext) {
Global dllFile, InteractorId, g_hGlobalInteractorSnapshot, TX_EMPTY_HANDLE, TX_RESULT_OK, TX_GAZEPOINTDATAMODE_LIGHTLYFILTERED
hInteractor := TX_EMPTY_HANDLE
VarSetCapacity(gazeParamStruct, 4, 0)
NumPut(TX_GAZEPOINTDATAMODE_LIGHTLYFILTERED, gazeParamStruct, 0, "UInt")
success := DllCall(dllFile . "\txCreateGlobalInteractorSnapshot"
, "Int", hContext
, "Str", InteractorId
, "Int*", g_hGlobalInteractorSnapshot
, "Int*", hInteractor
, "Cdecl Int") == TX_RESULT_OK
success &= DllCall(dllFile . "\txCreateGazePointDataBehavior"
, "Int", hInteractor
, "Ptr", &gazeParamStruct
, "Cdecl Int") == TX_RESULT_OK
DllCall(dllFile . "\txReleaseObject"
, "Int*", hInteractor
, "Cdecl")
return success
}
OnSnapshotCommitted(hAsyncData, param) {
Global dllFile, TX_RESULT_OK, TX_RESULT_UNKNOWN
result := TX_RESULT_UNKNOWN
success := DllCall(dllFile . "\txGetAsyncDataResultCode"
, "Int", hAsyncData
, "Int*", result
, "Cdecl Int") == TX_RESULT_OK
DebugMessage("OnSnapshotCommitted: success=" . success . ", result=" . result)
}
OnEngineConnectionStateChanged(connectionState, userParam) {
Global dllFile, g_hGlobalInteractorSnapshot, TX_RESULT_OK, TX_CONNECTIONSTATE_CONNECTED, TX_CONNECTIONSTATE_DISCONNECTED, TX_CONNECTIONSTATE_TRYINGTOCONNECT, TX_CONNECTIONSTATE_SERVERVERSIONTOOLOW, TX_CONNECTIONSTATE_SERVERVERSIONTOOHIGH
if (connectionState == TX_CONNECTIONSTATE_CONNECTED) {
success := DllCall(dllFile . "\txCommitSnapshotAsync"
, "Int", g_hGlobalInteractorSnapshot
, "Ptr", RegisterCallback("OnSnapshotCommitted", "Cdecl")
, "Int", 0
, "Cdecl Int") == TX_RESULT_OK
if (!success) {
DebugMessage("Failed to initialize the data stream.")
} else {
DebugMessage("Waiting for gaze data to start streaming...")
}
} else if (connectionState == TX_CONNECTIONSTATE_DISCONNECTED) {
DebugMessage("The connection state is now DISCONNECTED (We are disconnected from the EyeX Engine)")
} else if (connectionState == TX_CONNECTIONSTATE_TRYINGTOCONNECT) {
DebugMessage("The connection state is now TRYINGTOCONNECT (We are trying to connect to the EyeX Engine)")
} else if (connectionState == TX_CONNECTIONSTATE_SERVERVERSIONTOOLOW) {
DebugMessage("The connection state is now SERVER_VERSION_TOO_LOW: this application requires a more recent version of the EyeX Engine to run.")
} else if (connectionState == TX_CONNECTIONSTATE_SERVERVERSIONTOOHIGH) {
DebugMessage("The connection state is now SERVER_VERSION_TOO_HIGH: this application requires an older version of the EyeX Engine to run.")
}
}
OnGazeDataEvent(hGazeDataBehavior) {
Global dllFile, TX_RESULT_OK
VarSetCapacity(eventParamsStruct, 32, 0) ; 4 (Int) + 8 (Double) + 8 (Double) + 8 (Double) = 28, but compiler has padded Int to 8 as member of struct
success := DllCall(dllFile . "\txGetGazePointDataEventParams"
, "Int", hGazeDataBehavior
, "Ptr", &eventParamsStruct
, "Cdecl Int") == TX_RESULT_OK
if (success) {
;gazePointDataMode := NumGet(eventParamsStruct, 0, "Int")
timestamp := NumGet(eventParamsStruct, 8, "Double")
x := NumGet(eventParamsStruct, 16, "Double")
y := NumGet(eventParamsStruct, 24, "Double")
DebugMessage("Gaze Data: (" . x . ", " . y . ") " . timestamp . " ms")
} else {
DebugMessage("Failed to interpret gaze data event packet.")
}
}
HandleEvent(hAsyncData, userParam) {
Critical
Global dllFile, TX_EMPTY_HANDLE, TX_RESULT_OK, TX_BEHAVIORTYPE_GAZEPOINTDATA
hEvent := TX_EMPTY_HANDLE
hBehavior := TX_EMPTY_HANDLE
;Static count := 0
;DebugMessage(count++ . ": hAsyncData=" . hAsyncData . ", userParam=" . userParam)
success := DllCall(dllFile . "\txGetAsyncDataContent"
, "Int", hAsyncData
, "Int*", hEvent
, "Cdecl Int") == TX_RESULT_OK
isGazePointData := DllCall(dllFile . "\txGetEventBehavior"
, "Int", hEvent
, "Int*", hBehavior
, "Int", TX_BEHAVIORTYPE_GAZEPOINTDATA
, "Cdecl Int") == TX_RESULT_OK
if (isGazePointData) {
OnGazeDataEvent(hBehavior)
DllCall(dllFile . "\txReleaseObject"
, "Int*", hBehavior
, "Cdecl Int")
}
DllCall(dllFile . "\txReleaseObject"
, "Int*", hEvent
, "Cdecl Int")
}
main() {
Global dllFile, TX_EMPTY_HANDLE, TX_INVALID_TICKET, TX_EYEXCOMPONENTOVERRIDEFLAG_NONE, TX_RESULT_OK, TX_FALSE, TX_CLEANUPTIMEOUT_DEFAULT
hContext := TX_EMPTY_HANDLE
hConnectionStateChangedTicket := TX_INVALID_TICKET
hEventHandlerTicket := TX_INVALID_TICKET
hModule := DllCall("LoadLibrary", "Str", dllFile)
success := DllCall(dllFile . "\txInitializeEyeX"
, "Int", TX_EYEXCOMPONENTOVERRIDEFLAG_NONE
, "Int", 0
, "Int", 0
, "Int", 0
, "Int", 0
, "Cdecl Int") == TX_RESULT_OK
success &= DllCall(dllFile . "\txCreateContext"
, "Int*", hContext
, "Int", TX_FALSE
, "Cdecl Int") == TX_RESULT_OK
success &= InitializeGlobalInteractorSnapshot(hContext)
success &= DllCall(dllFile . "\txRegisterConnectionStateChangedHandler"
, "Int", hContext
, "Int*", hConnectionStateChangedTicket
, "Ptr", RegisterCallback("OnEngineConnectionStateChanged", "Cdecl", 2)
, "Int", 0
, "Cdecl Int") == TX_RESULT_OK
success &= DllCall(dllFile . "\txRegisterEventHandler"
, "Int", hContext
, "Int*", hEventHandlerTicket
, "Ptr", RegisterCallback("HandleEvent", "Cdecl", 2)
, "Int", 0
, "Cdecl Int") == TX_RESULT_OK
success &= DllCall(dllFile . "\txEnableConnection"
, "Int", hContext
, "Cdecl Int") == TX_RESULT_OK
DebugMessage("success=" . success . ", hConnectionStateChangedTicket=" . hConnectionStateChangedTicket . ", hEventHandlerTicket=" . hEventHandlerTicket)
if (success) {
DebugMessage("Initialization was successful.")
} else {
DebugMessage("Initialization failed.")
}
DebugMessage("Press any key to exit...")
Input, SingleKey, L1, {LControl}{RControl}{LAlt}{RAlt}{LShift}{RShift}{LWin}{RWin}{AppsKey}{F1}{F2}{F3}{F4}{F5}{F6}{F7}{F8}{F9}{F10}{F11}{F12}{Left}{Right}{Up}{Down}{Home}{End}{PgUp}{PgDn}{Del}{Ins}{BS}{Capslock}{Numlock}{PrintScreen}{Pause}
DebugMessage("Exiting.")
DllCall(dllFile . "\txDisableConnection"
, "Int", hContext
, "Cdecl Int")
DllCall(dllFile . "\txReleaseObject"
, "Int*", g_hGlobalInteractorSnapshot
, "Cdecl Int")
success &= DllCall(dllFile . "\txShutdownContext"
, "Int", hContext
, "Int", TX_CLEANUPTIMEOUT_DEFAULT
, "Int", TX_FALSE
, "Cdecl Int") == TX_RESULT_OK
success &= DllCall(dllFile . "\txReleaseContext"
, "Int*", hContext
, "Cdecl Int") == TX_RESULT_OK
success &= DllCall(dllFile . "\txUninitializeEyeX"
, "Cdecl Int") == TX_RESULT_OK
DllCall("FreeLibrary", "Ptr", hModule)
ExitApp
}
; From https://autohotkey.com/board/topic/59612-simple-debug-console-output/
DebugMessage(str)
{
global h_stdout
DebugConsoleInitialize() ; start console window if not yet started
str .= "~n" ; add line feed
;DllCall("WriteFile", "uint", h_Stdout, "uint", &str, "uint", StrLen(str), "uint*", BytesWritten, "uint", NULL) ; write into the console
FileAppend %str%, CONOUT$
WinSet, Bottom,, ahk_id %h_stout% ; keep console on bottom
}
DebugConsoleInitialize()
{
global h_Stdout ; Handle for console
static is_open = 0 ; toogle whether opened before
if (is_open = 1) ; yes, so don't open again
return
is_open := 1
; two calls to open, no error check (it's debug, so you know what you are doing)
DllCall("AttachConsole", int, -1, int)
DllCall("AllocConsole", int)
dllcall("SetConsoleTitle", "str","Paddy Debug Console") ; Set the name. Example. Probably could use a_scriptname here
h_Stdout := DllCall("GetStdHandle", "int", -11) ; get the handle
WinSet, Bottom,, ahk_id %h_stout% ; make sure it's on the bottom
;WinActivate,Lightroom ; Application specific; I need to make sure this application is running in the foreground. YMMV
return
}