From be2926499af2e372c313d965533be3a7ee0dcf4d Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Mon, 16 May 2016 11:19:10 +0200 Subject: add tobii eyex sdk --- Tobii EyeX/samples/ActivatableButtons/EyeXHost.cpp | 294 +++++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100755 Tobii EyeX/samples/ActivatableButtons/EyeXHost.cpp (limited to 'Tobii EyeX/samples/ActivatableButtons/EyeXHost.cpp') diff --git a/Tobii EyeX/samples/ActivatableButtons/EyeXHost.cpp b/Tobii EyeX/samples/ActivatableButtons/EyeXHost.cpp new file mode 100755 index 0000000..3291e54 --- /dev/null +++ b/Tobii EyeX/samples/ActivatableButtons/EyeXHost.cpp @@ -0,0 +1,294 @@ +/* + * ActivatableButtons sample: + * This is an example that demonstrates the Activatable behavior. + * It features two buttons that can be clicked by looking at the button and pressing the space bar. + * + * Copyright 2013 Tobii Technology AB. All rights reserved. + */ + +#include "stdafx.h" +#include "EyeXHost.h" +#include +#include +#include +#include + +#pragma comment (lib, "Tobii.EyeX.Client.lib") + +#if INTPTR_MAX == INT64_MAX +#define WINDOW_HANDLE_FORMAT "%lld" +#else +#define WINDOW_HANDLE_FORMAT "%d" +#endif + +EyeXHost::EyeXHost() + : _hWnd(nullptr), _statusChangedMessage(0), _focusedRegionChangedMessage(0), _regionActivatedMessage(0) +{ + // initialize the EyeX Engine client library. + txInitializeEyeX(TX_EYEXCOMPONENTOVERRIDEFLAG_NONE, nullptr, nullptr, nullptr, nullptr); + + // create a context and register event handlers. + txCreateContext(&_context, TX_FALSE); + RegisterConnectionStateChangedHandler(); + RegisterQueryHandler(); + RegisterEventHandler(); +} + +EyeXHost::~EyeXHost() +{ + if (_context != TX_EMPTY_HANDLE) + { + // shut down, then release the context. + txShutdownContext(_context, TX_CLEANUPTIMEOUT_DEFAULT, TX_FALSE); + txReleaseContext(&_context); + } +} + +void EyeXHost::Init(HWND hWnd, UINT statusChangedMessage, UINT focusedRegionChangedMessage, UINT regionActivatedMessage) +{ + _hWnd = hWnd; + _statusChangedMessage = statusChangedMessage; + _focusedRegionChangedMessage = focusedRegionChangedMessage; + _regionActivatedMessage = regionActivatedMessage; + + // connect to the engine. + if (txEnableConnection(_context) != TX_RESULT_OK) + { + PostMessage(_hWnd, _statusChangedMessage, false, 0); + } +} + +void EyeXHost::SetActivatableRegions(const std::vector& regions) +{ + std::lock_guard lock(_mutex); + + _regions.assign(regions.begin(), regions.end()); +} + +void EyeXHost::TriggerActivation() +{ + TX_HANDLE command(TX_EMPTY_HANDLE); + txCreateActionCommand(_context, &command, TX_ACTIONTYPE_ACTIVATE); + txExecuteCommandAsync(command, NULL, NULL); + txReleaseObject(&command); +} + +void EyeXHost::OnEngineConnectionStateChanged(TX_CONNECTIONSTATE connectionState) +{ + // note the use of the asynchronous PostMessage function to marshal the event to the main thread. + // (this callback function is typically invoked on a worker thread.) + switch (connectionState) + { + case TX_CONNECTIONSTATE::TX_CONNECTIONSTATE_CONNECTED: + PostMessage(_hWnd, _statusChangedMessage, true, 0); + break; + + case TX_CONNECTIONSTATE::TX_CONNECTIONSTATE_DISCONNECTED: + case TX_CONNECTIONSTATE::TX_CONNECTIONSTATE_TRYINGTOCONNECT: + case TX_CONNECTIONSTATE::TX_CONNECTIONSTATE_SERVERVERSIONTOOLOW: + case TX_CONNECTIONSTATE::TX_CONNECTIONSTATE_SERVERVERSIONTOOHIGH: + PostMessage(_hWnd, _statusChangedMessage, false, 0); + break; + + default: + break; + } +} + +bool EyeXHost::RegisterConnectionStateChangedHandler() +{ + auto connectionStateChangedTrampoline = [](TX_CONNECTIONSTATE connectionState, TX_USERPARAM userParam) + { + static_cast(userParam)->OnEngineConnectionStateChanged(connectionState); + }; + + bool success = txRegisterConnectionStateChangedHandler(_context, &_connectionStateChangedTicket, connectionStateChangedTrampoline, this) == TX_RESULT_OK; + return success; +} + +bool EyeXHost::RegisterQueryHandler() +{ + auto queryHandlerTrampoline = [](TX_CONSTHANDLE hObject, TX_USERPARAM userParam) + { + static_cast(userParam)->HandleQuery(hObject); + }; + + bool success = txRegisterQueryHandler(_context, &_queryHandlerTicket, queryHandlerTrampoline, this) == TX_RESULT_OK; + return success; +} + +bool EyeXHost::RegisterEventHandler() +{ + auto eventHandlerTrampoline = [](TX_CONSTHANDLE hObject, TX_USERPARAM userParam) + { + static_cast(userParam)->HandleEvent(hObject); + }; + + bool success = txRegisterEventHandler(_context, &_eventHandlerTicket, eventHandlerTrampoline, this) == TX_RESULT_OK; + return success; +} + +void EyeXHost::HandleQuery(TX_CONSTHANDLE hAsyncData) +{ + std::lock_guard lock(_mutex); + + // NOTE. This method will fail silently if, for example, the connection is lost before the snapshot has been committed, + // or if we run out of memory. This is by design, because there is nothing we can do to recover from these errors anyway. + + TX_HANDLE hQuery(TX_EMPTY_HANDLE); + txGetAsyncDataContent(hAsyncData, &hQuery); + + const int bufferSize = 20; + TX_CHAR stringBuffer[bufferSize]; + + // read the query bounds from the query, that is, the area on the screen that the query concerns. + // the query region is always rectangular. + TX_HANDLE hBounds(TX_EMPTY_HANDLE); + txGetQueryBounds(hQuery, &hBounds); + TX_REAL pX, pY, pWidth, pHeight; + txGetRectangularBoundsData(hBounds, &pX, &pY, &pWidth, &pHeight); + txReleaseObject(&hBounds); + Gdiplus::Rect queryBounds((INT)pX, (INT)pY, (INT)pWidth, (INT)pHeight); + + // create a new snapshot with the same window id and bounds as the query. + TX_HANDLE hSnapshot(TX_EMPTY_HANDLE); + txCreateSnapshotForQuery(hQuery, &hSnapshot); + + TX_CHAR windowIdString[bufferSize]; + sprintf_s(windowIdString, bufferSize, WINDOW_HANDLE_FORMAT, _hWnd); + + if (QueryIsForWindowId(hQuery, windowIdString)) + { + // define options for our activatable regions: no, we don't want tentative focus events. + TX_ACTIVATABLEPARAMS params = { TX_FALSE }; + + // iterate through all regions and create interactors for those that overlap with the query bounds. + for (auto region : _regions) + { + Gdiplus::Rect regionBounds((INT)region.bounds.left, (INT)region.bounds.top, + (INT)(region.bounds.right - region.bounds.left), (INT)(region.bounds.bottom - region.bounds.top)); + + if (queryBounds.IntersectsWith(regionBounds)) + { + TX_HANDLE hInteractor(TX_EMPTY_HANDLE); + + sprintf_s(stringBuffer, bufferSize, "%d", region.id); + + TX_RECT bounds; + bounds.X = region.bounds.left; + bounds.Y = region.bounds.top; + bounds.Width = region.bounds.right - region.bounds.left; + bounds.Height = region.bounds.bottom - region.bounds.top; + + txCreateRectangularInteractor(hSnapshot, &hInteractor, stringBuffer, &bounds, TX_LITERAL_ROOTID, windowIdString); + txCreateActivatableBehavior(hInteractor, ¶ms); + + txReleaseObject(&hInteractor); + } + } + } + + txCommitSnapshotAsync(hSnapshot, OnSnapshotCommitted, nullptr); + txReleaseObject(&hSnapshot); + txReleaseObject(&hQuery); +} + +void EyeXHost::HandleEvent(TX_CONSTHANDLE hAsyncData) +{ + TX_HANDLE hEvent(TX_EMPTY_HANDLE); + txGetAsyncDataContent(hAsyncData, &hEvent); + + // NOTE. Uncomment the following line of code to view the event object. The same function can be used with any interaction object. + //OutputDebugStringA(txDebugObject(hEvent)); + + // read the interactor ID from the event. + const int bufferSize = 20; + TX_CHAR stringBuffer[bufferSize]; + TX_SIZE idLength(bufferSize); + if (txGetEventInteractorId(hEvent, stringBuffer, &idLength) == TX_RESULT_OK) + { + int interactorId = atoi(stringBuffer); + + HandleActivatableEvent(hEvent, interactorId); + } + + txReleaseObject(&hEvent); +} + +void EyeXHost::HandleActivatableEvent(TX_HANDLE hEvent, int interactorId) +{ + TX_HANDLE hActivatable(TX_EMPTY_HANDLE); + if (txGetEventBehavior(hEvent, &hActivatable, TX_BEHAVIORTYPE_ACTIVATABLE) == TX_RESULT_OK) + { + TX_ACTIVATABLEEVENTTYPE eventType; + if (txGetActivatableEventType(hActivatable, &eventType) == TX_RESULT_OK) + { + if (eventType == TX_ACTIVATABLEEVENTTYPE_ACTIVATED) + { + OnActivated(hActivatable, interactorId); + } + else if (eventType == TX_ACTIVATABLEEVENTTYPE_ACTIVATIONFOCUSCHANGED) + { + OnActivationFocusChanged(hActivatable, interactorId); + } + } + + txReleaseObject(&hActivatable); + } +} + +void EyeXHost::OnActivationFocusChanged(TX_HANDLE hBehavior, int interactorId) +{ + TX_ACTIVATIONFOCUSCHANGEDEVENTPARAMS eventData; + if (txGetActivationFocusChangedEventParams(hBehavior, &eventData) == TX_RESULT_OK) + { + if (eventData.HasActivationFocus) + { + PostMessage(_hWnd, _focusedRegionChangedMessage, interactorId, 0); + } + else + { + PostMessage(_hWnd, _focusedRegionChangedMessage, -1, 0); + } + + } +} + +void EyeXHost::OnActivated(TX_HANDLE hBehavior, int interactorId) +{ + PostMessage(_hWnd, _regionActivatedMessage, interactorId, 0); +} + +void TX_CALLCONVENTION EyeXHost::OnSnapshotCommitted(TX_CONSTHANDLE hAsyncData, TX_USERPARAM param) +{ + // check the result code using an assertion. + // this will catch validation errors and runtime errors in debug builds. in release builds it won't do anything. + + TX_RESULT result = TX_RESULT_UNKNOWN; + txGetAsyncDataResultCode(hAsyncData, &result); + assert(result == TX_RESULT_OK || result == TX_RESULT_CANCELLED); +} + +bool EyeXHost::QueryIsForWindowId(TX_HANDLE hQuery, const TX_CHAR* windowId) +{ + const int bufferSize = 20; + TX_CHAR buffer[bufferSize]; + + TX_SIZE count; + if (TX_RESULT_OK == txGetQueryWindowIdCount(hQuery, &count)) + { + for (int i = 0; i < count; i++) + { + TX_SIZE size = bufferSize; + if (TX_RESULT_OK == txGetQueryWindowId(hQuery, i, buffer, &size)) + { + if (0 == strcmp(windowId, buffer)) + { + return true; + } + } + } + } + + return false; +} -- cgit v1.2.3