Tilt Five NDK  1.4.1
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 std::vector<char> buffer;
166 buffer.resize(64);
167
168 size_t bufferSize;
169
170 // If the buffer passed to listGlasses() is too small, it'll return with
171 // an overflow condition, in which case, increase the size of the buffer
172 // and try again.
173 for (;;) {
174 bufferSize = buffer.size();
175 T5_Result err = t5ListGlasses(mContext, buffer.data(), &bufferSize);
176 if (!err) {
177 break;
178 } else if (err == T5_ERROR_OVERFLOW) {
179 if (bufferSize > 1024) {
180 return Error::kOverflow;
181 }
182
183 buffer.resize(bufferSize);
184 } else {
185 return static_cast<Error>(err);
186 }
187 }
188
189 // Peel off string until we encounter a naked null (empty string)
190 std::vector<std::string> glassesList;
191 auto buffPtr = buffer.data();
192 for (;;) {
193 std::string id = buffPtr;
194 if (id.empty()) {
195 break;
196 }
197
198 buffPtr += id.length() + 1;
199 glassesList.push_back(id);
200 }
201 return glassesList;
202 }
203
211 T5_Result err =
213 if (!err) {
214 return std::string(value.get(), size);
215 } else {
216 return static_cast<Error>(err);
217 }
218 }
219
228 uint16_t changedParamsCount = 32;
229 std::vector<T5_ParamSys> changedParamsBuffer(changedParamsCount);
230
231 changedParamsBuffer.resize(changedParamsCount);
232 T5_Result err =
233 t5GetChangedSystemParams(mContext, changedParamsBuffer.data(), &changedParamsCount);
234
235 for (;;) {
236 if (!err) {
237 changedParamsBuffer.resize(changedParamsCount);
238 return changedParamsBuffer;
239
240 } else if (err == T5_ERROR_OVERFLOW) {
241 changedParamsCount = changedParamsBuffer.size() * 2;
242 continue;
243
244 } else {
245 return static_cast<Error>(err);
246 }
247 }
248 }
249
262 int64_t value = 0;
263
264 T5_Result err =
266 if (!err) {
267 return value != 0;
268 } else if (err == T5_ERROR_SETTING_UNKNOWN) {
269 return false;
270 } else {
271 return static_cast<Error>(err);
272 }
273 }
274
281
282 T5_Result err = t5GetGameboardSize(mContext, type, &size);
283 if (!err) {
284 return size;
285 } else {
286 return static_cast<Error>(err);
287 }
288 }
289
297 [[nodiscard]] auto createParamChangedHelper(
301
302 return obtainParamChangeHelper(shared_from_this(), std::move(listener), pollInterval);
303 }
304};
305
307enum class ConnectionState : int {
311
313 kReserved,
314
317
320};
321
323class Glasses : public std::enable_shared_from_this<Glasses> {
324protected:
325 friend Client;
326 friend ParamChangeHelper;
327
328 const std::string mIdentifier;
329 const std::shared_ptr<Client> mClient;
330 std::weak_ptr<WandStreamHelper> mWandStreamHelper{};
331 T5_Glasses mGlasses{};
332
333 friend std::ostream& operator<<(std::ostream& os, std::shared_ptr<Glasses> const& instance) {
334 os << *instance;
335 return os;
336 }
337
338 friend std::ostream& operator<<(std::ostream& os, Glasses const& instance) {
339 os << "<Glasses:" << instance.mIdentifier << ">";
340 return os;
341 }
342
343 friend auto obtainGlasses(const std::string& identifier, const std::shared_ptr<Client>& client)
345
346 explicit Glasses(std::string identifier, std::shared_ptr<Client> client)
347 : mIdentifier(std::move(identifier)), mClient(std::move(client)) {}
348
349 static auto create(std::string identifier, std::shared_ptr<Client> client)
351
352 if (!client) {
354 }
355
356 T5_Glasses handle;
357 T5_Result err = t5CreateGlasses(client->mContext, identifier.c_str(), &handle);
358
359 if (err) {
360 return static_cast<Error>(err);
361 }
362
363 std::shared_ptr<Glasses> glasses{new Glasses(identifier, client)};
364
365 glasses->mGlasses = handle;
366
367 return glasses;
368 }
369
370public:
377 [[nodiscard]] auto getHandle() const -> T5_Glasses {
378 return mGlasses;
379 }
380
390 [[nodiscard]] auto getIdentifier() const -> std::string {
391 return mIdentifier;
392 }
393
398 T5_ConnectionState connectionState;
399 T5_Result err = t5GetGlassesConnectionState(mGlasses, &connectionState);
400 if (err != T5_SUCCESS) {
401 return static_cast<Error>(err);
402 }
403
404 switch (connectionState) {
407
410
413
416
417 default:
419 }
420 }
421
430 uint16_t changedParamsCount = 32;
431 std::vector<T5_ParamGlasses> changedParamsBuffer(changedParamsCount);
432
433 changedParamsBuffer.resize(changedParamsCount);
434 T5_Result err =
435 t5GetChangedGlassesParams(mGlasses, changedParamsBuffer.data(), &changedParamsCount);
436
437 for (;;) {
438 if (!err) {
439 changedParamsBuffer.resize(changedParamsCount);
440 return changedParamsBuffer;
441
442 } else if (err == T5_ERROR_OVERFLOW) {
443 changedParamsCount = changedParamsBuffer.size() * 2;
444 continue;
445
446 } else {
447 return static_cast<Error>(err);
448 }
449 }
450 }
451
460 double value = 0;
462 if (!err) {
463 return value;
464 } else {
465 return static_cast<Error>(err);
466 }
467 }
468
481 mGlasses, 0, kT5_ParamGlasses_UTF8_FriendlyName, value.get(), &size);
482 if (!err) {
483 return std::string(value.get());
484 } else {
485 return static_cast<Error>(err);
486 }
487 }
488
509 auto reserve(const std::string& displayName) -> Result<void> {
510 T5_Result err = t5ReserveGlasses(mGlasses, displayName.c_str());
511 if (!err) {
512 return kSuccess;
513 } else {
514 return static_cast<Error>(err);
515 }
516 }
517
533 T5_Result err = t5EnsureGlassesReady(mGlasses);
534 if (!err) {
535 return kSuccess;
536 } else {
537 return static_cast<Error>(err);
538 }
539 }
540
549 T5_Result err = t5ReleaseGlasses(mGlasses);
550 if (!err) {
551 return kSuccess;
552 } else {
553 return static_cast<Error>(err);
554 }
555 }
556
563 T5_GlassesPose pose;
564 T5_Result err = t5GetGlassesPose(mGlasses, usage, &pose);
565
566 if (!err) {
567 return pose;
568 } else {
569 return static_cast<Error>(err);
570 }
571 }
572
577 auto initGraphicsContext(T5_GraphicsApi graphicsApi, void* graphicsContext) -> Result<void> {
578 T5_Result err = t5InitGlassesGraphicsContext(mGlasses, graphicsApi, graphicsContext);
579 if (!err) {
580 return kSuccess;
581 }
582 return static_cast<Error>(err);
583 }
584
589 T5_Result err = t5ConfigureCameraStreamForGlasses(mGlasses, config);
590 if (!err) {
591 return kSuccess;
592 } else {
593 return static_cast<Error>(err);
594 }
595 }
596
598 //
601 T5_CamImage img;
602 T5_Result err = t5GetFilledCamImageBuffer(mGlasses, &img);
603 if (!err) {
604 return std::move(img);
605 } else {
606 return static_cast<Error>(err);
607 }
608 }
609
611 //
614 T5_Result err = t5SubmitEmptyCamImageBuffer(mGlasses, imgBuffer);
615 if (!err) {
616 return kSuccess;
617 } else {
618 return static_cast<Error>(err);
619 }
620 }
621
623 //
626 auto cancelCamImageBuffer(uint8_t* buffer) -> Result<void> {
627 T5_Result err = t5CancelCamImageBuffer(mGlasses, buffer);
628 if (!err) {
629 return kSuccess;
630 } else {
631 return static_cast<Error>(err);
632 }
633 }
634
638 auto sendFrame(const T5_FrameInfo* const frameInfo) -> Result<void> {
639 T5_Result err = t5SendFrameToGlasses(mGlasses, frameInfo);
640 if (!err) {
641 return kSuccess;
642 } else {
643 return static_cast<Error>(err);
644 }
645 }
646
652 auto sendImpulse(T5_WandHandle handle, float amplitude, uint16_t duration) -> Result<void> {
653 T5_Result err = t5SendImpulse(mGlasses, handle, amplitude, duration);
654 if (!err) {
655 return kSuccess;
656 } else {
657 return static_cast<Error>(err);
658 }
659 }
660
665 uint8_t wandCount = 4;
666 std::vector<T5_WandHandle> wandBuffer(wandCount);
667
668 for (;;) {
669 wandBuffer.resize(wandCount);
670 T5_Result err = t5ListWandsForGlasses(mGlasses, wandBuffer.data(), &wandCount);
671
672 if (!err) {
674 wands.reserve(wandCount);
675
676 for (auto i = 0; i < wandCount; i++) {
677 wands.push_back(wandBuffer[i]);
678 }
679
680 return wands;
681
682 } else if (err == T5_ERROR_OVERFLOW) {
683 wandCount = wandBuffer.size() * 2;
684 continue;
685
686 } else {
687 return static_cast<Error>(err);
688 }
689 }
690 }
691
696 T5_Result err = t5ConfigureWandStreamForGlasses(mGlasses, config);
697 if (!err) {
698 return kSuccess;
699 } else {
700 return static_cast<Error>(err);
701 }
702 }
703
720 T5_WandStreamEvent event;
721
722 T5_Result err = t5ReadWandStreamForGlasses(mGlasses, &event, timeout.count());
723 if (!err) {
724 return event;
725 } else {
726 return static_cast<Error>(err);
727 }
728 }
729
735 auto wandStreamHelper = mWandStreamHelper.lock();
736 if (!wandStreamHelper) {
737 // needs initialization
738 wandStreamHelper = obtainWandStreamHelper(shared_from_this());
739 mWandStreamHelper = wandStreamHelper;
740 }
741 return wandStreamHelper;
742 }
743
752 const std::string& displayName,
753 std::chrono::milliseconds connectionPollInterval = std::chrono::milliseconds(100))
755
757 shared_from_this(), displayName, connectionPollInterval);
758 }
759
761 virtual ~Glasses() {
762 // Disconnect the glasses if they're connected
763 auto connectionState = getConnectionState();
764 if (!connectionState) {
765 return;
766 }
767
768 if (mGlasses) {
769 t5DestroyGlasses(&mGlasses);
770 mGlasses = nullptr;
771 }
772 }
774};
775
780private:
781 const std::shared_ptr<Glasses> mGlasses;
782 const std::string mDisplayName;
783 const std::chrono::milliseconds mConnectionPollInterval;
784 const std::chrono::milliseconds mConnectedPollInterval = mConnectionPollInterval * 10;
785
786 std::atomic<bool> mRunning{true};
787 std::thread mThread;
788
789 std::mutex mLastAsyncErrorMtx;
790 std::atomic<std::error_code> mLastAsyncError{};
791
792 void setLastAsyncError(std::error_code err) {
793 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
794 mLastAsyncError = err;
795 }
796
797 void threadMain() {
798 while (mRunning) {
799 auto connectionState = mGlasses->getConnectionState();
800 if (!connectionState) {
801 setLastAsyncError(connectionState.error());
802 std::this_thread::sleep_for(mConnectionPollInterval);
803 continue;
804 }
805
806 switch (*connectionState) {
808 // Attempt to connect
809 auto result = mGlasses->reserve(mDisplayName);
810 if (!result) {
811 setLastAsyncError(result.error());
812 }
813 // No action on success - the next call to getConnectionState() will
814 // detect the change
815
816 break;
817 }
818
821 auto result = mGlasses->ensureReady();
822 if (!result) {
823 setLastAsyncError(result.error());
824 }
825 // No action on success - the next call to getConnectionState() will
826 // detect the change
827
828 break;
829 }
830
832 // If we're connected, increase polling interval to reduce excessive
833 // connections state queries (at the expense of slowing detection of
834 // disconnected devices).
835 std::this_thread::sleep_for(mConnectedPollInterval);
836 break;
837 }
838
839 std::this_thread::sleep_for(mConnectionPollInterval);
840 }
841 }
842
844 const std::string& displayName,
845 std::chrono::milliseconds connectionPollInterval)
847
849 std::string displayName,
850 std::chrono::milliseconds connectionPollInterval)
851 : mGlasses(std::move(glasses))
852 , mDisplayName{std::move(displayName)}
853 , mConnectionPollInterval(connectionPollInterval) {
854
855 mThread = std::thread(&GlassesConnectionHelper::threadMain, this);
856 }
857
858public:
860 [[nodiscard]] auto glasses() -> Glasses& {
861 return *mGlasses;
862 }
863
866 auto connectionState = mGlasses->getConnectionState();
867 if (!connectionState) {
868 return connectionState.error();
869 }
870
871 while (*connectionState != ConnectionState::kConnected) {
872 std::this_thread::sleep_for(mConnectionPollInterval);
873
874 connectionState = mGlasses->getConnectionState();
875 if (!connectionState) {
876 return connectionState.error();
877 }
878 }
879
880 return kSuccess;
881 }
882
887 auto start = std::chrono::steady_clock::now();
888
889 auto connectionState = mGlasses->getConnectionState();
890 if (!connectionState) {
891 return connectionState.error();
892 }
893
894 while (*connectionState != ConnectionState::kConnected) {
895 if ((std::chrono::steady_clock::now() - start) > timeout) {
896 return Error::kTimeout;
897 }
898
899 std::this_thread::sleep_for(mConnectionPollInterval);
900
901 connectionState = mGlasses->getConnectionState();
902 if (!connectionState) {
903 return connectionState.error();
904 }
905 }
906
907 return kSuccess;
908 }
909
917 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
918 return mLastAsyncError.exchange({});
919 }
920
922 virtual ~GlassesConnectionHelper() {
923 mRunning = false;
924 if (mThread.joinable()) {
925 mThread.join();
926 }
927 }
929};
930
938class WandStreamHelper : public std::enable_shared_from_this<WandStreamHelper> {
939private:
940 friend Wand;
941
942 const std::shared_ptr<Glasses> mGlasses;
943 const std::chrono::milliseconds mPollTimeout;
944
945 std::atomic<bool> mWandListDirty{true};
946 std::mutex mWandListMtx; // guards access to mWandList
948
949 std::atomic<bool> mRunning{true};
950 std::thread mThread;
951
952 std::mutex mLastWandReportsMtx; // guards access to mLastWandReports
954
955 std::mutex mLastAsyncErrorMtx;
956 std::atomic<std::error_code> mLastAsyncError{};
957
958 void setLastAsyncError(std::error_code err) {
959 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
960 mLastAsyncError = err;
961 }
962
963 auto drainStream(const std::shared_ptr<Glasses>& glasses) -> Result<void> {
964 while (mRunning) {
965 auto result = glasses->readWandStream(mPollTimeout);
966 if (!result) {
967 return result.error();
968 }
969
970 std::lock_guard<std::mutex> lock{mLastWandReportsMtx};
971
972 // Process the event
973 switch (result->type) {
975 mLastWandReports[result->wandId] = {};
976 mWandListDirty = true;
977 break;
978
980 mLastWandReports.erase(result->wandId);
981 mWandListDirty = true;
982 break;
983
985 mWandListDirty = true;
986 break;
987
989 mLastWandReports[result->wandId] = result->report;
990 break;
991 }
992 }
993
994 return Error::kUnavailable;
995 }
996
997 // Update the reports map based on the latest wand list.
998 // Ensure empty reports are populated for newly-connected wands.
999 // Remove reports for wands that are no longer connected.
1000 //
1001 // PRECONDITIONS: Wand list mutex must be held.
1002 auto refreshReports() -> void {
1003 std::lock_guard<std::mutex> lock{mLastWandReportsMtx};
1004
1005 // Obtain a set of the wand handles held in mLastWandReports
1006 std::set<T5_WandHandle> lastWandReportKeys;
1007 std::transform(mLastWandReports.cbegin(),
1008 mLastWandReports.cend(),
1009 std::inserter(lastWandReportKeys, lastWandReportKeys.end()),
1010 [](std::pair<T5_WandHandle, T5_WandReport> pair) { return pair.first; });
1011
1012 // Remove from the list all connected wands and add empty reports for new wands.
1013 for (const auto& connectedWand : mWandList) {
1014 lastWandReportKeys.erase(connectedWand);
1015 mLastWandReports.insert({connectedWand, T5_WandReport{}});
1016 }
1017
1018 // The remainder of the list is wand reports for disconnected wands - remove them
1019 for (auto defunctKey : lastWandReportKeys) {
1020 mLastWandReports.erase(defunctKey);
1021 }
1022 }
1023
1024 void threadMain() {
1025 T5_WandStreamConfig streamConfig{true};
1026 bool configured = false;
1027
1028 while (mRunning) {
1029 // Configure the stream if we haven't already
1030 if (!configured) {
1031 auto configureRequest = mGlasses->configureWandStream(&streamConfig);
1032 if (!configureRequest) {
1033 setLastAsyncError(configureRequest.error());
1035 continue;
1036 }
1037 configured = true;
1038 }
1039
1040 // Drain the stream
1041 auto result = drainStream(mGlasses);
1042 if ((result.error() != tiltfive::Error::kTimeout) &&
1043 (result.error() != tiltfive::Error::kUnavailable)) {
1044
1045 // For errors other than timeout, record it, small delay and loop
1046 setLastAsyncError(result.error());
1048 }
1049 }
1050
1051 // Disable the stream
1052 streamConfig.enabled = false;
1053 auto configureRequest = mGlasses->configureWandStream(&streamConfig);
1054 if (!configureRequest) {
1055 setLastAsyncError(configureRequest.error());
1056 }
1057
1058 // Flag as no longer running if we've exited due to error
1059 mRunning = false;
1060 }
1061
1062 friend inline auto obtainWandStreamHelper(std::shared_ptr<Glasses> glasses,
1063 std::chrono::milliseconds pollTimeout)
1065
1066 explicit WandStreamHelper(
1069 : mGlasses(std::move(glasses)), mPollTimeout(pollTimeout) {
1070
1071 mThread = std::thread(&WandStreamHelper::threadMain, this);
1072 }
1073
1074 auto getLatestReport(const T5_WandHandle& handle) -> Result<T5_WandReport> {
1075 std::lock_guard<std::mutex> lock{mLastWandReportsMtx};
1076
1077 auto report = mLastWandReports.find(handle);
1078 if (report == mLastWandReports.end()) {
1080 }
1081
1082 return report->second;
1083 };
1084
1085public:
1094 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
1095 return mLastAsyncError.exchange({});
1096 }
1097
1105 std::lock_guard<std::mutex> lock{mWandListMtx};
1106
1107 // Update the wand list if it's been invalidated
1108 if (mWandListDirty.exchange(false)) {
1109 auto result = mGlasses->listWands();
1110 if (!result) {
1111 mWandListDirty = true;
1112 return result.error();
1113 }
1114
1115 std::vector<T5_WandHandle> wandHandles;
1116 for (auto wandHandle : *result) {
1117 wandHandles.push_back(wandHandle);
1118 }
1119 mWandList = wandHandles;
1120
1121 refreshReports();
1122 }
1123
1124 // Realize wand list
1126 for (auto wandHandle : mWandList) {
1127 wands.push_back(obtainWand(wandHandle, shared_from_this()));
1128 }
1129
1130 return wands;
1131 };
1132
1138 auto sendImpulse(const T5_WandHandle& handle, float amplitude, uint16_t duration)
1139 -> Result<void> {
1140 auto result = mGlasses->sendImpulse(handle, amplitude, duration);
1141 if (!result) {
1142 return result.error();
1143 }
1144
1145 return kSuccess;
1146 }
1147
1149 virtual ~WandStreamHelper() {
1150 mRunning = false;
1151 if (mThread.joinable()) {
1152 mThread.join();
1153 }
1154 }
1156};
1157
1160public:
1163 virtual auto onSysParamChanged(const std::vector<T5_ParamSys>& changed) -> void = 0;
1164
1168 const std::vector<T5_ParamGlasses>& changed) -> void = 0;
1169
1171 virtual ~ParamChangeListener() = default;
1173};
1174
1178private:
1179 const std::shared_ptr<Client> mClient;
1180 const std::weak_ptr<ParamChangeListener> mChangeListener;
1181
1182 static constexpr size_t kDefaultSettingBufferSize = 16;
1183
1184 std::mutex mRegisteredGlassesMtx;
1185 std::set<std::shared_ptr<Glasses>> mRegisteredGlasses;
1186
1187 std::vector<T5_ParamSys> mChangedSysParams;
1188 std::vector<T5_ParamGlasses> mChangedGlassesParams;
1189
1190 std::chrono::milliseconds mPollInterval;
1191
1192 std::thread mThread;
1193 std::atomic<bool> mRunning{true};
1194
1195 std::mutex mLastAsyncErrorMtx;
1196 std::atomic<std::error_code> mLastAsyncError{};
1197
1198 void setLastAsyncError(std::error_code err) {
1199 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
1200 mLastAsyncError = err;
1201 }
1202
1205 std::chrono::milliseconds pollInterval)
1207
1210 std::chrono::milliseconds pollInterval)
1211 : mClient(std::move(client))
1212 , mChangeListener(std::move(listener))
1213 , mPollInterval(pollInterval) {
1214
1215 mThread = std::thread(&ParamChangeHelper::threadMain, this);
1216 }
1217
1218 auto checkGlassesParams(const std::shared_ptr<ParamChangeListener>& listener) -> void {
1219 std::lock_guard<std::mutex> lock(mRegisteredGlassesMtx);
1220 for (const auto& glasses : mRegisteredGlasses) {
1221 checkGlassesParams(glasses, listener);
1222 }
1223 }
1224
1225 auto checkGlassesParams(const std::shared_ptr<Glasses>& glasses,
1226 const std::shared_ptr<ParamChangeListener>& listener) -> void {
1227 uint16_t changeCount;
1228
1229 mChangedGlassesParams.resize(kDefaultSettingBufferSize);
1230 for (;;) {
1231 changeCount = mChangedGlassesParams.size();
1233 glasses->mGlasses, mChangedGlassesParams.data(), &changeCount);
1234
1235 if (!err) {
1236 if (changeCount > 0) {
1237 mChangedGlassesParams.resize(changeCount);
1238 listener->onGlassesParamChanged(glasses, mChangedGlassesParams);
1239 }
1240 break;
1241 }
1242
1243 // Error - increase buffer if we overflowed, or record the error and exit
1244 if (err == T5_ERROR_OVERFLOW) {
1245 mChangedSysParams.resize(mChangedSysParams.size() * 2);
1246 continue;
1247 }
1248
1249 setLastAsyncError(static_cast<Error>(err));
1250 break;
1251 }
1252 }
1253
1254 auto checkSysParams(const std::shared_ptr<ParamChangeListener>& listener) -> void {
1255 uint16_t changeCount;
1256
1257 mChangedSysParams.resize(kDefaultSettingBufferSize);
1258 for (;;) {
1259 changeCount = mChangedSysParams.size();
1260 T5_Result err =
1261 t5GetChangedSystemParams(mClient->mContext, mChangedSysParams.data(), &changeCount);
1262
1263 if (!err) {
1264 if (changeCount > 0) {
1265 mChangedSysParams.resize(changeCount);
1266 listener->onSysParamChanged(mChangedSysParams);
1267 }
1268 break;
1269 }
1270
1271 // Error - increase buffer if we overflowed, or record the error and exit
1272 if (err == T5_ERROR_OVERFLOW) {
1273 mChangedSysParams.resize(mChangedSysParams.size() * 2);
1274 continue;
1275 }
1276
1277 setLastAsyncError(static_cast<Error>(err));
1278 break;
1279 }
1280 }
1281
1282 auto threadMain() -> void {
1283 while (mRunning) {
1284 // Listener weak_ptr -> shared_ptr or exit
1285 {
1286 auto listener = mChangeListener.lock();
1287 if (!listener) {
1288 break;
1289 }
1290
1291 checkGlassesParams(listener);
1292
1293 checkSysParams(listener);
1294 }
1295
1296 std::this_thread::sleep_for(mPollInterval);
1297 }
1298 }
1299
1300public:
1302 virtual ~ParamChangeHelper() {
1303 mRunning = false;
1304 if (mThread.joinable()) {
1305 mThread.join();
1306 }
1307 }
1309
1318 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
1319 return mLastAsyncError.exchange({});
1320 }
1321
1323 auto registerGlasses(const std::shared_ptr<Glasses>& glasses) -> void {
1324 std::lock_guard<std::mutex> lock(mRegisteredGlassesMtx);
1325 mRegisteredGlasses.insert(glasses);
1326 }
1327
1329 auto deregisterGlasses(const std::shared_ptr<Glasses>& glasses) -> void {
1330 std::lock_guard<std::mutex> lock(mRegisteredGlassesMtx);
1331 mRegisteredGlasses.erase(glasses);
1332 }
1333};
1334
1337class Wand {
1338protected:
1339 T5_WandHandle mHandle;
1340 std::shared_ptr<WandStreamHelper> mWandStreamHelper;
1341
1342 friend WandStreamHelper;
1343
1344 friend std::ostream& operator<<(std::ostream& os, std::shared_ptr<Wand> const& instance) {
1345 os << *instance;
1346 return os;
1347 }
1348
1349 friend std::ostream& operator<<(std::ostream& os, Wand const& instance) {
1350 os << "<Wand:" << +instance.mHandle << ">";
1351 return os;
1352 }
1353
1356
1358 : mHandle(handle), mWandStreamHelper(std::move(wandStreamHelper)) {}
1359
1360public:
1363 return mWandStreamHelper->getLatestReport(mHandle);
1364 }
1365
1369 auto sendImpulse(float amplitude, uint16_t duration) const -> Result<void> {
1370 return mWandStreamHelper->sendImpulse(mHandle, amplitude, duration);
1371 }
1372
1374 [[nodiscard]] T5_WandHandle handle() const {
1375 return mHandle;
1376 }
1377};
1378
1388inline auto obtainClient(const std::string& applicationId,
1389 const std::string& applicationVersion,
1390 void* platformContext,
1391 const uint8_t sdkType = 0) -> Result<std::shared_ptr<Client>> {
1392
1393 return Client::create(applicationId, applicationVersion, platformContext, sdkType);
1394}
1395
1402inline auto obtainGlasses(const std::string& identifier, const std::shared_ptr<Client>& client)
1404 return Glasses::create(identifier, client);
1405}
1406
1409 std::chrono::milliseconds pollTimeout)
1411 return std::shared_ptr<WandStreamHelper>(new WandStreamHelper(std::move(glasses), pollTimeout));
1412}
1413
1417 return std::shared_ptr<Wand>(new Wand(handle, std::move(wandStreamHelper)));
1418}
1419
1422 const std::string& displayName,
1423 std::chrono::milliseconds connectionPollInterval)
1425
1427 new GlassesConnectionHelper(std::move(glasses), displayName, connectionPollInterval));
1428}
1429
1433 std::chrono::milliseconds pollInterval)
1435
1437 new ParamChangeHelper(std::move(client), std::move(listener), pollInterval));
1438}
1440
1442
1443} // namespace tiltfive
1444
1446
1449inline std::ostream& operator<<(std::ostream& os, const T5_WandReport& instance) {
1450 // Print the validity flags
1451 os << "[" << (instance.analogValid ? "A" : "_") << (instance.buttonsValid ? "B" : "_")
1452 << (instance.poseValid ? "P" : "_") << "]";
1453
1454 if (instance.analogValid) {
1455 os << "[A: " << std::right << std::fixed << std::setw(10) << instance.stick.x << "x"
1456 << std::right << std::fixed << std::setw(10) << instance.stick.y << " | " << std::right
1457 << std::fixed << std::setw(10) << instance.trigger << "]";
1458 } else {
1459 os << "[A: Invalid]";
1460 }
1461
1462 if (instance.buttonsValid) {
1463 os << "[B: " << (instance.buttons.t5 ? "T" : "_") << (instance.buttons.one ? "1" : "_")
1464 << (instance.buttons.two ? "2" : "_") << (instance.buttons.three ? "3" : "_")
1465 << (instance.buttons.a ? "A" : "_") << (instance.buttons.b ? "B" : "_")
1466 << (instance.buttons.x ? "X" : "_") << (instance.buttons.y ? "Y" : "_") << "]";
1467 } else {
1468 os << "[B: Invalid]";
1469 }
1470
1471 if (instance.poseValid) {
1472 os << "[P: (" << std::right << std::fixed << std::setw(10) << instance.posGrip_GBD.x << ","
1473 << std::right << std::fixed << std::setw(10) << instance.posGrip_GBD.y << ","
1474 << std::right << std::fixed << std::setw(10) << instance.posGrip_GBD.z << ") ("
1475 << std::right << std::fixed << std::setw(10) << instance.rotToWND_GBD.w << ","
1476 << std::right << std::fixed << std::setw(10) << instance.rotToWND_GBD.x << ","
1477 << std::right << std::fixed << std::setw(10) << instance.rotToWND_GBD.y << ","
1478 << std::right << std::fixed << std::setw(10) << instance.rotToWND_GBD.z << ")"
1479 << "]";
1480 }
1481
1482 return os;
1483}
1484
1488 std::string gameboardType;
1489 switch (instance.gameboardType) {
1491 gameboardType = "None";
1492 break;
1494 gameboardType = "LE";
1495 break;
1497 gameboardType = "XE";
1498 break;
1500 gameboardType = "XE (Raised)";
1501 break;
1502 default:
1503 // Shouldn't happen unless there's some bad casting going on elsewhere.
1504 gameboardType = std::string("[Invalid T5_GameboardType : ") +
1505 std::to_string(static_cast<int>(instance.gameboardType)) +
1506 std::string("]");
1507 break;
1508 }
1509
1510 os << "[" << instance.timestampNanos << "| " << gameboardType << " (" << std::right
1511 << std::fixed << std::setw(10) << instance.posGLS_GBD.x << "," << std::right << std::fixed
1512 << std::setw(10) << instance.posGLS_GBD.y << "," << std::right << std::fixed << std::setw(10)
1513 << instance.posGLS_GBD.z << ") (" << std::right << std::fixed << std::setw(10)
1514 << instance.rotToGLS_GBD.w << "," << std::right << std::fixed << std::setw(10)
1515 << instance.rotToGLS_GBD.x << "," << std::right << std::fixed << std::setw(10)
1516 << instance.rotToGLS_GBD.y << "," << std::right << std::fixed << std::setw(10)
1517 << instance.rotToGLS_GBD.z << ")"
1518 << "]";
1519
1520 return os;
1521}
1522
1525inline std::ostream& operator<<(std::ostream& os, const T5_ParamSys& instance) {
1526 switch (instance) {
1528 os << "Service Version";
1529 break;
1530
1532 os << "UI Attention Required";
1533 break;
1534
1535 default:
1536 // Shouldn't happen unless there's some bad casting going on elsewhere.
1537 os << "[Invalid T5_ParamSys : " << static_cast<int>(instance) << "]";
1538 break;
1539 }
1540
1541 return os;
1542}
1543
1547 switch (instance) {
1549 os << "IPD";
1550 break;
1551
1553 os << "Friendly Name";
1554 break;
1555
1556 default:
1557 // Shouldn't happen unless there's some bad casting going on elsewhere.
1558 os << "[Invalid T5_ParamGlasses : " << static_cast<int>(instance) << "]";
1559 break;
1560 }
1561
1562 return os;
1563}
1564
C interface definition for the Tilt Five™ API.
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)
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
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 createParamChangedHelper(std::weak_ptr< ParamChangeListener > listener, std::chrono::milliseconds pollInterval=std::chrono::milliseconds(100)) -> std::unique_ptr< ParamChangeHelper >
Create a ParamChangeHelper.
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.
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 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 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 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 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.
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.
Specialization of tiltfive::Result for functions with 'no return'.
Definition: result.hpp:199
Templated return type with support for error conditions.
Definition: result.hpp:37
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.
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.
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.
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.
#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_GraphicsApi
Graphics API types.
Definition: types.h:107
T5_ParamSys
Possible parameters that can be retrieved with System-wide parameters.
Definition: types.h:510
T5_GameboardType
Possible gameboard types.
Definition: types.h:179
struct T5_GlassesImpl * T5_Glasses
Opaque handle used with glasses.
Definition: types.h:98
T5_GlassesPoseUsage
Glasses pose usage indicator.
Definition: types.h:249
@ 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_Integer_CPL_AttRequired
Non-zero if the control panel requires user interaction (E.g. Important firmware update) - Integer,...
Definition: types.h:516
@ 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
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_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
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 t5SubmitEmptyCamImageBuffer(T5_Glasses glasses, T5_CamImage *image)
Submit an empty image buffer to be filled by the camera frame stream.
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.
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 t5SendFrameToGlasses(T5_Glasses glasses, const T5_FrameInfo *info)
Send a frame to display on the glasses.
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 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 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.
std::ostream & operator<<(std::ostream &os, const T5_WandReport &instance)
Support for writing T5_WandReport to an std::ostream.
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 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.
C++ Templated common return type for the Tilt Five™ API.
Camera Frame information to be retrieved with t5GetFilledCamImageBuffer()
Definition: types.h:357
Camera stream configuration.
Definition: types.h:297
Client provided information for use with t5CreateGlasses()
Definition: types.h:217
uint8_t sdkType
The SDK type.
Definition: types.h:227
const char * applicationId
The application ID.
Definition: types.h:219
const char * applicationVersion
The application version.
Definition: types.h:222
Render information to be used with t5SendFrameToGlasses()
Definition: types.h:306
Physical dimensions of a gameboard.
Definition: types.h:194
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
Contains wand related information (Pose, Buttons, Trigger, Stick, Battery)
Definition: types.h:427
bool poseValid
Validity of pose parameters. True = valid.
Definition: types.h:441
T5_Quat rotToWND_GBD
WND/GBD rotation unit quaternion.
Definition: types.h:469
struct T5_WandReport::@5 buttons
Buttons state. True = Pressed.
T5_Vec3 posGrip_GBD
Position (Grip) - Vector3f.
Definition: types.h:478
float trigger
Trigger - Analog, Range [0.0 - 1.0], 1.0 = Fully depressed.
Definition: types.h:444
bool analogValid
Validity of analog parameters. True = valid.
Definition: types.h:432
bool buttonsValid
Validity of button parameters. True = valid.
Definition: types.h:438
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
Represents an event from the wand stream.
Definition: types.h:485