void EyeXGaze::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); }
/* * Callback function invoked when an event has been received from the EyeX Engine. */ void TX_CALLCONVENTION HandleEvent(TX_CONSTHANDLE hAsyncData, TX_USERPARAM userParam) { TX_HANDLE hEvent = TX_EMPTY_HANDLE; TX_HANDLE hBehavior = 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)); if (txGetEventBehavior(hEvent, &hBehavior, TX_BEHAVIORTYPE_GAZEPOINTDATA) == TX_RESULT_OK) { OnGazeDataEvent(hBehavior); txReleaseObject(&hBehavior); } else if (txGetEventBehavior(hEvent, &hBehavior, TX_BEHAVIORTYPE_EYEPOSITIONDATA) == TX_RESULT_OK) { OnEyepositionDataEvent(hBehavior); txReleaseObject(&hBehavior); } // NOTE since this is a very simple application with a single interactor and a single data stream, // our event handling code can be very simple too. A more complex application would typically have to // check for multiple behaviors and route events based on interactor IDs. txReleaseObject(&hEvent); }
void EyeXHost::HandleQuery(TX_CONSTHANDLE hAsyncData) { std::lock_guard<std::mutex> 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); }