Tilt Five NDK
TiltFiveNative.hpp
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
17#pragma once
18
21
22#include "TiltFiveNative.h"
23#include "errors.hpp"
24#include "result.hpp"
25
26#include <algorithm>
27#include <atomic>
28#include <chrono>
29#include <functional>
30#include <iomanip>
31#include <iostream>
32#include <map>
33#include <memory>
34#include <mutex>
35#include <set>
36#include <string>
37#include <thread>
38#include <utility>
39#include <vector>
40
44
45namespace tiltfive {
46
50
54
55class Client;
56class Glasses;
57class Wand;
58class WandStreamHelper;
59class GlassesConnectionHelper;
60class ParamChangeHelper;
61class ParamChangeListener;
62
65inline auto obtainWand(T5_WandHandle handle, std::shared_ptr<WandStreamHelper> wandStreamHelper)
67inline auto obtainWandStreamHelper(
72 const std::string& displayName,
73 std::chrono::milliseconds connectionPollInterval)
77 std::chrono::milliseconds pollInterval)
80
82class Client : public std::enable_shared_from_this<Client> {
83protected:
84 static constexpr bool kDebug = true;
85
86 Client(std::string applicationId, std::string applicationVersion, const uint8_t sdkType)
87 : mApplicationId(std::move(applicationId))
88 , mApplicationVersion(std::move(applicationVersion)) {
89
90 mClientInfo.applicationId = mApplicationId.c_str();
91 mClientInfo.applicationVersion = mApplicationVersion.c_str();
92 mClientInfo.sdkType = sdkType;
93 }
94
95 const std::string mApplicationId;
96 const std::string mApplicationVersion;
97
98 T5_Context mContext{};
99 T5_ClientInfo mClientInfo{};
100
101 friend Glasses;
102 friend ParamChangeHelper;
103
104 friend std::ostream& operator<<(std::ostream& os, std::shared_ptr<Client> const& instance) {
105 os << *instance;
106 return os;
107 }
108
109 friend std::ostream& operator<<(std::ostream& os, Client const& instance) {
110 os << "<Client:" << instance.mApplicationId << ">";
111 return os;
112 }
113
114 friend auto obtainClient(const std::string& applicationId,
115 const std::string& applicationVersion,
116 void* platformContext,
117 const uint8_t sdkType) -> Result<std::shared_ptr<Client>>;
118
119 static auto create(const std::string& applicationId,
120 const std::string& applicationVersion,
121 void* platformContext,
122 const uint8_t sdkType) -> Result<std::shared_ptr<Client>> {
123
124 // Validate inputs
125 if (applicationId.length() > T5_MAX_STRING_PARAM_LEN) {
127 }
128
129 if (applicationVersion.length() > T5_MAX_STRING_PARAM_LEN) {
131 }
132
133 // Create client
134 auto client =
135 std::shared_ptr<Client>(new Client(applicationId, applicationVersion, sdkType));
136
137 // Start up the service connection
138 auto err = t5CreateContext(&client->mContext, &client->mClientInfo, platformContext);
139 if (err) {
140 return static_cast<Error>(err);
141 }
142
143 return client;
144 };
145
146public:
148 virtual ~Client() {
149 // Release context and library
150 t5DestroyContext(&mContext);
151 mContext = nullptr;
152 }
154
158
165 [[nodiscard]] auto getHandle() const -> T5_Context {
166 return mContext;
167 }
168
175 std::vector<char> buffer;
176 buffer.resize(64);
177
178 size_t bufferSize;
179
180 // If the buffer passed to listGlasses() is too small, it'll return with
181 // an overflow condition, in which case, increase the size of the buffer
182 // and try again.
183 for (;;) {
184 bufferSize = buffer.size();
185 T5_Result err = t5ListGlasses(mContext, buffer.data(), &bufferSize);
186 if (!err) {
187 break;
188 } else if (err == T5_ERROR_OVERFLOW) {
189 if (bufferSize > 1024) {
190 return Error::kOverflow;
191 }
192
193 buffer.resize(bufferSize);
194 } else {
195 return static_cast<Error>(err);
196 }
197 }
198
199 // Peel off string until we encounter a naked null (empty string)
200 std::vector<std::string> glassesList;
201 auto buffPtr = buffer.data();
202 for (;;) {
203 std::string id = buffPtr;
204 if (id.empty()) {
205 break;
206 }
207
208 buffPtr += id.length() + 1;
209 glassesList.push_back(id);
210 }
211 return glassesList;
212 }
213
221 T5_Result err =
223 if (!err) {
224 return std::string(value.get(), size);
225 } else {
226 return static_cast<Error>(err);
227 }
228 }
229
238 uint16_t changedParamsCount = 32;
239 std::vector<T5_ParamSys> changedParamsBuffer(changedParamsCount);
240
241 changedParamsBuffer.resize(changedParamsCount);
242 T5_Result err =
243 t5GetChangedSystemParams(mContext, changedParamsBuffer.data(), &changedParamsCount);
244
245 for (;;) {
246 if (!err) {
247 changedParamsBuffer.resize(changedParamsCount);
248 return changedParamsBuffer;
249
250 } else if (err == T5_ERROR_OVERFLOW) {
251 changedParamsCount = changedParamsBuffer.size() * 2;
252 continue;
253
254 } else {
255 return static_cast<Error>(err);
256 }
257 }
258 }
259
272 int64_t value = 0;
273
274 T5_Result err =
276 if (!err) {
277 return value != 0;
278 } else if (err == T5_ERROR_SETTING_UNKNOWN) {
279 return false;
280 } else {
281 return static_cast<Error>(err);
282 }
283 }
284
291
292 T5_Result err = t5GetGameboardSize(mContext, type, &size);
293 if (!err) {
294 return size;
295 } else {
296 return static_cast<Error>(err);
297 }
298 }
299
307 [[nodiscard]] auto createParamChangedHelper(
311
312 return obtainParamChangeHelper(shared_from_this(), std::move(listener), pollInterval);
313 }
314};
315
317enum class ConnectionState : int {
321
323 kReserved,
324
327
330};
331
333class Glasses : public std::enable_shared_from_this<Glasses> {
334protected:
335 friend Client;
336 friend ParamChangeHelper;
337
338 const std::string mIdentifier;
339 const std::shared_ptr<Client> mClient;
340 std::weak_ptr<WandStreamHelper> mWandStreamHelper{};
341 T5_Glasses mGlasses{};
342
343 friend std::ostream& operator<<(std::ostream& os, std::shared_ptr<Glasses> const& instance) {
344 os << *instance;
345 return os;
346 }
347
348 friend std::ostream& operator<<(std::ostream& os, Glasses const& instance) {
349 os << "<Glasses:" << instance.mIdentifier << ">";
350 return os;
351 }
352
353 friend auto obtainGlasses(const std::string& identifier, const std::shared_ptr<Client>& client)
355
356 explicit Glasses(std::string identifier, std::shared_ptr<Client> client)
357 : mIdentifier(std::move(identifier)), mClient(std::move(client)) {}
358
359 static auto create(std::string identifier, std::shared_ptr<Client> client)
361
362 if (!client) {
364 }
365
366 T5_Glasses handle;
367 T5_Result err = t5CreateGlasses(client->mContext, identifier.c_str(), &handle);
368
369 if (err) {
370 return static_cast<Error>(err);
371 }
372
373 std::shared_ptr<Glasses> glasses{new Glasses(identifier, client)};
374
375 glasses->mGlasses = handle;
376
377 return glasses;
378 }
379
380public:
387 [[nodiscard]] auto getHandle() const -> T5_Glasses {
388 return mGlasses;
389 }
390
400 [[nodiscard]] auto getIdentifier() const -> std::string {
401 return mIdentifier;
402 }
403
408 T5_ConnectionState connectionState;
409 T5_Result err = t5GetGlassesConnectionState(mGlasses, &connectionState);
410 if (err != T5_SUCCESS) {
411 return static_cast<Error>(err);
412 }
413
414 switch (connectionState) {
417
420
423
426
427 default:
429 }
430 }
431
458 T5_DepthRange depthRange,
459 T5_MatrixOrder matrixOrder,
460 double nearPlane,
461 double farPlane,
462 double worldScale) -> Result<T5_ProjectionInfo> {
463
464 T5_ProjectionInfo projectionInfo;
465 T5_Result err = t5GetProjection(mGlasses,
466 handedness,
467 depthRange,
468 matrixOrder,
469 nearPlane,
470 farPlane,
471 worldScale,
472 &projectionInfo);
473
474 if (!err) {
475 return projectionInfo;
476 } else {
477 return static_cast<Error>(err);
478 }
479 }
480
489 uint16_t changedParamsCount = 32;
490 std::vector<T5_ParamGlasses> changedParamsBuffer(changedParamsCount);
491
492 changedParamsBuffer.resize(changedParamsCount);
493 T5_Result err =
494 t5GetChangedGlassesParams(mGlasses, changedParamsBuffer.data(), &changedParamsCount);
495
496 for (;;) {
497 if (!err) {
498 changedParamsBuffer.resize(changedParamsCount);
499 return changedParamsBuffer;
500
501 } else if (err == T5_ERROR_OVERFLOW) {
502 changedParamsCount = changedParamsBuffer.size() * 2;
503 continue;
504
505 } else {
506 return static_cast<Error>(err);
507 }
508 }
509 }
510
519 double value = 0;
521 if (!err) {
522 return value;
523 } else {
524 return static_cast<Error>(err);
525 }
526 }
527
540 mGlasses, 0, kT5_ParamGlasses_UTF8_FriendlyName, value.get(), &size);
541 if (!err) {
542 return std::string(value.get());
543 } else {
544 return static_cast<Error>(err);
545 }
546 }
547
568 auto reserve(const std::string& displayName) -> Result<void> {
569 T5_Result err = t5ReserveGlasses(mGlasses, displayName.c_str());
570 if (!err) {
571 return kSuccess;
572 } else {
573 return static_cast<Error>(err);
574 }
575 }
576
592 T5_Result err = t5EnsureGlassesReady(mGlasses);
593 if (!err) {
594 return kSuccess;
595 } else {
596 return static_cast<Error>(err);
597 }
598 }
599
608 T5_Result err = t5ReleaseGlasses(mGlasses);
609 if (!err) {
610 return kSuccess;
611 } else {
612 return static_cast<Error>(err);
613 }
614 }
615
622 T5_GlassesPose pose;
623 T5_Result err = t5GetGlassesPose(mGlasses, usage, &pose);
624
625 if (!err) {
626 return pose;
627 } else {
628 return static_cast<Error>(err);
629 }
630 }
631
636 uint16_t gameboardCount = 1;
637 std::vector<T5_GameboardPose> gameboardPoses(gameboardCount);
638 T5_Result err = t5GetGameboardPoses(mGlasses, gameboardPoses.data(), &gameboardCount);
639
640 if (!err) {
641 return gameboardPoses;
642 } else {
643 return static_cast<Error>(err);
644 }
645 }
646
651 auto initGraphicsContext(T5_GraphicsApi graphicsApi, void* graphicsContext) -> Result<void> {
652 T5_Result err = t5InitGlassesGraphicsContext(mGlasses, graphicsApi, graphicsContext);
653 if (!err) {
654 return kSuccess;
655 }
656 return static_cast<Error>(err);
657 }
658
663 T5_Result err = t5ConfigureCameraStreamForGlasses(mGlasses, config);
664 if (!err) {
665 return kSuccess;
666 } else {
667 return static_cast<Error>(err);
668 }
669 }
670
672 //
675 T5_CamImage img;
676 T5_Result err = t5GetFilledCamImageBuffer(mGlasses, &img);
677 if (!err) {
678 return std::move(img);
679 } else {
680 return static_cast<Error>(err);
681 }
682 }
683
685 //
688 T5_Result err = t5SubmitEmptyCamImageBuffer(mGlasses, imgBuffer);
689 if (!err) {
690 return kSuccess;
691 } else {
692 return static_cast<Error>(err);
693 }
694 }
695
697 //
700 auto cancelCamImageBuffer(uint8_t* buffer) -> Result<void> {
701 T5_Result err = t5CancelCamImageBuffer(mGlasses, buffer);
702 if (!err) {
703 return kSuccess;
704 } else {
705 return static_cast<Error>(err);
706 }
707 }
708
710 //
714 T5_Result err = t5GetDewarpedPixelCoordinate(mGlasses, pixelCoordinate);
715 if (!err) {
716 return kSuccess;
717 } else {
718 return static_cast<Error>(err);
719 }
720 }
721
725 auto sendFrame(const T5_FrameInfo* const frameInfo) -> Result<void> {
726 T5_Result err = t5SendFrameToGlasses(mGlasses, frameInfo);
727 if (!err) {
728 return kSuccess;
729 } else {
730 return static_cast<Error>(err);
731 }
732 }
733
739 auto sendImpulse(T5_WandHandle handle, float amplitude, uint16_t duration) -> Result<void> {
740 T5_Result err = t5SendImpulse(mGlasses, handle, amplitude, duration);
741 if (!err) {
742 return kSuccess;
743 } else {
744 return static_cast<Error>(err);
745 }
746 }
747
752 uint8_t wandCount = 4;
753 std::vector<T5_WandHandle> wandBuffer(wandCount);
754
755 for (;;) {
756 wandBuffer.resize(wandCount);
757 T5_Result err = t5ListWandsForGlasses(mGlasses, wandBuffer.data(), &wandCount);
758
759 if (!err) {
761 wands.reserve(wandCount);
762
763 for (auto i = 0; i < wandCount; i++) {
764 wands.push_back(wandBuffer[i]);
765 }
766
767 return wands;
768
769 } else if (err == T5_ERROR_OVERFLOW) {
770 wandCount = wandBuffer.size() * 2;
771 continue;
772
773 } else {
774 return static_cast<Error>(err);
775 }
776 }
777 }
778
783 T5_Result err = t5ConfigureWandStreamForGlasses(mGlasses, config);
784 if (!err) {
785 return kSuccess;
786 } else {
787 return static_cast<Error>(err);
788 }
789 }
790
807 T5_WandStreamEvent event;
808
809 T5_Result err = t5ReadWandStreamForGlasses(mGlasses, &event, timeout.count());
810 if (!err) {
811 return event;
812 } else {
813 return static_cast<Error>(err);
814 }
815 }
816
822 auto wandStreamHelper = mWandStreamHelper.lock();
823 if (!wandStreamHelper) {
824 // needs initialization
825 wandStreamHelper = obtainWandStreamHelper(shared_from_this());
826 mWandStreamHelper = wandStreamHelper;
827 }
828 return wandStreamHelper;
829 }
830
839 const std::string& displayName,
840 std::chrono::milliseconds connectionPollInterval = std::chrono::milliseconds(100))
842
844 shared_from_this(), displayName, connectionPollInterval);
845 }
846
848 virtual ~Glasses() {
849 // Disconnect the glasses if they're connected
850 auto connectionState = getConnectionState();
851 if (!connectionState) {
852 return;
853 }
854
855 if (mGlasses) {
856 t5DestroyGlasses(&mGlasses);
857 mGlasses = nullptr;
858 }
859 }
861};
862
867private:
868 const std::shared_ptr<Glasses> mGlasses;
869 const std::string mDisplayName;
870 const std::chrono::milliseconds mConnectionPollInterval;
871 const std::chrono::milliseconds mConnectedPollInterval = mConnectionPollInterval * 10;
872
873 std::atomic<bool> mRunning{true};
874 std::thread mThread;
875
876 std::mutex mLastAsyncErrorMtx;
877 std::atomic<std::error_code> mLastAsyncError{};
878
879 void setLastAsyncError(std::error_code err) {
880 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
881 mLastAsyncError = err;
882 }
883
884 void threadMain() {
885 while (mRunning) {
886 auto connectionState = mGlasses->getConnectionState();
887 if (!connectionState) {
888 setLastAsyncError(connectionState.error());
889 std::this_thread::sleep_for(mConnectionPollInterval);
890 continue;
891 }
892
893 switch (*connectionState) {
895 // Attempt to connect
896 auto result = mGlasses->reserve(mDisplayName);
897 if (!result) {
898 setLastAsyncError(result.error());
899 }
900 // No action on success - the next call to getConnectionState() will
901 // detect the change
902
903 break;
904 }
905
908 auto result = mGlasses->ensureReady();
909 if (!result) {
910 setLastAsyncError(result.error());
911 }
912 // No action on success - the next call to getConnectionState() will
913 // detect the change
914
915 break;
916 }
917
919 // If we're connected, increase polling interval to reduce excessive
920 // connections state queries (at the expense of slowing detection of
921 // disconnected devices).
922 std::this_thread::sleep_for(mConnectedPollInterval);
923 break;
924 }
925
926 std::this_thread::sleep_for(mConnectionPollInterval);
927 }
928 }
929
931 const std::string& displayName,
932 std::chrono::milliseconds connectionPollInterval)
934
936 std::string displayName,
937 std::chrono::milliseconds connectionPollInterval)
938 : mGlasses(std::move(glasses))
939 , mDisplayName{std::move(displayName)}
940 , mConnectionPollInterval(connectionPollInterval) {
941
942 mThread = std::thread(&GlassesConnectionHelper::threadMain, this);
943 }
944
945public:
947 [[nodiscard]] auto glasses() -> Glasses& {
948 return *mGlasses;
949 }
950
953 auto connectionState = mGlasses->getConnectionState();
954 if (!connectionState) {
955 return connectionState.error();
956 }
957
958 while (*connectionState != ConnectionState::kConnected) {
959 std::this_thread::sleep_for(mConnectionPollInterval);
960
961 connectionState = mGlasses->getConnectionState();
962 if (!connectionState) {
963 return connectionState.error();
964 }
965 }
966
967 return kSuccess;
968 }
969
974 auto start = std::chrono::steady_clock::now();
975
976 auto connectionState = mGlasses->getConnectionState();
977 if (!connectionState) {
978 return connectionState.error();
979 }
980
981 while (*connectionState != ConnectionState::kConnected) {
982 if ((std::chrono::steady_clock::now() - start) > timeout) {
983 return Error::kTimeout;
984 }
985
986 std::this_thread::sleep_for(mConnectionPollInterval);
987
988 connectionState = mGlasses->getConnectionState();
989 if (!connectionState) {
990 return connectionState.error();
991 }
992 }
993
994 return kSuccess;
995 }
996
1004 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
1005 return mLastAsyncError.exchange({});
1006 }
1007
1009 virtual ~GlassesConnectionHelper() {
1010 mRunning = false;
1011 if (mThread.joinable()) {
1012 mThread.join();
1013 }
1014 }
1016};
1017
1025class WandStreamHelper : public std::enable_shared_from_this<WandStreamHelper> {
1026private:
1027 friend Wand;
1028
1029 const std::shared_ptr<Glasses> mGlasses;
1030 const std::chrono::milliseconds mPollTimeout;
1031
1032 std::atomic<bool> mWandListDirty{true};
1033 std::mutex mWandListMtx; // guards access to mWandList
1035
1036 std::atomic<bool> mRunning{true};
1037 std::thread mThread;
1038
1039 std::mutex mLastWandReportsMtx; // guards access to mLastWandReports
1041
1042 std::mutex mLastAsyncErrorMtx;
1043 std::atomic<std::error_code> mLastAsyncError{};
1044
1045 void setLastAsyncError(std::error_code err) {
1046 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
1047 mLastAsyncError = err;
1048 }
1049
1050 auto drainStream(const std::shared_ptr<Glasses>& glasses) -> Result<void> {
1051 while (mRunning) {
1052 auto result = glasses->readWandStream(mPollTimeout);
1053 if (!result) {
1054 return result.error();
1055 }
1056
1057 std::lock_guard<std::mutex> lock{mLastWandReportsMtx};
1058
1059 // Process the event
1060 switch (result->type) {
1062 mLastWandReports[result->wandId] = {};
1063 mWandListDirty = true;
1064 break;
1065
1067 mLastWandReports.erase(result->wandId);
1068 mWandListDirty = true;
1069 break;
1070
1072 mWandListDirty = true;
1073 break;
1074
1076 mLastWandReports[result->wandId] = result->report;
1077 break;
1078 }
1079 }
1080
1081 return Error::kUnavailable;
1082 }
1083
1084 // Update the reports map based on the latest wand list.
1085 // Ensure empty reports are populated for newly-connected wands.
1086 // Remove reports for wands that are no longer connected.
1087 //
1088 // PRECONDITIONS: Wand list mutex must be held.
1089 auto refreshReports() -> void {
1090 std::lock_guard<std::mutex> lock{mLastWandReportsMtx};
1091
1092 // Obtain a set of the wand handles held in mLastWandReports
1093 std::set<T5_WandHandle> lastWandReportKeys;
1094 std::transform(mLastWandReports.cbegin(),
1095 mLastWandReports.cend(),
1096 std::inserter(lastWandReportKeys, lastWandReportKeys.end()),
1097 [](std::pair<T5_WandHandle, T5_WandReport> pair) { return pair.first; });
1098
1099 // Remove from the list all connected wands and add empty reports for new wands.
1100 for (const auto& connectedWand : mWandList) {
1101 lastWandReportKeys.erase(connectedWand);
1102 mLastWandReports.insert({connectedWand, T5_WandReport{}});
1103 }
1104
1105 // The remainder of the list is wand reports for disconnected wands - remove them
1106 for (auto defunctKey : lastWandReportKeys) {
1107 mLastWandReports.erase(defunctKey);
1108 }
1109 }
1110
1111 void threadMain() {
1112 T5_WandStreamConfig streamConfig{true};
1113 bool configured = false;
1114
1115 while (mRunning) {
1116 // Configure the stream if we haven't already
1117 if (!configured) {
1118 auto configureRequest = mGlasses->configureWandStream(&streamConfig);
1119 if (!configureRequest) {
1120 setLastAsyncError(configureRequest.error());
1122 continue;
1123 }
1124 configured = true;
1125 }
1126
1127 // Drain the stream
1128 auto result = drainStream(mGlasses);
1129 if ((result.error() != tiltfive::Error::kTimeout) &&
1130 (result.error() != tiltfive::Error::kUnavailable)) {
1131
1132 // For errors other than timeout, record it, small delay and loop
1133 setLastAsyncError(result.error());
1135 }
1136 }
1137
1138 // Disable the stream
1139 streamConfig.enabled = false;
1140 auto configureRequest = mGlasses->configureWandStream(&streamConfig);
1141 if (!configureRequest) {
1142 setLastAsyncError(configureRequest.error());
1143 }
1144
1145 // Flag as no longer running if we've exited due to error
1146 mRunning = false;
1147 }
1148
1149 friend inline auto obtainWandStreamHelper(std::shared_ptr<Glasses> glasses,
1150 std::chrono::milliseconds pollTimeout)
1152
1153 explicit WandStreamHelper(
1156 : mGlasses(std::move(glasses)), mPollTimeout(pollTimeout) {
1157
1158 mThread = std::thread(&WandStreamHelper::threadMain, this);
1159 }
1160
1161 auto getLatestReport(const T5_WandHandle& handle) -> Result<T5_WandReport> {
1162 std::lock_guard<std::mutex> lock{mLastWandReportsMtx};
1163
1164 auto report = mLastWandReports.find(handle);
1165 if (report == mLastWandReports.end()) {
1167 }
1168
1169 return report->second;
1170 };
1171
1172public:
1181 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
1182 return mLastAsyncError.exchange({});
1183 }
1184
1192 std::lock_guard<std::mutex> lock{mWandListMtx};
1193
1194 // Update the wand list if it's been invalidated
1195 if (mWandListDirty.exchange(false)) {
1196 auto result = mGlasses->listWands();
1197 if (!result) {
1198 mWandListDirty = true;
1199 return result.error();
1200 }
1201
1202 std::vector<T5_WandHandle> wandHandles;
1203 for (auto wandHandle : *result) {
1204 wandHandles.push_back(wandHandle);
1205 }
1206 mWandList = wandHandles;
1207
1208 refreshReports();
1209 }
1210
1211 // Realize wand list
1213 for (auto wandHandle : mWandList) {
1214 wands.push_back(obtainWand(wandHandle, shared_from_this()));
1215 }
1216
1217 return wands;
1218 };
1219
1225 auto sendImpulse(const T5_WandHandle& handle, float amplitude, uint16_t duration)
1226 -> Result<void> {
1227 auto result = mGlasses->sendImpulse(handle, amplitude, duration);
1228 if (!result) {
1229 return result.error();
1230 }
1231
1232 return kSuccess;
1233 }
1234
1236 virtual ~WandStreamHelper() {
1237 mRunning = false;
1238 if (mThread.joinable()) {
1239 mThread.join();
1240 }
1241 }
1243};
1244
1247public:
1250 virtual auto onSysParamChanged(const std::vector<T5_ParamSys>& changed) -> void = 0;
1251
1255 const std::vector<T5_ParamGlasses>& changed) -> void = 0;
1256
1258 virtual ~ParamChangeListener() = default;
1260};
1261
1265private:
1266 const std::shared_ptr<Client> mClient;
1267 const std::weak_ptr<ParamChangeListener> mChangeListener;
1268
1269 static constexpr size_t kDefaultSettingBufferSize = 16;
1270
1271 std::mutex mRegisteredGlassesMtx;
1272 std::set<std::shared_ptr<Glasses>> mRegisteredGlasses;
1273
1274 std::vector<T5_ParamSys> mChangedSysParams;
1275 std::vector<T5_ParamGlasses> mChangedGlassesParams;
1276
1277 std::chrono::milliseconds mPollInterval;
1278
1279 std::thread mThread;
1280 std::atomic<bool> mRunning{true};
1281
1282 std::mutex mLastAsyncErrorMtx;
1283 std::atomic<std::error_code> mLastAsyncError{};
1284
1285 void setLastAsyncError(std::error_code err) {
1286 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
1287 mLastAsyncError = err;
1288 }
1289
1292 std::chrono::milliseconds pollInterval)
1294
1297 std::chrono::milliseconds pollInterval)
1298 : mClient(std::move(client))
1299 , mChangeListener(std::move(listener))
1300 , mPollInterval(pollInterval) {
1301
1302 mThread = std::thread(&ParamChangeHelper::threadMain, this);
1303 }
1304
1305 auto checkGlassesParams(const std::shared_ptr<ParamChangeListener>& listener) -> void {
1306 std::lock_guard<std::mutex> lock(mRegisteredGlassesMtx);
1307 for (const auto& glasses : mRegisteredGlasses) {
1308 checkGlassesParams(glasses, listener);
1309 }
1310 }
1311
1312 auto checkGlassesParams(const std::shared_ptr<Glasses>& glasses,
1313 const std::shared_ptr<ParamChangeListener>& listener) -> void {
1314 uint16_t changeCount;
1315
1316 mChangedGlassesParams.resize(kDefaultSettingBufferSize);
1317 for (;;) {
1318 changeCount = mChangedGlassesParams.size();
1320 glasses->mGlasses, mChangedGlassesParams.data(), &changeCount);
1321
1322 if (!err) {
1323 if (changeCount > 0) {
1324 mChangedGlassesParams.resize(changeCount);
1325 listener->onGlassesParamChanged(glasses, mChangedGlassesParams);
1326 }
1327 break;
1328 }
1329
1330 // Error - increase buffer if we overflowed, or record the error and exit
1331 if (err == T5_ERROR_OVERFLOW) {
1332 mChangedSysParams.resize(mChangedSysParams.size() * 2);
1333 continue;
1334 }
1335
1336 setLastAsyncError(static_cast<Error>(err));
1337 break;
1338 }
1339 }
1340
1341 auto checkSysParams(const std::shared_ptr<ParamChangeListener>& listener) -> void {
1342 uint16_t changeCount;
1343
1344 mChangedSysParams.resize(kDefaultSettingBufferSize);
1345 for (;;) {
1346 changeCount = mChangedSysParams.size();
1347 T5_Result err =
1348 t5GetChangedSystemParams(mClient->mContext, mChangedSysParams.data(), &changeCount);
1349
1350 if (!err) {
1351 if (changeCount > 0) {
1352 mChangedSysParams.resize(changeCount);
1353 listener->onSysParamChanged(mChangedSysParams);
1354 }
1355 break;
1356 }
1357
1358 // Error - increase buffer if we overflowed, or record the error and exit
1359 if (err == T5_ERROR_OVERFLOW) {
1360 mChangedSysParams.resize(mChangedSysParams.size() * 2);
1361 continue;
1362 }
1363
1364 setLastAsyncError(static_cast<Error>(err));
1365 break;
1366 }
1367 }
1368
1369 auto threadMain() -> void {
1370 while (mRunning) {
1371 // Listener weak_ptr -> shared_ptr or exit
1372 {
1373 auto listener = mChangeListener.lock();
1374 if (!listener) {
1375 break;
1376 }
1377
1378 checkGlassesParams(listener);
1379
1380 checkSysParams(listener);
1381 }
1382
1383 std::this_thread::sleep_for(mPollInterval);
1384 }
1385 }
1386
1387public:
1389 virtual ~ParamChangeHelper() {
1390 mRunning = false;
1391 if (mThread.joinable()) {
1392 mThread.join();
1393 }
1394 }
1396
1405 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
1406 return mLastAsyncError.exchange({});
1407 }
1408
1410 auto registerGlasses(const std::shared_ptr<Glasses>& glasses) -> void {
1411 std::lock_guard<std::mutex> lock(mRegisteredGlassesMtx);
1412 mRegisteredGlasses.insert(glasses);
1413 }
1414
1416 auto deregisterGlasses(const std::shared_ptr<Glasses>& glasses) -> void {
1417 std::lock_guard<std::mutex> lock(mRegisteredGlassesMtx);
1418 mRegisteredGlasses.erase(glasses);
1419 }
1420};
1421
1424class Wand {
1425protected:
1426 T5_WandHandle mHandle;
1427 std::shared_ptr<WandStreamHelper> mWandStreamHelper;
1428
1429 friend WandStreamHelper;
1430
1431 friend std::ostream& operator<<(std::ostream& os, std::shared_ptr<Wand> const& instance) {
1432 os << *instance;
1433 return os;
1434 }
1435
1436 friend std::ostream& operator<<(std::ostream& os, Wand const& instance) {
1437 os << "<Wand:" << +instance.mHandle << ">";
1438 return os;
1439 }
1440
1443
1445 : mHandle(handle), mWandStreamHelper(std::move(wandStreamHelper)) {}
1446
1447public:
1450 return mWandStreamHelper->getLatestReport(mHandle);
1451 }
1452
1456 auto sendImpulse(float amplitude, uint16_t duration) const -> Result<void> {
1457 return mWandStreamHelper->sendImpulse(mHandle, amplitude, duration);
1458 }
1459
1461 [[nodiscard]] T5_WandHandle handle() const {
1462 return mHandle;
1463 }
1464};
1465
1475inline auto obtainClient(const std::string& applicationId,
1476 const std::string& applicationVersion,
1477 void* platformContext,
1478 const uint8_t sdkType = 0) -> Result<std::shared_ptr<Client>> {
1479
1480 return Client::create(applicationId, applicationVersion, platformContext, sdkType);
1481}
1482
1489inline auto obtainGlasses(const std::string& identifier, const std::shared_ptr<Client>& client)
1491 return Glasses::create(identifier, client);
1492}
1493
1496 std::chrono::milliseconds pollTimeout)
1498 return std::shared_ptr<WandStreamHelper>(new WandStreamHelper(std::move(glasses), pollTimeout));
1499}
1500
1504 return std::shared_ptr<Wand>(new Wand(handle, std::move(wandStreamHelper)));
1505}
1506
1509 const std::string& displayName,
1510 std::chrono::milliseconds connectionPollInterval)
1512
1514 new GlassesConnectionHelper(std::move(glasses), displayName, connectionPollInterval));
1515}
1516
1520 std::chrono::milliseconds pollInterval)
1522
1524 new ParamChangeHelper(std::move(client), std::move(listener), pollInterval));
1525}
1527
1529
1530} // namespace tiltfive
1531
1533
1536inline std::ostream& operator<<(std::ostream& os, const T5_WandReport& instance) {
1537 // Print the validity flags
1538 os << "[" << (instance.analogValid ? "A" : "_") << (instance.buttonsValid ? "B" : "_")
1539 << (instance.poseValid ? "P" : "_") << "]";
1540
1541 if (instance.analogValid) {
1542 os << "[A: " << std::right << std::fixed << std::setw(10) << instance.stick.x << "x"
1543 << std::right << std::fixed << std::setw(10) << instance.stick.y << " | " << std::right
1544 << std::fixed << std::setw(10) << instance.trigger << "]";
1545 } else {
1546 os << "[A: Invalid]";
1547 }
1548
1549 if (instance.buttonsValid) {
1550 os << "[B: " << (instance.buttons.t5 ? "T" : "_") << (instance.buttons.one ? "1" : "_")
1551 << (instance.buttons.two ? "2" : "_") << (instance.buttons.three ? "3" : "_")
1552 << (instance.buttons.a ? "A" : "_") << (instance.buttons.b ? "B" : "_")
1553 << (instance.buttons.x ? "X" : "_") << (instance.buttons.y ? "Y" : "_") << "]";
1554 } else {
1555 os << "[B: Invalid]";
1556 }
1557
1558 if (instance.poseValid) {
1559 os << "[P: (" << std::right << std::fixed << std::setw(10) << instance.posGrip_STAGE.x
1560 << "," << std::right << std::fixed << std::setw(10) << instance.posGrip_STAGE.y << ","
1561 << std::right << std::fixed << std::setw(10) << instance.posGrip_STAGE.z << ") ("
1562 << std::right << std::fixed << std::setw(10) << instance.rotToWND_STAGE.w << ","
1563 << std::right << std::fixed << std::setw(10) << instance.rotToWND_STAGE.x << ","
1564 << std::right << std::fixed << std::setw(10) << instance.rotToWND_STAGE.y << ","
1565 << std::right << std::fixed << std::setw(10) << instance.rotToWND_STAGE.z << ")"
1566 << "]";
1567 }
1568
1569 return os;
1570}
1571
1575 std::string gameboardType;
1576 switch (instance.gameboardType) {
1578 gameboardType = "None";
1579 break;
1581 gameboardType = "LE";
1582 break;
1584 gameboardType = "XE";
1585 break;
1587 gameboardType = "XE (Raised)";
1588 break;
1589 default:
1590 // Shouldn't happen unless there's some bad casting going on elsewhere.
1591 gameboardType = std::string("[Invalid T5_GameboardType : ") +
1592 std::to_string(static_cast<int>(instance.gameboardType)) +
1593 std::string("]");
1594 break;
1595 }
1596
1597 os << "[" << instance.timestampNanos << "| " << gameboardType << " (" << std::right
1598 << std::fixed << std::setw(10) << instance.posGLS_STAGE.x << "," << std::right << std::fixed
1599 << std::setw(10) << instance.posGLS_STAGE.y << "," << std::right << std::fixed
1600 << std::setw(10) << instance.posGLS_STAGE.z << ") (" << std::right << std::fixed
1601 << std::setw(10) << instance.rotToGLS_STAGE.w << "," << std::right << std::fixed
1602 << std::setw(10) << instance.rotToGLS_STAGE.x << "," << std::right << std::fixed
1603 << std::setw(10) << instance.rotToGLS_STAGE.y << "," << std::right << std::fixed
1604 << std::setw(10) << instance.rotToGLS_STAGE.z << ")"
1605 << "]";
1606
1607 return os;
1608}
1609
1613 std::string primaryGameboardType;
1614 switch (instance.gameboardType) {
1616 primaryGameboardType = "None";
1617 break;
1619 primaryGameboardType = "LE";
1620 break;
1622 primaryGameboardType = "XE";
1623 break;
1625 primaryGameboardType = "XE (Raised)";
1626 break;
1627 default:
1628 // Shouldn't happen unless there's some bad casting going on elsewhere.
1629 primaryGameboardType = std::string("[Invalid T5_GameboardType : ") +
1630 std::to_string(static_cast<int>(instance.gameboardType)) +
1631 std::string("]");
1632 break;
1633 }
1634
1635 os << "[" << instance.timestampNanos << "| " << primaryGameboardType << " (" << std::right
1636 << std::fixed << std::setw(10) << instance.posBOARD_STAGE.x << "," << std::right
1637 << std::fixed << std::setw(10) << instance.posBOARD_STAGE.y << "," << std::right
1638 << std::fixed << std::setw(10) << instance.posBOARD_STAGE.z << ") (" << std::right
1639 << std::fixed << std::setw(10) << instance.rotToBOARD_STAGE.w << "," << std::right
1640 << std::fixed << std::setw(10) << instance.rotToBOARD_STAGE.x << "," << std::right
1641 << std::fixed << std::setw(10) << instance.rotToBOARD_STAGE.y << "," << std::right
1642 << std::fixed << std::setw(10) << instance.rotToBOARD_STAGE.z << ")"
1643 << "]";
1644
1645 return os;
1646}
1647
1650inline std::ostream& operator<<(std::ostream& os, const T5_ParamSys& instance) {
1651 switch (instance) {
1653 os << "Service Version";
1654 break;
1655
1657 os << "UI Attention Required";
1658 break;
1659
1660 default:
1661 // Shouldn't happen unless there's some bad casting going on elsewhere.
1662 os << "[Invalid T5_ParamSys : " << static_cast<int>(instance) << "]";
1663 break;
1664 }
1665
1666 return os;
1667}
1668
1672 switch (instance) {
1674 os << "IPD";
1675 break;
1676
1678 os << "Friendly Name";
1679 break;
1680
1681 default:
1682 // Shouldn't happen unless there's some bad casting going on elsewhere.
1683 os << "[Invalid T5_ParamGlasses : " << static_cast<int>(instance) << "]";
1684 break;
1685 }
1686
1687 return os;
1688}
1689
uint32_t T5_Result
Represents an error code that may be returned by the Tilt Five™ API.
Definition: errors.h:47
C++ errors for the Tilt Five™ API.
C++ Templated common return type for the Tilt Five™ API.
C interface definition for the Tilt Five™ API.
#define T5_ERROR_SETTING_UNKNOWN
The requested param is unknown.
Definition: errors.h:93
#define T5_SUCCESS
Success.
Definition: errors.h:54
#define T5_ERROR_OVERFLOW
Buffer overflow.
Definition: errors.h:102
Error
Error codes returned by most functions of return type Result.
Definition: errors.hpp:37
@ kStringOverflow
String overflow.
@ kTimeout
Timeout.
@ kInvalidArgument
Argument(s) are invalid.
@ kUnavailable
Target is unavailable.
@ kSuccess
Success.
@ kOverflow
Buffer overflow.
@ kTargetNotFound
Target (wand) not found.
@ kInternalError
An internal error occurred.
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.
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 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 t5GetSystemIntegerParam(T5_Context context, T5_ParamSys param, int64_t *value)
Get a system-wide integer parameter.
T5_EXPORT T5_Result t5GetChangedSystemParams(T5_Context context, T5_ParamSys *buffer, uint16_t *count)
Get a system-wide list of changed parameters.
T5_EXPORT T5_Result t5GetDewarpedPixelCoordinate(T5_Glasses glasses, T5_PixelDewarp *pixelCoordinate)
Submit a 2D image coordinate to be dewarped using a specific camera.
T5_EXPORT T5_Result t5CancelCamImageBuffer(T5_Glasses glasses, uint8_t *buffer)
Clear out the remaining buffers and return all buffers as a vector of camera images.
T5_EXPORT T5_Result t5GetFilledCamImageBuffer(T5_Glasses glasses, T5_CamImage *image)
T5_EXPORT T5_Result t5ReleaseGlasses(T5_Glasses glasses)
T5_EXPORT T5_Result t5ConfigureCameraStreamForGlasses(T5_Glasses glasses, T5_CameraStreamConfig config)
Configure the camera stream.
T5_EXPORT T5_Result t5GetProjection(T5_Glasses glasses, T5_CartesianCoordinateHandedness handedness, T5_DepthRange depthRange, T5_MatrixOrder matrixOrder, double nearPlane, double farPlane, double worldScale, T5_ProjectionInfo *projectionInfo)
Get projection properties for glasses.
T5_EXPORT T5_Result t5SubmitEmptyCamImageBuffer(T5_Glasses glasses, T5_CamImage *image)
Submit an empty image buffer to be filled by the camera frame stream.
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 t5EnsureGlassesReady(T5_Glasses glasses)
Ensure that reserved glasses are ready for exclusive operations.
T5_EXPORT T5_Result t5SendFrameToGlasses(T5_Glasses glasses, const T5_FrameInfo *info)
Send a frame to display on the glasses.
T5_EXPORT T5_Result t5GetGameboardPoses(T5_Glasses glasses, T5_GameboardPose *buffer, uint16_t *count)
Get the latest set of gameboard poses.
T5_EXPORT T5_Result t5InitGlassesGraphicsContext(T5_Glasses glasses, T5_GraphicsApi graphicsApi, void *graphicsContext)
Initialize the graphics context to enable sending rendered frames to the 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 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 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 t5SendImpulse(T5_Glasses glasses, T5_WandHandle wand, float amplitude, uint16_t duration)
Send a haptic data buffer to a wand.
T5_EXPORT T5_Result t5ConfigureWandStreamForGlasses(T5_Glasses glasses, const T5_WandStreamConfig *config)
Configure the wand event stream.
auto obtainParamChangeHelper(std::shared_ptr< Client > client, std::weak_ptr< ParamChangeListener > listener, std::chrono::milliseconds pollInterval) -> std::unique_ptr< ParamChangeHelper >
Internal utility function - Do not call directly.
auto obtainGlassesConnectionHelper(std::shared_ptr< Glasses > glasses, const std::string &displayName, std::chrono::milliseconds connectionPollInterval) -> std::unique_ptr< GlassesConnectionHelper >
Internal utility function - Do not call directly.
auto obtainClient(const std::string &applicationId, const std::string &applicationVersion, void *platformContext, const uint8_t sdkType=0) -> Result< std::shared_ptr< Client > >
Obtain an instance of the Tilt Five™ API client.
auto obtainWandStreamHelper(std::shared_ptr< Glasses > glasses, std::chrono::milliseconds pollTimeout) -> std::shared_ptr< WandStreamHelper >
Internal utility function - Do not call directly.
auto obtainGlasses(const std::string &identifier, const std::shared_ptr< Client > &client) -> Result< std::shared_ptr< Glasses > >
Obtain an instance of the Tilt Five™ Glasses.
auto obtainWand(T5_WandHandle handle, std::shared_ptr< WandStreamHelper > wandStreamHelper) -> std::shared_ptr< Wand >
Internal utility function - Do not call directly.
ConnectionState
Represents the exclusivity connection state of glasses.
@ kReserved
Glasses are reserved for exclusive use by this client.
@ kConnected
Glasses are connected for exclusive use by this client.
@ kDisconnected
Glasses were previously connected for exclusive use, but have now disconnected.
@ kNotExclusivelyConnected
Glasses have not been connected for exclusive use. They may still be connected to the host.
std::ostream & operator<<(std::ostream &os, const T5_WandReport &instance)
Support for writing T5_WandReport to an std::ostream.
#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:312
T5_MatrixOrder
Matrix order.
Definition: types.h:107
T5_ParamGlasses
Possible parameters that can be retrieved for glasses.
Definition: types.h:673
uint8_t T5_WandHandle
Opaque handle used with wands.
Definition: types.h:143
T5_DepthRange
Z Depth range.
Definition: types.h:113
struct T5_ContextImpl * T5_Context
Opaque handle used with system-wide functions.
Definition: types.h:131
T5_GraphicsApi
Graphics API types.
Definition: types.h:146
T5_ParamSys
Possible parameters that can be retrieved with System-wide parameters.
Definition: types.h:683
T5_GameboardType
Possible gameboard types.
Definition: types.h:257
struct T5_GlassesImpl * T5_Glasses
Opaque handle used with glasses.
Definition: types.h:137
T5_GlassesPoseUsage
Glasses pose usage indicator.
Definition: types.h:327
T5_CartesianCoordinateHandedness
Handedness of a cartesian coordinate system.
Definition: types.h:122
@ kT5_ConnectionState_Disconnected
Glasses were previously exclusively connected, but the device has disconnected.
Definition: types.h:323
@ kT5_ConnectionState_ExclusiveConnection
Glasses are connected for exclusive use.
Definition: types.h:314
@ kT5_ConnectionState_NotExclusivelyConnected
Glasses have not been exclusively connected or reserved.
Definition: types.h:320
@ kT5_ConnectionState_ExclusiveReservation
Glasses are reserved for exclusive use.
Definition: types.h:317
@ kT5_ParamGlasses_Float_IPD
Interpupillary distance - Float, millimeters
Definition: types.h:676
@ kT5_ParamGlasses_UTF8_FriendlyName
User-facing name of the glasses - UTF8.
Definition: types.h:679
@ kT5_ParamSys_Integer_CPL_AttRequired
Non-zero if the control panel requires user interaction (E.g. Important firmware update) - Integer,...
Definition: types.h:689
@ kT5_ParamSys_UTF8_Service_Version
Version of the service software - UTF8.
Definition: types.h:685
@ kT5_GameboardType_LE
An LE gameboard.
Definition: types.h:262
@ kT5_GameboardType_XE
An XE gameboard, flap laid flat.
Definition: types.h:265
@ kT5_GameboardType_None
No gameboard.
Definition: types.h:259
@ kT5_GameboardType_XE_Raised
An XE gameboard, flap raised at an angle on the kickstand.
Definition: types.h:268
@ kT5_WandStreamEventType_Connect
Wand connected.
Definition: types.h:575
@ kT5_WandStreamEventType_Disconnect
Wand disconnected.
Definition: types.h:578
@ kT5_WandStreamEventType_Report
Wand report (Pose, Buttons, Trigger, Stick, Battery)
Definition: types.h:584
@ kT5_WandStreamEventType_Desync
Stream has desynchronized.
Definition: types.h:581
constexpr pair(piecewise_construct_t, tuple< _Args1... >, tuple< _Args2... >)
size_t start() const
valarray< size_t > size() const
constexpr _OutputIterator transform(_InputIterator __first, _InputIterator __last, _OutputIterator __result, _UnaryOperation __unary_op)
constexpr _OI move(_II __first, _II __last, _OI __result)
void lock(_L1 &__l1, _L2 &__l2, _L3 &... __l3)
insert_iterator< _Container > inserter(_Container &__x, typename _Container::iterator __i)
basic_string< char > string
constexpr auto empty(const _Container &__cont) noexcept(noexcept(__cont.empty())) -> decltype(__cont.empty())
void sleep_for(const chrono::duration< _Rep, _Period > &__rtime)
Templated return type with support for error conditions.
Definition: result.hpp:37
Specialization of tiltfive::Result for functions with 'no return'.
Definition: result.hpp:199
Client for communicating with the Tilt Five™ API.
auto getServiceVersion() -> Result< std::string >
Get the version of the Tilt Five™ service.
auto getGameboardSize(T5_GameboardType type) -> Result< T5_GameboardSize >
Obtain the dimensions of the gameboard.
auto getChangedParams() -> Result< std::vector< T5_ParamSys > >
Get a system-wide list of changed parameters.
friend auto obtainClient(const std::string &applicationId, const std::string &applicationVersion, void *platformContext, const uint8_t sdkType) -> Result< std::shared_ptr< Client > >
Obtain an instance of the Tilt Five™ API client.
auto isTiltFiveUiRequestingAttention() -> Result< bool >
Check if the Tilt Five™ UI wants the users attention.
auto listGlasses() -> Result< std::vector< std::string > >
Enumerate glasses.
auto getHandle() const -> T5_Context
Obtain the C handle for this client.
auto createParamChangedHelper(std::weak_ptr< ParamChangeListener > listener, std::chrono::milliseconds pollInterval=std::chrono::milliseconds(100)) -> std::unique_ptr< ParamChangeHelper >
Create a ParamChangeHelper.
Represents an instance of Tilt Five™ glasses.
auto initGraphicsContext(T5_GraphicsApi graphicsApi, void *graphicsContext) -> Result< void >
Initialize the glasses for graphics operations.
auto getIpd() -> Result< double >
Get the current IPD setting for this glasses.
auto getHandle() const -> T5_Glasses
Obtain the C handle for this glasses object.
auto getLatestGameboardPoses() -> Result< std::vector< T5_GameboardPose > >
Get the latest set of gameboard poses for this glasses.
auto getFilledCamImageBuffer() -> Result< T5_CamImage >
Get the latest camera image for this glasses.
auto getChangedParams() -> Result< std::vector< T5_ParamGlasses > >
Get a system-wide list of changed parameters.
auto release() -> Result< void >
Release previously-reserved glasses.
auto submitEmptyCamImageBuffer(T5_CamImage *imgBuffer) -> Result< void >
Submit a buffer to the camera image stream to hold Camera Frame data.
auto sendFrame(const T5_FrameInfo *const frameInfo) -> Result< void >
Send a frame to display on the glasses.
auto configureWandStream(const T5_WandStreamConfig *const config) -> Result< void >
Configure the wand event stream.
auto getDewarpedPixelCoordinate(T5_PixelDewarp *pixelCoordinate) -> Result< void >
Submit a pixel coordinate to have the dewarped image location calculated.
auto getLatestGlassesPose(T5_GlassesPoseUsage usage) -> Result< T5_GlassesPose >
Get the latest pose for this glasses.
friend auto obtainGlasses(const std::string &identifier, const std::shared_ptr< Client > &client) -> Result< std::shared_ptr< Glasses > >
Obtain an instance of the Tilt Five™ Glasses.
auto getProjectionInfo(T5_CartesianCoordinateHandedness handedness, T5_DepthRange depthRange, T5_MatrixOrder matrixOrder, double nearPlane, double farPlane, double worldScale) -> Result< T5_ProjectionInfo >
Get projection properties for glasses.
auto cancelCamImageBuffer(uint8_t *buffer) -> Result< void >
Cancel an image buffer in use by the service for freeing.
auto sendImpulse(T5_WandHandle handle, float amplitude, uint16_t duration) -> Result< void >
Send a Haptic Impulse to a wand.
auto readWandStream(std::chrono::milliseconds timeout=std::chrono::milliseconds(100)) -> Result< T5_WandStreamEvent >
Read from the wands event stream.
auto reserve(const std::string &displayName) -> Result< void >
Reserve glasses for exclusive operations by the client.
auto configureCameraStream(T5_CameraStreamConfig config) -> Result< void >
Configure the wand event stream.
auto getConnectionState() -> Result< ConnectionState >
Get the current connection state of the glasses.
auto listWands() -> Result< std::vector< T5_WandHandle > >
Obtain a list of connected wands.
auto createConnectionHelper(const std::string &displayName, std::chrono::milliseconds connectionPollInterval=std::chrono::milliseconds(100)) -> std::unique_ptr< GlassesConnectionHelper >
Create a GlassesConnectionHelper.
auto getWandStreamHelper() -> std::shared_ptr< WandStreamHelper >
Get a WandStreamHelper.
auto getIdentifier() const -> std::string
Obtain a hardware (not user facing) identifier for the glasses.
auto ensureReady() -> Result< void >
Ensure that reserved glasses are ready for exclusive operations.
auto getFriendlyName() -> Result< std::string >
Get the user-facing name of the glasses.
Utility class to automate the Glasses exclusive connection process.
auto awaitConnection(const std::chrono::milliseconds timeout) -> Result< void >
Block until a connection is established or timed out.
auto awaitConnection() -> Result< void >
Block until a connection is established.
auto glasses() -> Glasses &
Obtain a reference to the wrapped tiltfive::Glasses object.
friend auto obtainGlassesConnectionHelper(std::shared_ptr< Glasses > glasses, const std::string &displayName, std::chrono::milliseconds connectionPollInterval) -> std::unique_ptr< GlassesConnectionHelper >
Internal utility function - Do not call directly.
auto consumeLastAsyncError() -> std::error_code
Obtain and consume the last asynchronous error.
Utility class to manage the wand stream.
auto listWands() -> Result< std::vector< std::shared_ptr< Wand > > >
Obtain a list of tiltfive::Wand.
friend auto obtainWandStreamHelper(std::shared_ptr< Glasses > glasses, std::chrono::milliseconds pollTimeout) -> std::shared_ptr< WandStreamHelper >
Internal utility function - Do not call directly.
auto consumeLastAsyncError() -> std::error_code
Obtain and consume the last asynchronous error.
auto sendImpulse(const T5_WandHandle &handle, float amplitude, uint16_t duration) -> Result< void >
Send a haptic impulse to a specific tiltfive::Wand.
Virtual base class for use with tiltfive::ParamChangeHelper.
virtual auto onSysParamChanged(const std::vector< T5_ParamSys > &changed) -> void=0
Called by a tiltfive::ParamChangeHelper when system-wide (T5_ParamSys) params have changed.
virtual auto onGlassesParamChanged(const std::shared_ptr< Glasses > &glasses, const std::vector< T5_ParamGlasses > &changed) -> void=0
Called by a tiltfive::ParamChangeHelper when glasses specific (T5_ParamGlasses) params have changed.
Utility class to track changes to parameters Using ParamChangeHelper.
auto registerGlasses(const std::shared_ptr< Glasses > &glasses) -> void
Register glasses for parameter change tracking.
friend auto obtainParamChangeHelper(std::shared_ptr< Client > client, std::weak_ptr< ParamChangeListener > listener, std::chrono::milliseconds pollInterval) -> std::unique_ptr< ParamChangeHelper >
Internal utility function - Do not call directly.
auto deregisterGlasses(const std::shared_ptr< Glasses > &glasses) -> void
De-register glasses for parameter change tracking.
auto consumeLastAsyncError() -> std::error_code
Obtain and consume the last asynchronous error.
Represents an abstract instance of a Tilt Five™ wand Used with tiltfive::WandStreamHelper.
T5_WandHandle handle() const
Get the wand handle.
friend auto obtainWand(T5_WandHandle handle, std::shared_ptr< WandStreamHelper > wandStreamHelper) -> std::shared_ptr< Wand >
Internal utility function - Do not call directly.
auto getLatestReport() const -> Result< T5_WandReport >
Get the latest wand report for this wand.
auto sendImpulse(float amplitude, uint16_t duration) const -> Result< void >
Send an impulse to this wand.
Physical dimensions of a gameboard.
Definition: types.h:272
Client provided information for use with t5CreateGlasses()
Definition: types.h:295
uint8_t sdkType
The SDK type.
Definition: types.h:305
const char * applicationId
The application ID.
Definition: types.h:297
const char * applicationVersion
The application version.
Definition: types.h:300
Glasses pose information to be retrieved with t5GetGlassesPose()
Definition: types.h:358
uint64_t timestampNanos
The timestamp of the pose.
Definition: types.h:360
T5_GameboardType gameboardType
The type of gameboard visible for this pose.
Definition: types.h:371
T5_Quat rotToGLS_STAGE
The rotation that transforms points in the STAGE frame orientation to the GLS (glasses) frame orienta...
Definition: types.h:368
T5_Vec3 posGLS_STAGE
The position of the origin of the GLS (glasses) frame relative to the STAGE frame.
Definition: types.h:364
Gameboard pose information to be retrieved by enumerating the output of t5GetGameboardPoses()
Definition: types.h:392
T5_Vec3 posBOARD_STAGE
The position of the origin of the BOARD (gameboard) frame relative to the STAGE frame.
Definition: types.h:398
T5_Quat rotToBOARD_STAGE
The rotation that transforms points in the STAGE frame orientation to the BOARD (gameboard) frame ori...
Definition: types.h:402
T5_GameboardType gameboardType
The type of gameboard represented by this pose.
Definition: types.h:405
uint64_t timestampNanos
The timestamp of the pose.
Definition: types.h:394
Camera stream configuration.
Definition: types.h:412
Render information to be used with t5SendFrameToGlasses()
Definition: types.h:421
Camera Frame information to be retrieved with t5GetFilledCamImageBuffer()
Definition: types.h:513
Wand stream configuration.
Definition: types.h:566
Contains wand related information (Pose, Buttons, Trigger, Stick, Battery)
Definition: types.h:600
bool poseValid
Validity of pose parameters. True = valid.
Definition: types.h:614
T5_Vec3 posGrip_STAGE
Position (Grip) - Vector3f.
Definition: types.h:651
float trigger
Trigger - Analog, Range [0.0 - 1.0], 1.0 = Fully depressed.
Definition: types.h:617
bool analogValid
Validity of analog parameters. True = valid.
Definition: types.h:605
bool buttonsValid
Validity of button parameters. True = valid.
Definition: types.h:611
T5_Quat rotToWND_STAGE
WND/STAGE rotation unit quaternion.
Definition: types.h:642
T5_Vec2 stick
Stick (X/Y) - Analog, Range [-1.0 - 1.0], 0 = Centered, 1.0 = Top/Right.
Definition: types.h:620
struct T5_WandReport::@7 buttons
Buttons state. True = Pressed.
Represents an event from the wand stream.
Definition: types.h:658
Projection parameters.
Definition: types.h:693
const char * c_str() const noexcept
size_type length() const noexcept
void insert(_InputIterator __first, _InputIterator __last)
const_iterator end() const noexcept
const_iterator cend() const noexcept
size_type erase(const key_type &__x)
iterator find(const key_type &__x)
const_iterator cbegin() const noexcept
iterator end() const noexcept
void insert(_InputIterator __first, _InputIterator __last)
size_type erase(const key_type &__x)
void resize(size_type __new_size)
void push_back(const value_type &__x)
void reserve(size_type __n)
_Tp * data() noexcept
size_type size() const noexcept
pointer get() const noexcept