Tilt Five NDK  1.4.1
sample.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2020-2023 Tilt Five, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
19
21
22#ifdef _WIN32
23#include <windows.h>
24#define SLEEP(ms) Sleep(ms)
25#elif __unix
26// For linux, OSX, and other unixes
27#define _POSIX_C_SOURCE 199309L // or greater
28#include <time.h>
29#define SLEEP(ms) \
30 do { \
31 struct timespec ts; \
32 ts.tv_sec = (ms) / 1000; \
33 ts.tv_nsec = (ms) % 1000 * 1000000; \
34 nanosleep(&ts, NULL); \
35 } while (0)
36#else
37#warning Unsupported platform - sleep unavailable, code may run too fast
38#define SLEEP(ms) \
39 {}
40#endif
41
42#include "include/TiltFiveNative.h"
43
44#include <stdbool.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48
49#define IDENTIFIER_BUFFER_SIZE 1024
50#define GLASSES_BUFFER_SIZE 1024
51#define PARAM_BUFFER_SIZE 1024
52#define WAND_BUFFER_SIZE 4
53
54bool checkConnection(T5_Glasses glassesHandle, const char* id) {
55 T5_ConnectionState connectionState;
56 T5_Result err = t5GetGlassesConnectionState(glassesHandle, &connectionState);
57 if (err) {
58 printf("Glasses connection : '%s' -> Error: %s\n", id, t5GetResultMessage(err));
59 return false;
60 }
61
62 switch (connectionState) {
64 printf("Glasses connection : '%s' -> Connected\n", id);
65 return true;
66
68 printf("Glasses connection : '%s' -> Reserved\n", id);
69 return true;
70
72 printf("Glasses connection : '%s' -> Not Connected\n", id);
73 return true;
74
76 printf("Glasses connection : '%s' -> Device Lost\n", id);
77 return true;
78
79 default:
80 printf("Glasses connection : '%s' -> Unexpected state!\n", id);
81 return false;
82 }
83}
84
85void findWands(T5_Glasses glassesHandle) {
86 uint8_t count = WAND_BUFFER_SIZE;
87 T5_WandHandle wandListBuffer[WAND_BUFFER_SIZE];
88
89 T5_Result err = t5ListWandsForGlasses(glassesHandle, wandListBuffer, &count);
90 if (err) {
91 printf("Wand list : Error : %s\n", t5GetResultMessage(err));
92 return;
93 }
94
95 printf("Wand list : %u wand(s)\n", count);
96 for (int i = 0; i < count; i++) {
97 printf(" Wand found : ID=%d\n", wandListBuffer[i]);
98 }
99
101 if (count > 0) {
102 // Enable wand stream
103 T5_WandStreamConfig config;
104 config.enabled = true;
105 err = t5ConfigureWandStreamForGlasses(glassesHandle, &config);
106 if (err) {
107 printf("Failed to enable stream : %s\n", t5GetResultMessage(err));
108 return;
109 }
110
111 T5_WandStreamEvent event;
112 for (int i = 0; i < 100; i++) {
113 err = t5ReadWandStreamForGlasses(glassesHandle, &event, 100);
114 if (err) {
115 if (err == T5_TIMEOUT) {
116 continue;
117 }
118
119 printf("Failed to read stream : %s\n", t5GetResultMessage(err));
120 return;
121 }
122
123 switch (event.type) {
125 printf("WAND EVENT : CONNECT [%u]\n", event.wandId);
126 break;
127
129 printf("WAND EVENT : DISCONNECT [%u]\n", event.wandId);
130 break;
131
133 printf("WAND EVENT : DESYNC [%u]\n", event.wandId);
134 break;
135
137 printf("WAND EVENT : REPORT [%u] %fx%f\n",
138 event.wandId,
139 event.report.stick.x,
140 event.report.stick.y);
141 break;
142 }
143 }
144
145 // Disable wand stream
146 config.enabled = false;
147 err = t5ConfigureWandStreamForGlasses(glassesHandle, &config);
148 if (err) {
149 printf("Failed to disable stream : %s\n", t5GetResultMessage(err));
150 return;
151 }
152 }
154}
155
156const char* gameboardTypeString(T5_GameboardType gameboardType) {
157 switch (gameboardType) {
159 return "None";
161 return "LE";
163 return "XE";
165 return "XE Raised";
166 default:
167 return "<Unknown>";
168 }
169}
170
172void getPoses(T5_Glasses glassesHandle) {
173 T5_Result err;
174 T5_GlassesPose pose;
175
176 printf("Getting some poses");
177
178 bool waiting = false;
179 for (int i = 0; i < 1000; i++) {
181 if (err) {
182 if (err == T5_ERROR_TRY_AGAIN) {
183 if (!waiting) {
184 printf("\nWaiting...");
185 fflush(stdout);
186 } else {
187 printf(".");
188 fflush(stdout);
189 }
190 SLEEP(100);
191 i--;
192 waiting = true;
193 continue;
194 } else {
195 printf("Failed to get pose : %s\n", t5GetResultMessage(err));
196 return;
197 }
198 }
199
200 waiting = false;
201
202 printf("\nGLASSES POSE : [%lu] (Board:%s) "
203 "[%6.4f, %6.4f, %6.4f] [%6.4f, %6.4f, %6.4f, %6.4f]",
204 pose.timestampNanos,
205 gameboardTypeString(pose.gameboardType),
206 pose.posGLS_GBD.x,
207 pose.posGLS_GBD.y,
208 pose.posGLS_GBD.z,
209 pose.rotToGLS_GBD.w,
210 pose.rotToGLS_GBD.x,
211 pose.rotToGLS_GBD.y,
212 pose.rotToGLS_GBD.z);
213
214 SLEEP(16);
215 }
216
217 printf("\n");
218}
220
222T5_Result endlesslyWatchSettings(T5_Glasses glassesHandle, const char* id) {
223 T5_Result err;
224
225 printf("Watching for changes to settings... (forever)\n");
226 for (;;) {
227 uint16_t count = PARAM_BUFFER_SIZE;
228 T5_ParamGlasses paramBuffer[PARAM_BUFFER_SIZE];
229 err = t5GetChangedGlassesParams(glassesHandle, paramBuffer, &count);
230 if (err) {
231 printf("Error getting changed params for '%s' : %s\n", id, t5GetResultMessage(err));
232 return err;
233 }
234
235 // Get the values of the changed params
236 for (int i = 0; i < count; i++) {
237 switch (paramBuffer[i]) {
239 double value = 0.0;
240 err = t5GetGlassesFloatParam(glassesHandle, 0, paramBuffer[i], &value);
241 if (err) {
242 printf("Error getting changed IPD for '%s' : %s\n",
243 id,
244 t5GetResultMessage(err));
245 return err;
246 }
247
248 printf("IPD changed for '%s' : %f\n", id, value);
249 } break;
250
252 char buffer[T5_MAX_STRING_PARAM_LEN];
253 size_t bufferSize = T5_MAX_STRING_PARAM_LEN;
255 glassesHandle, paramBuffer[i], 0, buffer, &bufferSize);
256 if (err) {
257 printf("Error getting changed friendly name for '%s' : %s\n",
258 id,
259 t5GetResultMessage(err));
260 return err;
261 }
262
263 printf("Friendly name changed for '%s' : '%s'\n", id, buffer);
264 } break;
265
266 default:
267 // Ignore other parameters
268 break;
269 }
270 }
271 }
272}
274
275void connectGlasses(T5_Context t5ctx, const char* id) {
277
278 // CREATE GLASSES
279 T5_Glasses glassesHandle;
280 T5_Result err = t5CreateGlasses(t5ctx, id, &glassesHandle);
281 if (err) {
282 printf("Error creating glasses '%s' : %s\n", id, t5GetResultMessage(err));
283 return;
284 } else {
285 printf("Created glasses : '%s'\n", id);
286 }
288
289 // CHECK CONNECTION STATE
290 if (!checkConnection(glassesHandle, id)) {
291 return;
292 }
293
295 err = t5ReserveGlasses(glassesHandle, "My Awesome Application");
296 if (err) {
297 printf("Error acquiring glasses '%s': %s\n", id, t5GetResultMessage(err));
298 return;
299 }
301
302 printf("Glasses reserved : '%s'\n", id);
303
305 if (!checkConnection(glassesHandle, id)) {
306 return;
307 }
308
310 // ENSURE GLASSES ARE READY
311 for (;;) {
312 err = t5EnsureGlassesReady(glassesHandle);
313 if (err) {
314 if (err == T5_ERROR_TRY_AGAIN) {
315 // A non-sample program would probably sleep here or retry on another frame pass.
316 continue;
317 }
318 printf("Error ensure glasses ready '%s': %s\n", id, t5GetResultMessage(err));
319 return;
320 }
321
322 printf("Glasses ready : '%s'\n", id);
323 break;
324 }
326
327 // CHECK CONNECTION STATE
328 if (!checkConnection(glassesHandle, id)) {
329 return;
330 }
331
332 // RECALL THE IDENTIFIER FOR GLASSES
333 size_t bufferSize = IDENTIFIER_BUFFER_SIZE;
334 char recalledId[IDENTIFIER_BUFFER_SIZE];
335 err = t5GetGlassesIdentifier(glassesHandle, recalledId, &bufferSize);
336 if (err) {
337 printf("Error getting ID for glasses '%s' : %s\n", id, t5GetResultMessage(err));
338 return;
339 } else if (strcmp(id, recalledId) != 0) {
340 printf("Mismatch getting ID for glasses : '%s' -> '%s'\n", id, recalledId);
341 } else {
342 printf("Got ID for glasses : '%s' -> '%s'\n", id, recalledId);
343 }
344
345 // BRIEFLY PERFORM AN EXCLUSIVE OPERATION ON THE GLASSES
346 if (true) {
347 getPoses(glassesHandle);
348 }
349
350 // FIND AND BRIEFLY WATCH CONNECTED WANDS
351 if (true) {
352 findWands(glassesHandle);
353 }
354
355 // CHECK CONNECTION STATE
356 if (!checkConnection(glassesHandle, id)) {
357 return;
358 }
359
360 printf("Glasses ready : '%s'\n", id);
361
362 // Enable this to watch for changes to the glasses settings
363 if (false) {
364 err = endlesslyWatchSettings(glassesHandle, id);
365 if (err) {
366 printf("Error watching settings for glasses '%s' : %s\n", id, t5GetResultMessage(err));
367 return;
368 }
369 }
370
372 // RELEASE THE GLASSES
373 t5ReleaseGlasses(glassesHandle);
375
376 // CHECK CONNECTION STATE
377 if (!checkConnection(glassesHandle, id)) {
378 return;
379 }
380
381 // FIND AND BRIEFLY WATCH CONNECTED WANDS
382 // Note that the glasses are not reserved here!
383 if (true) {
384 findWands(glassesHandle);
385 }
386
387 // DESTROY THE GLASSES
388 t5DestroyGlasses(&glassesHandle);
389 printf("Destroyed glasses : '%s'\n", id);
390}
391
392size_t strnlen(const char* str, size_t maxLen) {
393 size_t len = 0;
394 while ((len < maxLen) && (str[len] != '\0')) {
395 len++;
396 }
397 return len;
398}
399
400int main(int argc, char** argv) {
402 T5_ClientInfo clientInfo = {
403 .applicationId = "com.MyCompany.MyApplication",
404 .applicationVersion = "1.0.0",
405 .sdkType = 0,
406 .reserved = 0,
407 };
408
409 T5_Context t5ctx;
410 T5_Result err = t5CreateContext(&t5ctx, &clientInfo, 0);
411 if (err) {
412 printf("Failed to create context\n");
413 }
415 printf("Init complete\n");
416
417 {
419 T5_GameboardSize gameboardSize;
420 err = t5GetGameboardSize(t5ctx, kT5_GameboardType_LE, &gameboardSize);
421 if (err) {
422 printf("Failed to get gameboard size\n");
423 } else {
424 printf("LE Gameboard size : %fm x %fm x %fm\n",
425 gameboardSize.viewableExtentPositiveX + gameboardSize.viewableExtentNegativeX,
426 gameboardSize.viewableExtentPositiveY + gameboardSize.viewableExtentNegativeY,
427 gameboardSize.viewableExtentPositiveZ);
428 }
430 }
431
433 bool waiting = false;
434 for (;;) {
435 char serviceVersion[T5_MAX_STRING_PARAM_LEN];
436 size_t bufferSize = T5_MAX_STRING_PARAM_LEN;
438 t5ctx, kT5_ParamSys_UTF8_Service_Version, serviceVersion, &bufferSize);
439 if (!err) {
440 printf("Service version : %s\n", serviceVersion);
441 break;
442 }
443
444 if (err == T5_ERROR_NO_SERVICE) {
445 if (!waiting) {
446 printf("Waiting for service...\n");
447 waiting = true;
448 }
449 } else {
450 printf("Error getting service version : %s\n", t5GetResultMessage(err));
451 exit(EXIT_FAILURE);
452 }
453 }
455
457 for (;;) {
458 size_t bufferSize = GLASSES_BUFFER_SIZE;
459 char glassesListBuffer[GLASSES_BUFFER_SIZE];
460 err = t5ListGlasses(t5ctx, glassesListBuffer, &bufferSize);
461 if (!err) {
462 size_t glassesCount = 0;
463
464 const char* buffPtr = glassesListBuffer;
465 for (;;) {
466 // Get the length of the string and exit if we've read the
467 // terminal string (Zero length)
468 size_t stringLength = strnlen(buffPtr, GLASSES_BUFFER_SIZE);
469 if (stringLength == 0) {
470 break;
471 }
472
473 fprintf(stderr, "Glasses : %s\n", buffPtr);
474 glassesCount++;
475
477 connectGlasses(t5ctx, buffPtr);
479
480 // Advance through the returned values
481 buffPtr += stringLength;
482 if (buffPtr > (glassesListBuffer + GLASSES_BUFFER_SIZE)) {
483 printf("Warning: list may be missing null terminator");
484 break;
485 }
486 }
487
488 printf("Listed glasses [%zu found]\n", glassesCount);
489 break;
490 }
491
492 if (err == T5_ERROR_NO_SERVICE) {
493 if (!waiting) {
494 printf("Waiting for service...\n");
495 waiting = true;
496 }
497 } else {
498 printf("Error listing glasses : %s\n", t5GetResultMessage(err));
499 exit(EXIT_FAILURE);
500 }
501 }
503
505 t5DestroyContext(&t5ctx);
507 printf("Shutdown complete\n");
508}
constexpr iterator_traits< _InputIterator >::difference_type count(_InputIterator __first, _InputIterator __last, const _Tp &__value)
uint32_t T5_Result
Represents an error code that may be returned by the Tilt Fiveā„¢ API.
Definition: errors.h:47
#define T5_MAX_STRING_PARAM_LEN
The maximum number of characters allowed for string values.
Definition: types.h:38
T5_ConnectionState
Glasses connection state.
Definition: types.h:234
T5_ParamGlasses
Possible parameters that can be retrieved for glasses.
Definition: types.h:500
uint8_t T5_WandHandle
Opaque handle used with wands.
Definition: types.h:104
struct T5_ContextImpl * T5_Context
Opaque handle used with system-wide functions.
Definition: types.h:92
T5_GameboardType
Possible gameboard types.
Definition: types.h:179
struct T5_GlassesImpl * T5_Glasses
Opaque handle used with glasses.
Definition: types.h:98
@ kT5_ConnectionState_Disconnected
Glasses were previously exclusively connected, but the device has disconnected.
Definition: types.h:245
@ kT5_ConnectionState_ExclusiveConnection
Glasses are connected for exclusive use.
Definition: types.h:236
@ kT5_ConnectionState_NotExclusivelyConnected
Glasses have not been exclusively connected or reserved.
Definition: types.h:242
@ kT5_ConnectionState_ExclusiveReservation
Glasses are reserved for exclusive use.
Definition: types.h:239
@ kT5_ParamGlasses_Float_IPD
Interpupillary distance - Float, millimeters
Definition: types.h:503
@ kT5_ParamGlasses_UTF8_FriendlyName
User-facing name of the glasses - UTF8.
Definition: types.h:506
@ kT5_ParamSys_UTF8_Service_Version
Version of the service software - UTF8.
Definition: types.h:512
@ kT5_GameboardType_LE
An LE gameboard.
Definition: types.h:184
@ kT5_GameboardType_XE
An XE gameboard, flap laid flat.
Definition: types.h:187
@ kT5_GameboardType_None
No gameboard.
Definition: types.h:181
@ kT5_GameboardType_XE_Raised
An XE gameboard, flap raised at an angle on the kickstand.
Definition: types.h:190
@ kT5_WandStreamEventType_Connect
Wand connected.
Definition: types.h:402
@ kT5_WandStreamEventType_Disconnect
Wand disconnected.
Definition: types.h:405
@ kT5_WandStreamEventType_Report
Wand report (Pose, Buttons, Trigger, Stick, Battery)
Definition: types.h:411
@ kT5_WandStreamEventType_Desync
Stream has desynchronized.
Definition: types.h:408
@ kT5_GlassesPoseUsage_GlassesPresentation
The pose will be used to render images to be presented on the glasses.
Definition: types.h:257
T5_EXPORT T5_Result t5CreateContext(T5_Context *context, const T5_ClientInfo *clientInfo, void *platformContext)
Create a context object.
T5_EXPORT void t5DestroyContext(T5_Context *context)
Destroy a context object.
#define T5_ERROR_TRY_AGAIN
Target is not currently available.
Definition: errors.h:120
#define T5_TIMEOUT
Timeout.
Definition: errors.h:57
T5_EXPORT const char * t5GetResultMessage(T5_Result result)
#define T5_ERROR_NO_SERVICE
Service isn't connected.
Definition: errors.h:72
T5_EXPORT T5_Result t5ReleaseGlasses(T5_Glasses glasses)
T5_EXPORT T5_Result t5CreateGlasses(T5_Context context, const char *id, T5_Glasses *glasses)
Create a glasses access object.
T5_EXPORT T5_Result t5GetGameboardSize(T5_Context context, T5_GameboardType gameboardType, T5_GameboardSize *gameboardSize)
Get the gameboard dimensions.
T5_EXPORT void t5DestroyGlasses(T5_Glasses *glasses)
Destroy a glasses object.
T5_EXPORT T5_Result t5ListGlasses(T5_Context context, char *buffer, size_t *bufferSize)
Enumerate all glasses.
T5_EXPORT T5_Result t5GetGlassesPose(T5_Glasses glasses, T5_GlassesPoseUsage usage, T5_GlassesPose *pose)
Get the latest pose of the glasses.
T5_EXPORT T5_Result t5GetGlassesConnectionState(T5_Glasses glasses, T5_ConnectionState *connectionState)
Get the exclusivity/connection status of the glasses.
T5_EXPORT T5_Result t5ReserveGlasses(T5_Glasses glasses, const char *displayName)
Reserve glasses for exclusive operations by the client.
T5_EXPORT T5_Result t5GetGlassesIdentifier(T5_Glasses glasses, char *buffer, size_t *bufferSize)
Get the device identifier of a glasses.
T5_EXPORT T5_Result t5EnsureGlassesReady(T5_Glasses glasses)
Ensure that reserved glasses are ready for exclusive operations.
T5_EXPORT T5_Result t5GetGlassesFloatParam(T5_Glasses glasses, T5_WandHandle wand, T5_ParamGlasses param, double *value)
Get a glasses floating point parameter.
T5_EXPORT T5_Result t5GetGlassesUtf8Param(T5_Glasses glasses, T5_WandHandle wand, T5_ParamGlasses param, char *buffer, size_t *bufferSize)
Get a glasses UTF-8 encoded string parameter.
T5_EXPORT T5_Result t5GetChangedGlassesParams(T5_Glasses glasses, T5_ParamGlasses *buffer, uint16_t *count)
Get a glasses-specific list of changed parameters.
T5_EXPORT T5_Result t5GetSystemUtf8Param(T5_Context context, T5_ParamSys param, char *buffer, size_t *bufferSize)
Get a system-wide UTF-8 encoded string parameter.
T5_EXPORT T5_Result t5ReadWandStreamForGlasses(T5_Glasses glasses, T5_WandStreamEvent *event, uint32_t timeoutMs)
Read from the wands event stream.
T5_EXPORT T5_Result t5ListWandsForGlasses(T5_Glasses glasses, T5_WandHandle *buffer, uint8_t *count)
List available wands connected to this glasses.
T5_EXPORT T5_Result t5ConfigureWandStreamForGlasses(T5_Glasses glasses, const T5_WandStreamConfig *config)
Configure the wand event stream.
Client provided information for use with t5CreateGlasses()
Definition: types.h:217
const char * applicationId
The application ID.
Definition: types.h:219
Physical dimensions of a gameboard.
Definition: types.h:194
float viewableExtentNegativeY
The distance in meters from the gameboard origin to the edge of the viewable area in the negative Y d...
Definition: types.h:209
float viewableExtentPositiveZ
The distance in meters above the gameboard origin that the viewable area extends in the positive Z di...
Definition: types.h:213
float viewableExtentPositiveY
The distance in meters from the gameboard origin to the edge of the viewable area in the positive Y d...
Definition: types.h:205
float viewableExtentNegativeX
The distance in meters from the gameboard origin to the edge of the viewable area in the negative X d...
Definition: types.h:201
float viewableExtentPositiveX
The distance in meters from the gameboard origin to the edge of the viewable area in the positive X d...
Definition: types.h:197
Glasses pose information to be retrieved with t5GetGlassesPose()
Definition: types.h:280
uint64_t timestampNanos
The timestamp of the pose.
Definition: types.h:282
T5_Vec3 posGLS_GBD
The position of the origin of the GLS (glasses) frame relative to the GBD (gameboard) frame.
Definition: types.h:286
T5_Quat rotToGLS_GBD
The rotation that transforms points in the GBD (gameboard) frame orientation to the GLS (glasses) fra...
Definition: types.h:290
T5_GameboardType gameboardType
The type of gameboard visible for this pose.
Definition: types.h:293
T5_Vec2 stick
Stick (X/Y) - Analog, Range [-1.0 - 1.0], 0 = Centered, 1.0 = Top/Right.
Definition: types.h:447
Wand stream configuration.
Definition: types.h:393
bool enabled
Enable or disable the entire stream. True = enabled.
Definition: types.h:395
Represents an event from the wand stream.
Definition: types.h:485
T5_WandReport report
Report (Valid if type = kT5_WandStreamEventType_Report)
Definition: types.h:496
T5_WandStreamEventType type
Type of event.
Definition: types.h:490
T5_WandHandle wandId
Opaque identifier for the wand.
Definition: types.h:487