| Tilt Five NDK |

TiltFiveNative.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2020-2022 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
45 namespace tiltfive {
46
50
54
55 class Client;
56 class Glasses;
57 class Wand;
58 class WandStreamHelper;
59 class GlassesConnectionHelper;
60 class ParamChangeHelper;
61 class ParamChangeListener;
62
65 inline auto obtainWand(T5_WandHandle handle, std::shared_ptr<WandStreamHelper> wandStreamHelper)
66 -> std::shared_ptr<Wand>;
67 inline auto obtainWandStreamHelper(
68 std::shared_ptr<Glasses> glasses,
69 std::chrono::milliseconds pollTimeout = std::chrono::milliseconds(100))
70 -> std::shared_ptr<WandStreamHelper>;
71 inline auto obtainGlassesConnectionHelper(std::shared_ptr<Glasses> glasses,
72 const std::string& displayName,
73 std::chrono::milliseconds connectionPollInterval)
74 -> std::unique_ptr<GlassesConnectionHelper>;
75 inline auto obtainParamChangeHelper(std::shared_ptr<Client> client,
76 std::weak_ptr<ParamChangeListener> listener,
77 std::chrono::milliseconds pollInterval)
78 -> std::unique_ptr<ParamChangeHelper>;
80
82 class Client : public std::enable_shared_from_this<Client> {
83 private:
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) {
126 return Error::kStringOverflow;
127 }
128
129 if (applicationVersion.length() > T5_MAX_STRING_PARAM_LEN) {
130 return Error::kStringOverflow;
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
146 public:
148 virtual ~Client() {
149 // Release context and library
150 t5DestroyContext(&mContext);
151 mContext = nullptr;
152 }
154
158
164 auto listGlasses() -> Result<std::vector<std::string>> {
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
208 auto getServiceVersion() -> Result<std::string> {
209 std::unique_ptr<char[]> value(new char[T5_MAX_STRING_PARAM_LEN]);
210 size_t size = T5_MAX_STRING_PARAM_LEN;
211 T5_Result err =
212 t5GetSystemUtf8Param(mContext, kT5_ParamSys_UTF8_Service_Version, value.get(), &size);
213 if (!err) {
214 return std::string(value.get(), size);
215 } else {
216 return static_cast<Error>(err);
217 }
218 }
219
227 auto getChangedParams() -> Result<std::vector<T5_ParamSys>> {
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
261 auto isTiltFiveUiRequestingAttention() -> Result<bool> {
262 int64_t value = 0;
263
264 T5_Result err =
265 t5GetSystemIntegerParam(mContext, kT5_ParamSys_Integer_CPL_AttRequired, &value);
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
279 auto getGameboardSize(T5_GameboardType type) -> Result<T5_GameboardSize> {
280 T5_GameboardSize size;
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(
298 std::weak_ptr<ParamChangeListener> listener,
299 std::chrono::milliseconds pollInterval = std::chrono::milliseconds(100))
300 -> std::unique_ptr<ParamChangeHelper> {
301
302 return obtainParamChangeHelper(shared_from_this(), std::move(listener), pollInterval);
303 }
304};
305
307 enum class ConnectionState : int {
310 kNotExclusivelyConnected,
311
313 kReserved,
314
316 kConnected,
317
319 kDisconnected,
320};
321
323 class Glasses : public std::enable_shared_from_this<Glasses> {
324 friend Client;
325 friend ParamChangeHelper;
326
327 const std::string mIdentifier;
328 const std::shared_ptr<Client> mClient;
329 std::weak_ptr<WandStreamHelper> mWandStreamHelper{};
330 T5_Glasses mGlasses{};
331
332 friend std::ostream& operator<<(std::ostream& os, std::shared_ptr<Glasses> const& instance) {
333 os << *instance;
334 return os;
335 }
336
337 friend std::ostream& operator<<(std::ostream& os, Glasses const& instance) {
338 os << "<Glasses:" << instance.mIdentifier << ">";
339 return os;
340 }
341
342 friend auto obtainGlasses(const std::string& identifier, const std::shared_ptr<Client>& client)
343 -> Result<std::shared_ptr<Glasses>>;
344
345 explicit Glasses(std::string identifier, std::shared_ptr<Client> client)
346 : mIdentifier(std::move(identifier)), mClient(std::move(client)) {}
347
348 static auto create(std::string identifier, std::shared_ptr<Client> client)
349 -> Result<std::shared_ptr<Glasses>> {
350
351 if (!client) {
352 return tiltfive::Error::kInvalidArgument;
353 }
354
355 T5_Glasses handle;
356 T5_Result err = t5CreateGlasses(client->mContext, identifier.c_str(), &handle);
357
358 if (err) {
359 return static_cast<Error>(err);
360 }
361
362 std::shared_ptr<Glasses> glasses{new Glasses(identifier, client)};
363
364 glasses->mGlasses = handle;
365
366 return glasses;
367 }
368
369 public:
376 [[nodiscard]] auto getHandle() const -> T5_Glasses {
377 return mGlasses;
378 }
379
389 [[nodiscard]] auto getIdentifier() const -> std::string {
390 return mIdentifier;
391 }
392
396 auto getConnectionState() -> Result<ConnectionState> {
397 T5_ConnectionState connectionState;
398 T5_Result err = t5GetGlassesConnectionState(mGlasses, &connectionState);
399 if (err != T5_SUCCESS) {
400 return static_cast<Error>(err);
401 }
402
403 switch (connectionState) {
404 case kT5_ConnectionState_NotExclusivelyConnected:
405 return ConnectionState::kNotExclusivelyConnected;
406
407 case kT5_ConnectionState_ExclusiveReservation:
408 return ConnectionState::kReserved;
409
410 case kT5_ConnectionState_ExclusiveConnection:
411 return ConnectionState::kConnected;
412
413 case kT5_ConnectionState_Disconnected:
414 return ConnectionState::kDisconnected;
415
416 default:
417 return Error::kInternalError;
418 }
419 }
420
428 auto getChangedParams() -> Result<std::vector<T5_ParamGlasses>> {
429 uint16_t changedParamsCount = 32;
430 std::vector<T5_ParamGlasses> changedParamsBuffer(changedParamsCount);
431
432 changedParamsBuffer.resize(changedParamsCount);
433 T5_Result err =
434 t5GetChangedGlassesParams(mGlasses, changedParamsBuffer.data(), &changedParamsCount);
435
436 for (;;) {
437 if (!err) {
438 changedParamsBuffer.resize(changedParamsCount);
439 return changedParamsBuffer;
440
441 } else if (err == T5_ERROR_OVERFLOW) {
442 changedParamsCount = changedParamsBuffer.size() * 2;
443 continue;
444
445 } else {
446 return static_cast<Error>(err);
447 }
448 }
449 }
450
458 auto getIpd() -> Result<double> {
459 double value = 0;
460 T5_Result err = t5GetGlassesFloatParam(mGlasses, 0, kT5_ParamGlasses_Float_IPD, &value);
461 if (!err) {
462 return value;
463 } else {
464 return static_cast<Error>(err);
465 }
466 }
467
476 auto getFriendlyName() -> Result<std::string> {
477 std::unique_ptr<char[]> value(new char[T5_MAX_STRING_PARAM_LEN]);
478 size_t size = T5_MAX_STRING_PARAM_LEN;
479 T5_Result err = t5GetGlassesUtf8Param(
480 mGlasses, 0, kT5_ParamGlasses_UTF8_FriendlyName, value.get(), &size);
481 if (!err) {
482 return std::string(value.get());
483 } else {
484 return static_cast<Error>(err);
485 }
486 }
487
508 auto reserve(const std::string& displayName) -> Result<void> {
509 T5_Result err = t5ReserveGlasses(mGlasses, displayName.c_str());
510 if (!err) {
511 return kSuccess;
512 } else {
513 return static_cast<Error>(err);
514 }
515 }
516
531 auto ensureReady() -> Result<void> {
532 T5_Result err = t5EnsureGlassesReady(mGlasses);
533 if (!err) {
534 return kSuccess;
535 } else {
536 return static_cast<Error>(err);
537 }
538 }
539
547 auto release() -> Result<void> {
548 T5_Result err = t5ReleaseGlasses(mGlasses);
549 if (!err) {
550 return kSuccess;
551 } else {
552 return static_cast<Error>(err);
553 }
554 }
555
561 auto getLatestGlassesPose(T5_GlassesPoseUsage usage) -> Result<T5_GlassesPose> {
562 T5_GlassesPose pose;
563 T5_Result err = t5GetGlassesPose(mGlasses, usage, &pose);
564
565 if (!err) {
566 return pose;
567 } else {
568 return static_cast<Error>(err);
569 }
570 }
571
576 auto initGraphicsContext(T5_GraphicsApi graphicsApi, void* graphicsContext) -> Result<void> {
577 T5_Result err = t5InitGlassesGraphicsContext(mGlasses, graphicsApi, graphicsContext);
578 if (!err) {
579 return kSuccess;
580 }
581 return static_cast<Error>(err);
582 }
583
587 auto configureCameraStream(T5_CameraStreamConfig config) -> Result<void> {
588 T5_Result err = t5ConfigureCameraStreamForGlasses(mGlasses, config);
589 if (!err) {
590 return kSuccess;
591 } else {
592 return static_cast<Error>(err);
593 }
594 }
595
597 //
599 auto getFilledCamImageBuffer() -> Result<T5_CamImage> {
600 T5_CamImage img;
601 T5_Result err = t5GetFilledCamImageBuffer(mGlasses, &img);
602 if (!err) {
603 return std::move(img);
604 } else {
605 return static_cast<Error>(err);
606 }
607 }
608
610 //
612 auto submitEmptyCamImageBuffer(T5_CamImage* imgBuffer) -> Result<void> {
613 T5_Result err = t5SubmitEmptyCamImageBuffer(mGlasses, imgBuffer);
614 if (!err) {
615 return kSuccess;
616 } else {
617 return static_cast<Error>(err);
618 }
619 }
620
622 //
625 auto cancelCamImageBuffer(uint8_t* buffer) -> Result<void> {
626 T5_Result err = t5CancelCamImageBuffer(mGlasses, buffer);
627 if (!err) {
628 return kSuccess;
629 } else {
630 return static_cast<Error>(err);
631 }
632 }
633
637 auto sendFrame(const T5_FrameInfo* const frameInfo) -> Result<void> {
638 T5_Result err = t5SendFrameToGlasses(mGlasses, frameInfo);
639 if (!err) {
640 return kSuccess;
641 } else {
642 return static_cast<Error>(err);
643 }
644 }
645
649 auto listWands() -> Result<std::vector<T5_WandHandle>> {
650 uint8_t wandCount = 4;
651 std::vector<T5_WandHandle> wandBuffer(wandCount);
652
653 for (;;) {
654 wandBuffer.resize(wandCount);
655 T5_Result err = t5ListWandsForGlasses(mGlasses, wandBuffer.data(), &wandCount);
656
657 if (!err) {
658 std::vector<T5_WandHandle> wands;
659 wands.reserve(wandCount);
660
661 for (auto i = 0; i < wandCount; i++) {
662 wands.push_back(wandBuffer[i]);
663 }
664
665 return wands;
666
667 } else if (err == T5_ERROR_OVERFLOW) {
668 wandCount = wandBuffer.size() * 2;
669 continue;
670
671 } else {
672 return static_cast<Error>(err);
673 }
674 }
675 }
676
680 auto configureWandStream(const T5_WandStreamConfig* const config) -> Result<void> {
681 T5_Result err = t5ConfigureWandStreamForGlasses(mGlasses, config);
682 if (!err) {
683 return kSuccess;
684 } else {
685 return static_cast<Error>(err);
686 }
687 }
688
703 auto readWandStream(std::chrono::milliseconds timeout = std::chrono::milliseconds(100))
704 -> Result<T5_WandStreamEvent> {
705 T5_WandStreamEvent event;
706
707 T5_Result err = t5ReadWandStreamForGlasses(mGlasses, &event, timeout.count());
708 if (!err) {
709 return event;
710 } else {
711 return static_cast<Error>(err);
712 }
713 }
714
719 auto getWandStreamHelper() -> std::shared_ptr<WandStreamHelper> {
720 auto wandStreamHelper = mWandStreamHelper.lock();
721 if (!wandStreamHelper) {
722 // needs initialization
723 wandStreamHelper = obtainWandStreamHelper(shared_from_this());
724 mWandStreamHelper = wandStreamHelper;
725 }
726 return wandStreamHelper;
727 }
728
736 auto createConnectionHelper(
737 const std::string& displayName,
738 std::chrono::milliseconds connectionPollInterval = std::chrono::milliseconds(100))
739 -> std::unique_ptr<GlassesConnectionHelper> {
740
741 return obtainGlassesConnectionHelper(
742 shared_from_this(), displayName, connectionPollInterval);
743 }
744
746 virtual ~Glasses() {
747 // Disconnect the glasses if they're connected
748 auto connectionState = getConnectionState();
749 if (!connectionState) {
750 return;
751 }
752
753 if (mGlasses) {
754 t5DestroyGlasses(&mGlasses);
755 mGlasses = nullptr;
756 }
757 }
759};
760
764 class GlassesConnectionHelper {
765 private:
766 const std::shared_ptr<Glasses> mGlasses;
767 const std::string mDisplayName;
768 const std::chrono::milliseconds mConnectionPollInterval;
769 const std::chrono::milliseconds mConnectedPollInterval = mConnectionPollInterval * 10;
770
771 std::atomic<bool> mRunning{true};
772 std::thread mThread;
773
774 std::mutex mLastAsyncErrorMtx;
775 std::atomic<std::error_code> mLastAsyncError{};
776
777 void setLastAsyncError(std::error_code err) {
778 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
779 mLastAsyncError = err;
780 }
781
782 void threadMain() {
783 while (mRunning) {
784 auto connectionState = mGlasses->getConnectionState();
785 if (!connectionState) {
786 setLastAsyncError(connectionState.error());
787 std::this_thread::sleep_for(mConnectionPollInterval);
788 continue;
789 }
790
791 switch (*connectionState) {
792 case ConnectionState::kNotExclusivelyConnected: {
793 // Attempt to connect
794 auto result = mGlasses->reserve(mDisplayName);
795 if (!result) {
796 setLastAsyncError(result.error());
797 }
798 // No action on success - the next call to getConnectionState() will
799 // detect the change
800
801 break;
802 }
803
804 case ConnectionState::kReserved:
805 case ConnectionState::kDisconnected: {
806 auto result = mGlasses->ensureReady();
807 if (!result) {
808 setLastAsyncError(result.error());
809 }
810 // No action on success - the next call to getConnectionState() will
811 // detect the change
812
813 break;
814 }
815
816 case ConnectionState::kConnected:
817 // If we're connected, increase polling interval to reduce excessive
818 // connections state queries (at the expense of slowing detection of
819 // disconnected devices).
820 std::this_thread::sleep_for(mConnectedPollInterval);
821 break;
822 }
823
824 std::this_thread::sleep_for(mConnectionPollInterval);
825 }
826 }
827
828 friend auto obtainGlassesConnectionHelper(std::shared_ptr<Glasses> glasses,
829 const std::string& displayName,
830 std::chrono::milliseconds connectionPollInterval)
831 -> std::unique_ptr<GlassesConnectionHelper>;
832
833 explicit GlassesConnectionHelper(std::shared_ptr<Glasses> glasses,
834 std::string displayName,
835 std::chrono::milliseconds connectionPollInterval)
836 : mGlasses(std::move(glasses))
837 , mDisplayName{std::move(displayName)}
838 , mConnectionPollInterval(connectionPollInterval) {
839
840 mThread = std::thread(&GlassesConnectionHelper::threadMain, this);
841 }
842
843 public:
845 [[nodiscard]] auto glasses() -> Glasses& {
846 return *mGlasses;
847 }
848
850 auto awaitConnection() -> Result<void> {
851 auto connectionState = mGlasses->getConnectionState();
852 if (!connectionState) {
853 return connectionState.error();
854 }
855
856 while (*connectionState != ConnectionState::kConnected) {
857 std::this_thread::sleep_for(mConnectionPollInterval);
858
859 connectionState = mGlasses->getConnectionState();
860 if (!connectionState) {
861 return connectionState.error();
862 }
863 }
864
865 return kSuccess;
866 }
867
871 auto awaitConnection(const std::chrono::milliseconds timeout) -> Result<void> {
872 auto start = std::chrono::steady_clock::now();
873
874 auto connectionState = mGlasses->getConnectionState();
875 if (!connectionState) {
876 return connectionState.error();
877 }
878
879 while (*connectionState != ConnectionState::kConnected) {
880 if ((std::chrono::steady_clock::now() - start) > timeout) {
881 return Error::kTimeout;
882 }
883
884 std::this_thread::sleep_for(mConnectionPollInterval);
885
886 connectionState = mGlasses->getConnectionState();
887 if (!connectionState) {
888 return connectionState.error();
889 }
890 }
891
892 return kSuccess;
893 }
894
901 auto consumeLastAsyncError() -> std::error_code {
902 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
903 return mLastAsyncError.exchange({});
904 }
905
907 virtual ~GlassesConnectionHelper() {
908 mRunning = false;
909 if (mThread.joinable()) {
910 mThread.join();
911 }
912 }
914};
915
923 class WandStreamHelper : public std::enable_shared_from_this<WandStreamHelper> {
924 private:
925 friend Wand;
926
927 const std::shared_ptr<Glasses> mGlasses;
928 const std::chrono::milliseconds mPollTimeout;
929
930 std::atomic<bool> mWandListDirty{true};
931 std::mutex mWandListMtx; // guards access to mWandList
932 std::vector<T5_WandHandle> mWandList;
933
934 std::atomic<bool> mRunning{true};
935 std::thread mThread;
936
937 std::mutex mLastWandReportsMtx; // guards access to mLastWandReports
938 std::map<T5_WandHandle, T5_WandReport> mLastWandReports;
939
940 std::mutex mLastAsyncErrorMtx;
941 std::atomic<std::error_code> mLastAsyncError{};
942
943 void setLastAsyncError(std::error_code err) {
944 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
945 mLastAsyncError = err;
946 }
947
948 auto drainStream(const std::shared_ptr<Glasses>& glasses) -> Result<void> {
949 while (mRunning) {
950 auto result = glasses->readWandStream(mPollTimeout);
951 if (!result) {
952 return result.error();
953 }
954
955 std::lock_guard<std::mutex> lock{mLastWandReportsMtx};
956
957 // Process the event
958 switch (result->type) {
959 case kT5_WandStreamEventType_Connect:
960 mLastWandReports[result->wandId] = {};
961 mWandListDirty = true;
962 break;
963
964 case kT5_WandStreamEventType_Disconnect:
965 mLastWandReports.erase(result->wandId);
966 mWandListDirty = true;
967 break;
968
969 case kT5_WandStreamEventType_Desync:
970 mWandListDirty = true;
971 break;
972
973 case kT5_WandStreamEventType_Report:
974 mLastWandReports[result->wandId] = result->report;
975 break;
976 }
977 }
978
979 return Error::kUnavailable;
980 }
981
982 // Update the reports map based on the latest wand list.
983 // Ensure empty reports are populated for newly-connected wands.
984 // Remove reports for wands that are no longer connected.
985 //
986 // PRECONDITIONS: Wand list mutex must be held.
987 auto refreshReports() -> void {
988 std::lock_guard<std::mutex> lock{mLastWandReportsMtx};
989
990 // Obtain a set of the wand handles held in mLastWandReports
991 std::set<T5_WandHandle> lastWandReportKeys;
992 std::transform(mLastWandReports.cbegin(),
993 mLastWandReports.cend(),
994 std::inserter(lastWandReportKeys, lastWandReportKeys.end()),
995 [](std::pair<T5_WandHandle, T5_WandReport> pair) { return pair.first; });
996
997 // Remove from the list all connected wands and add empty reports for new wands.
998 for (const auto& connectedWand : mWandList) {
999 lastWandReportKeys.erase(connectedWand);
1000 mLastWandReports.insert({connectedWand, T5_WandReport{}});
1001 }
1002
1003 // The remainder of the list is wand reports for disconnected wands - remove them
1004 for (auto defunctKey : lastWandReportKeys) {
1005 mLastWandReports.erase(defunctKey);
1006 }
1007 }
1008
1009 void threadMain() {
1010 T5_WandStreamConfig streamConfig{true};
1011 bool configured = false;
1012
1013 while (mRunning) {
1014 // Configure the stream if we haven't already
1015 if (!configured) {
1016 auto configureRequest = mGlasses->configureWandStream(&streamConfig);
1017 if (!configureRequest) {
1018 setLastAsyncError(configureRequest.error());
1019 std::this_thread::sleep_for(std::chrono::milliseconds(20));
1020 continue;
1021 }
1022 configured = true;
1023 }
1024
1025 // Drain the stream
1026 auto result = drainStream(mGlasses);
1027 if ((result.error() != tiltfive::Error::kTimeout) &&
1028 (result.error() != tiltfive::Error::kUnavailable)) {
1029
1030 // For errors other than timeout, record it, small delay and loop
1031 setLastAsyncError(result.error());
1032 std::this_thread::sleep_for(std::chrono::milliseconds(20));
1033 }
1034 }
1035
1036 // Disable the stream
1037 streamConfig.enabled = false;
1038 auto configureRequest = mGlasses->configureWandStream(&streamConfig);
1039 if (!configureRequest) {
1040 setLastAsyncError(configureRequest.error());
1041 }
1042
1043 // Flag as no longer running if we've exited due to error
1044 mRunning = false;
1045 }
1046
1047 friend inline auto obtainWandStreamHelper(std::shared_ptr<Glasses> glasses,
1048 std::chrono::milliseconds pollTimeout)
1049 -> std::shared_ptr<WandStreamHelper>;
1050
1051 explicit WandStreamHelper(
1052 std::shared_ptr<Glasses> glasses,
1053 std::chrono::milliseconds pollTimeout = std::chrono::milliseconds(100))
1054 : mGlasses(std::move(glasses)), mPollTimeout(pollTimeout) {
1055
1056 mThread = std::thread(&WandStreamHelper::threadMain, this);
1057 }
1058
1059 auto getLatestReport(const T5_WandHandle& handle) -> Result<T5_WandReport> {
1060 std::lock_guard<std::mutex> lock{mLastWandReportsMtx};
1061
1062 auto report = mLastWandReports.find(handle);
1063 if (report == mLastWandReports.end()) {
1064 return tiltfive::Error::kTargetNotFound;
1065 }
1066
1067 return report->second;
1068 };
1069
1070 public:
1078 auto consumeLastAsyncError() -> std::error_code {
1079 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
1080 return mLastAsyncError.exchange({});
1081 }
1082
1089 auto listWands() -> Result<std::vector<std::shared_ptr<Wand>>> {
1090 std::lock_guard<std::mutex> lock{mWandListMtx};
1091
1092 // Update the wand list if it's been invalidated
1093 if (mWandListDirty.exchange(false)) {
1094 auto result = mGlasses->listWands();
1095 if (!result) {
1096 mWandListDirty = true;
1097 return result.error();
1098 }
1099
1100 std::vector<T5_WandHandle> wandHandles;
1101 for (auto wandHandle : *result) {
1102 wandHandles.push_back(wandHandle);
1103 }
1104 mWandList = wandHandles;
1105
1106 refreshReports();
1107 }
1108
1109 // Realize wand list
1110 std::vector<std::shared_ptr<Wand>> wands;
1111 for (auto wandHandle : mWandList) {
1112 wands.push_back(obtainWand(wandHandle, shared_from_this()));
1113 }
1114
1115 return wands;
1116 };
1117
1119 virtual ~WandStreamHelper() {
1120 mRunning = false;
1121 if (mThread.joinable()) {
1122 mThread.join();
1123 }
1124 }
1126};
1127
1129 class ParamChangeListener {
1130 public:
1133 virtual auto onSysParamChanged(const std::vector<T5_ParamSys>& changed) -> void = 0;
1134
1137 virtual auto onGlassesParamChanged(const std::shared_ptr<Glasses>& glasses,
1138 const std::vector<T5_ParamGlasses>& changed) -> void = 0;
1139
1141 virtual ~ParamChangeListener() = default;
1143};
1144
1147 class ParamChangeHelper {
1148 private:
1149 const std::shared_ptr<Client> mClient;
1150 const std::weak_ptr<ParamChangeListener> mChangeListener;
1151
1152 static constexpr size_t kDefaultSettingBufferSize = 16;
1153
1154 std::mutex mRegisteredGlassesMtx;
1155 std::set<std::shared_ptr<Glasses>> mRegisteredGlasses;
1156
1157 std::vector<T5_ParamSys> mChangedSysParams;
1158 std::vector<T5_ParamGlasses> mChangedGlassesParams;
1159
1160 std::chrono::milliseconds mPollInterval;
1161
1162 std::thread mThread;
1163 std::atomic<bool> mRunning{true};
1164
1165 std::mutex mLastAsyncErrorMtx;
1166 std::atomic<std::error_code> mLastAsyncError{};
1167
1168 void setLastAsyncError(std::error_code err) {
1169 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
1170 mLastAsyncError = err;
1171 }
1172
1173 friend auto obtainParamChangeHelper(std::shared_ptr<Client> client,
1174 std::weak_ptr<ParamChangeListener> listener,
1175 std::chrono::milliseconds pollInterval)
1176 -> std::unique_ptr<ParamChangeHelper>;
1177
1178 ParamChangeHelper(std::shared_ptr<Client> client,
1179 std::weak_ptr<ParamChangeListener> listener,
1180 std::chrono::milliseconds pollInterval)
1181 : mClient(std::move(client))
1182 , mChangeListener(std::move(listener))
1183 , mPollInterval(pollInterval) {
1184
1185 mThread = std::thread(&ParamChangeHelper::threadMain, this);
1186 }
1187
1188 auto checkGlassesParams(const std::shared_ptr<ParamChangeListener>& listener) -> void {
1189 std::lock_guard<std::mutex> lock(mRegisteredGlassesMtx);
1190 for (const auto& glasses : mRegisteredGlasses) {
1191 checkGlassesParams(glasses, listener);
1192 }
1193 }
1194
1195 auto checkGlassesParams(const std::shared_ptr<Glasses>& glasses,
1196 const std::shared_ptr<ParamChangeListener>& listener) -> void {
1197 uint16_t changeCount;
1198
1199 mChangedGlassesParams.resize(kDefaultSettingBufferSize);
1200 for (;;) {
1201 changeCount = mChangedGlassesParams.size();
1202 T5_Result err = t5GetChangedGlassesParams(
1203 glasses->mGlasses, mChangedGlassesParams.data(), &changeCount);
1204
1205 if (!err) {
1206 if (changeCount > 0) {
1207 mChangedGlassesParams.resize(changeCount);
1208 listener->onGlassesParamChanged(glasses, mChangedGlassesParams);
1209 }
1210 break;
1211 }
1212
1213 // Error - increase buffer if we overflowed, or record the error and exit
1214 if (err == T5_ERROR_OVERFLOW) {
1215 mChangedSysParams.resize(mChangedSysParams.size() * 2);
1216 continue;
1217 }
1218
1219 setLastAsyncError(static_cast<Error>(err));
1220 break;
1221 }
1222 }
1223
1224 auto checkSysParams(const std::shared_ptr<ParamChangeListener>& listener) -> void {
1225 uint16_t changeCount;
1226
1227 mChangedSysParams.resize(kDefaultSettingBufferSize);
1228 for (;;) {
1229 changeCount = mChangedSysParams.size();
1230 T5_Result err =
1231 t5GetChangedSystemParams(mClient->mContext, mChangedSysParams.data(), &changeCount);
1232
1233 if (!err) {
1234 if (changeCount > 0) {
1235 mChangedSysParams.resize(changeCount);
1236 listener->onSysParamChanged(mChangedSysParams);
1237 }
1238 break;
1239 }
1240
1241 // Error - increase buffer if we overflowed, or record the error and exit
1242 if (err == T5_ERROR_OVERFLOW) {
1243 mChangedSysParams.resize(mChangedSysParams.size() * 2);
1244 continue;
1245 }
1246
1247 setLastAsyncError(static_cast<Error>(err));
1248 break;
1249 }
1250 }
1251
1252 auto threadMain() -> void {
1253 while (mRunning) {
1254 // Listener weak_ptr -> shared_ptr or exit
1255 {
1256 auto listener = mChangeListener.lock();
1257 if (!listener) {
1258 break;
1259 }
1260
1261 checkGlassesParams(listener);
1262
1263 checkSysParams(listener);
1264 }
1265
1266 std::this_thread::sleep_for(mPollInterval);
1267 }
1268 }
1269
1270 public:
1272 virtual ~ParamChangeHelper() {
1273 mRunning = false;
1274 if (mThread.joinable()) {
1275 mThread.join();
1276 }
1277 }
1279
1287 auto consumeLastAsyncError() -> std::error_code {
1288 std::lock_guard<std::mutex> lock(mLastAsyncErrorMtx);
1289 return mLastAsyncError.exchange({});
1290 }
1291
1293 auto registerGlasses(const std::shared_ptr<Glasses>& glasses) -> void {
1294 std::lock_guard<std::mutex> lock(mRegisteredGlassesMtx);
1295 mRegisteredGlasses.insert(glasses);
1296 }
1297
1299 auto deregisterGlasses(const std::shared_ptr<Glasses>& glasses) -> void {
1300 std::lock_guard<std::mutex> lock(mRegisteredGlassesMtx);
1301 mRegisteredGlasses.erase(glasses);
1302 }
1303};
1304
1307 class Wand {
1308 private:
1309 T5_WandHandle mHandle;
1310 std::shared_ptr<WandStreamHelper> mWandStreamHelper;
1311
1312 friend WandStreamHelper;
1313
1314 friend std::ostream& operator<<(std::ostream& os, std::shared_ptr<Wand> const& instance) {
1315 os << *instance;
1316 return os;
1317 }
1318
1319 friend std::ostream& operator<<(std::ostream& os, Wand const& instance) {
1320 os << "<Wand:" << +instance.mHandle << ">";
1321 return os;
1322 }
1323
1324 friend auto obtainWand(T5_WandHandle handle, std::shared_ptr<WandStreamHelper> wandStreamHelper)
1325 -> std::shared_ptr<Wand>;
1326
1327 Wand(T5_WandHandle handle, std::shared_ptr<WandStreamHelper> wandStreamHelper)
1328 : mHandle(handle), mWandStreamHelper(std::move(wandStreamHelper)) {}
1329
1330 public:
1332 auto getLatestReport() const -> Result<T5_WandReport> {
1333 return mWandStreamHelper->getLatestReport(mHandle);
1334 }
1335
1337 [[nodiscard]] T5_WandHandle handle() const {
1338 return mHandle;
1339 }
1340};
1341
1351 inline auto obtainClient(const std::string& applicationId,
1352 const std::string& applicationVersion,
1353 void* platformContext,
1354 const uint8_t sdkType = 0) -> Result<std::shared_ptr<Client>> {
1355
1356 return Client::create(applicationId, applicationVersion, platformContext, sdkType);
1357}
1358
1365 inline auto obtainGlasses(const std::string& identifier, const std::shared_ptr<Client>& client)
1366 -> Result<std::shared_ptr<Glasses>> {
1367 return Glasses::create(identifier, client);
1368}
1369
1371 inline auto obtainWandStreamHelper(std::shared_ptr<Glasses> glasses,
1372 std::chrono::milliseconds pollTimeout)
1373 -> std::shared_ptr<WandStreamHelper> {
1374 return std::shared_ptr<WandStreamHelper>(new WandStreamHelper(std::move(glasses), pollTimeout));
1375}
1376
1378 inline auto obtainWand(T5_WandHandle handle, std::shared_ptr<WandStreamHelper> wandStreamHelper)
1379 -> std::shared_ptr<Wand> {
1380 return std::shared_ptr<Wand>(new Wand(handle, std::move(wandStreamHelper)));
1381}
1382
1384 inline auto obtainGlassesConnectionHelper(std::shared_ptr<Glasses> glasses,
1385 const std::string& displayName,
1386 std::chrono::milliseconds connectionPollInterval)
1387 -> std::unique_ptr<GlassesConnectionHelper> {
1388
1389 return std::unique_ptr<GlassesConnectionHelper>(
1390 new GlassesConnectionHelper(std::move(glasses), displayName, connectionPollInterval));
1391}
1392
1394 inline auto obtainParamChangeHelper(std::shared_ptr<Client> client,
1395 std::weak_ptr<ParamChangeListener> listener,
1396 std::chrono::milliseconds pollInterval)
1397 -> std::unique_ptr<ParamChangeHelper> {
1398
1399 return std::unique_ptr<ParamChangeHelper>(
1400 new ParamChangeHelper(std::move(client), std::move(listener), pollInterval));
1401}
1403
1405
1406} // namespace tiltfive
1407
1409
1412 inline std::ostream& operator<<(std::ostream& os, const T5_WandReport& instance) {
1413 // Print the validity flags
1414 os << "[" << (instance.analogValid ? "A" : "_") << (instance.buttonsValid ? "B" : "_")
1415 << (instance.poseValid ? "P" : "_") << "]";
1416
1417 if (instance.analogValid) {
1418 os << "[A: " << std::right << std::fixed << std::setw(10) << instance.stick.x << "x"
1419 << std::right << std::fixed << std::setw(10) << instance.stick.y << " | " << std::right
1420 << std::fixed << std::setw(10) << instance.trigger << "]";
1421 } else {
1422 os << "[A: Invalid]";
1423 }
1424
1425 if (instance.buttonsValid) {
1426 os << "[B: " << (instance.buttons.t5 ? "T" : "_") << (instance.buttons.one ? "1" : "_")
1427 << (instance.buttons.two ? "2" : "_") << (instance.buttons.three ? "3" : "_")
1428 << (instance.buttons.a ? "A" : "_") << (instance.buttons.b ? "B" : "_")
1429 << (instance.buttons.x ? "X" : "_") << (instance.buttons.y ? "Y" : "_") << "]";
1430 } else {
1431 os << "[B: Invalid]";
1432 }
1433
1434 if (instance.poseValid) {
1435 os << "[P: (" << std::right << std::fixed << std::setw(10) << instance.posGrip_GBD.x << ","
1436 << std::right << std::fixed << std::setw(10) << instance.posGrip_GBD.y << ","
1437 << std::right << std::fixed << std::setw(10) << instance.posGrip_GBD.z << ") ("
1438 << std::right << std::fixed << std::setw(10) << instance.rotToWND_GBD.w << ","
1439 << std::right << std::fixed << std::setw(10) << instance.rotToWND_GBD.x << ","
1440 << std::right << std::fixed << std::setw(10) << instance.rotToWND_GBD.y << ","
1441 << std::right << std::fixed << std::setw(10) << instance.rotToWND_GBD.z << ")"
1442 << "]";
1443 }
1444
1445 return os;
1446}
1447
1450 inline std::ostream& operator<<(std::ostream& os, const T5_GlassesPose& instance) {
1451 std::string gameboardType;
1452 switch (instance.gameboardType) {
1453 case kT5_GameboardType_None:
1454 gameboardType = "None";
1455 break;
1456 case kT5_GameboardType_LE:
1457 gameboardType = "LE";
1458 break;
1459 case kT5_GameboardType_XE:
1460 gameboardType = "XE";
1461 break;
1462 case kT5_GameboardType_XE_Raised:
1463 gameboardType = "XE (Raised)";
1464 break;
1465 default:
1466 // Shouldn't happen unless there's some bad casting going on elsewhere.
1467 gameboardType = std::string("[Invalid T5_GameboardType : ") +
1468 std::to_string(static_cast<int>(instance.gameboardType)) +
1469 std::string("]");
1470 break;
1471 }
1472
1473 os << "[" << instance.timestampNanos << "| " << gameboardType << " (" << std::right
1474 << std::fixed << std::setw(10) << instance.posGLS_GBD.x << "," << std::right << std::fixed
1475 << std::setw(10) << instance.posGLS_GBD.y << "," << std::right << std::fixed << std::setw(10)
1476 << instance.posGLS_GBD.z << ") (" << std::right << std::fixed << std::setw(10)
1477 << instance.rotToGLS_GBD.w << "," << std::right << std::fixed << std::setw(10)
1478 << instance.rotToGLS_GBD.x << "," << std::right << std::fixed << std::setw(10)
1479 << instance.rotToGLS_GBD.y << "," << std::right << std::fixed << std::setw(10)
1480 << instance.rotToGLS_GBD.z << ")"
1481 << "]";
1482
1483 return os;
1484}
1485
1488 inline std::ostream& operator<<(std::ostream& os, const T5_ParamSys& instance) {
1489 switch (instance) {
1490 case kT5_ParamSys_UTF8_Service_Version:
1491 os << "Service Version";
1492 break;
1493
1494 case kT5_ParamSys_Integer_CPL_AttRequired:
1495 os << "UI Attention Required";
1496 break;
1497
1498 default:
1499 // Shouldn't happen unless there's some bad casting going on elsewhere.
1500 os << "[Invalid T5_ParamSys : " << static_cast<int>(instance) << "]";
1501 break;
1502 }
1503
1504 return os;
1505}
1506
1509 inline std::ostream& operator<<(std::ostream& os, const T5_ParamGlasses& instance) {
1510 switch (instance) {
1511 case kT5_ParamGlasses_Float_IPD:
1512 os << "IPD";
1513 break;
1514
1515 case kT5_ParamGlasses_UTF8_FriendlyName:
1516 os << "Friendly Name";
1517 break;
1518
1519 default:
1520 // Shouldn't happen unless there's some bad casting going on elsewhere.
1521 os << "[Invalid T5_ParamGlasses : " << static_cast<int>(instance) << "]";
1522 break;
1523 }
1524
1525 return os;
1526}
1527
T5_Result
uint32_t T5_Result
Represents an error code that may be returned by the Tilt Five™ API.
Definition: errors.h:47
errors.hpp
C++ errors for the Tilt Five™ API.
result.hpp
C++ Templated common return type for the Tilt Five™ API.
TiltFiveNative.h
C interface definition for the Tilt Five™ API.
T5_ERROR_SETTING_UNKNOWN
#define T5_ERROR_SETTING_UNKNOWN
The requested param is unknown.
Definition: errors.h:93
T5_SUCCESS
#define T5_SUCCESS
Success.
Definition: errors.h:54
T5_ERROR_OVERFLOW
#define T5_ERROR_OVERFLOW
Buffer overflow.
Definition: errors.h:102
tiltfive::Error
Error
Error codes returned by most functions of return type Result.
Definition: errors.hpp:37
tiltfive::Error::kStringOverflow
@ kStringOverflow
String overflow.
tiltfive::Error::kTimeout
@ kTimeout
Timeout.
tiltfive::Error::kInvalidArgument
@ kInvalidArgument
Argument(s) are invalid.
tiltfive::Error::kUnavailable
@ kUnavailable
Target is unavailable.
tiltfive::Error::kSuccess
@ kSuccess
Success.
tiltfive::Error::kOverflow
@ kOverflow
Buffer overflow.
tiltfive::Error::kTargetNotFound
@ kTargetNotFound
Target (wand) not found.
tiltfive::Error::kInternalError
@ kInternalError
An internal error occurred.
t5CreateContext
T5_EXPORT T5_Result t5CreateContext(T5_Context *context, const T5_ClientInfo *clientInfo, void *platformContext)
Create a context object.
t5DestroyContext
T5_EXPORT void t5DestroyContext(T5_Context *context)
Destroy a context object.
t5CreateGlasses
T5_EXPORT T5_Result t5CreateGlasses(T5_Context context, const char *id, T5_Glasses *glasses)
Create a glasses access object.
t5GetGameboardSize
T5_EXPORT T5_Result t5GetGameboardSize(T5_Context context, T5_GameboardType gameboardType, T5_GameboardSize *gameboardSize)
Get the gameboard dimensions.
t5DestroyGlasses
T5_EXPORT void t5DestroyGlasses(T5_Glasses *glasses)
Destroy a glasses object.
t5ListGlasses
T5_EXPORT T5_Result t5ListGlasses(T5_Context context, char *buffer, size_t *bufferSize)
Enumerate all glasses.
t5GetSystemUtf8Param
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.
t5GetSystemIntegerParam
T5_EXPORT T5_Result t5GetSystemIntegerParam(T5_Context context, T5_ParamSys param, int64_t *value)
Get a system-wide integer parameter.
t5GetChangedSystemParams
T5_EXPORT T5_Result t5GetChangedSystemParams(T5_Context context, T5_ParamSys *buffer, uint16_t *count)
Get a system-wide list of changed parameters.
t5CancelCamImageBuffer
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.
t5GetFilledCamImageBuffer
T5_EXPORT T5_Result t5GetFilledCamImageBuffer(T5_Glasses glasses, T5_CamImage *image)
t5ReleaseGlasses
T5_EXPORT T5_Result t5ReleaseGlasses(T5_Glasses glasses)
t5ConfigureCameraStreamForGlasses
T5_EXPORT T5_Result t5ConfigureCameraStreamForGlasses(T5_Glasses glasses, T5_CameraStreamConfig config)
Configure the camera stream.
t5SubmitEmptyCamImageBuffer
T5_EXPORT T5_Result t5SubmitEmptyCamImageBuffer(T5_Glasses glasses, T5_CamImage *image)
Submit an empty image buffer to be filled by the camera frame stream.
t5GetGlassesConnectionState
T5_EXPORT T5_Result t5GetGlassesConnectionState(T5_Glasses glasses, T5_ConnectionState *connectionState)
Get the exclusivity/connection status of the glasses.
t5ReserveGlasses
T5_EXPORT T5_Result t5ReserveGlasses(T5_Glasses glasses, const char *displayName)
Reserve glasses for exclusive operations by the client.
t5EnsureGlassesReady
T5_EXPORT T5_Result t5EnsureGlassesReady(T5_Glasses glasses)
Ensure that reserved glasses are ready for exclusive operations.
t5SendFrameToGlasses
T5_EXPORT T5_Result t5SendFrameToGlasses(T5_Glasses glasses, const T5_FrameInfo *info)
Send a frame to display on the glasses.
t5InitGlassesGraphicsContext
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.
t5GetGlassesPose
T5_EXPORT T5_Result t5GetGlassesPose(T5_Glasses glasses, T5_GlassesPoseUsage usage, T5_GlassesPose *pose)
Get the latest pose of the glasses.
t5GetGlassesFloatParam
T5_EXPORT T5_Result t5GetGlassesFloatParam(T5_Glasses glasses, T5_WandHandle wand, T5_ParamGlasses param, double *value)
Get a glasses floating point parameter.
t5GetGlassesUtf8Param
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.
t5GetChangedGlassesParams
T5_EXPORT T5_Result t5GetChangedGlassesParams(T5_Glasses glasses, T5_ParamGlasses *buffer, uint16_t *count)
Get a glasses-specific list of changed parameters.
t5ReadWandStreamForGlasses
T5_EXPORT T5_Result t5ReadWandStreamForGlasses(T5_Glasses glasses, T5_WandStreamEvent *event, uint32_t timeoutMs)
Read from the wands event stream.
t5ListWandsForGlasses
T5_EXPORT T5_Result t5ListWandsForGlasses(T5_Glasses glasses, T5_WandHandle *buffer, uint8_t *count)
List available wands connected to this glasses.
t5ConfigureWandStreamForGlasses
T5_EXPORT T5_Result t5ConfigureWandStreamForGlasses(T5_Glasses glasses, const T5_WandStreamConfig *config)
Configure the wand event stream.
tiltfive::obtainParamChangeHelper
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.
Definition: TiltFiveNative.hpp:1394
tiltfive::obtainGlassesConnectionHelper
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.
Definition: TiltFiveNative.hpp:1384
tiltfive::obtainClient
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.
Definition: TiltFiveNative.hpp:1351
tiltfive::obtainWandStreamHelper
auto obtainWandStreamHelper(std::shared_ptr< Glasses > glasses, std::chrono::milliseconds pollTimeout) -> std::shared_ptr< WandStreamHelper >
Internal utility function - Do not call directly.
Definition: TiltFiveNative.hpp:1371
tiltfive::obtainGlasses
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.
Definition: TiltFiveNative.hpp:1365
tiltfive::obtainWand
auto obtainWand(T5_WandHandle handle, std::shared_ptr< WandStreamHelper > wandStreamHelper) -> std::shared_ptr< Wand >
Internal utility function - Do not call directly.
Definition: TiltFiveNative.hpp:1378
tiltfive::ConnectionState
ConnectionState
Represents the exclusivity connection state of glasses.
Definition: TiltFiveNative.hpp:307
tiltfive::ConnectionState::kReserved
@ kReserved
Glasses are reserved for exclusive use by this client.
tiltfive::ConnectionState::kConnected
@ kConnected
Glasses are connected for exclusive use by this client.
tiltfive::ConnectionState::kDisconnected
@ kDisconnected
Glasses were previously connected for exclusive use, but have now disconnected.
tiltfive::ConnectionState::kNotExclusivelyConnected
@ kNotExclusivelyConnected
Glasses have not been connected for exclusive use. They may still be connected to the host.
algorithm
operator<<
std::ostream & operator<<(std::ostream &os, const T5_WandReport &instance)
Support for writing T5_WandReport to an std::ostream.
Definition: TiltFiveNative.hpp:1412
T5_MAX_STRING_PARAM_LEN
#define T5_MAX_STRING_PARAM_LEN
The maximum number of characters allowed for string values.
Definition: types.h:38
T5_ConnectionState
T5_ConnectionState
Glasses connection state.
Definition: types.h:153
T5_ParamGlasses
T5_ParamGlasses
Possible parameters that can be retrieved for glasses.
Definition: types.h:419
T5_WandHandle
uint8_t T5_WandHandle
Opaque handle used with wands.
Definition: types.h:83
T5_Context
struct T5_ContextImpl * T5_Context
Opaque handle used with system-wide functions.
Definition: types.h:71
T5_GraphicsApi
T5_GraphicsApi
Graphics API types.
Definition: types.h:86
T5_ParamSys
T5_ParamSys
Possible parameters that can be retrieved with System-wide parameters.
Definition: types.h:429
T5_GameboardType
T5_GameboardType
Possible gameboard types.
Definition: types.h:98
T5_Glasses
struct T5_GlassesImpl * T5_Glasses
Opaque handle used with glasses.
Definition: types.h:77
T5_GlassesPoseUsage
T5_GlassesPoseUsage
Glasses pose usage indicator.
Definition: types.h:168
kT5_ConnectionState_Disconnected
@ kT5_ConnectionState_Disconnected
Glasses were previously exclusively connected, but the device has disconnected.
Definition: types.h:164
kT5_ConnectionState_ExclusiveConnection
@ kT5_ConnectionState_ExclusiveConnection
Glasses are connected for exclusive use.
Definition: types.h:155
kT5_ConnectionState_NotExclusivelyConnected
@ kT5_ConnectionState_NotExclusivelyConnected
Glasses have not been exclusively connected or reserved.
Definition: types.h:161
kT5_ConnectionState_ExclusiveReservation
@ kT5_ConnectionState_ExclusiveReservation
Glasses are reserved for exclusive use.
Definition: types.h:158
kT5_ParamGlasses_Float_IPD
@ kT5_ParamGlasses_Float_IPD
Interpupillary distance - Float, millimeters
Definition: types.h:422
kT5_ParamGlasses_UTF8_FriendlyName
@ kT5_ParamGlasses_UTF8_FriendlyName
User-facing name of the glasses - UTF8.
Definition: types.h:425
kT5_ParamSys_Integer_CPL_AttRequired
@ kT5_ParamSys_Integer_CPL_AttRequired
Non-zero if the control panel requires user interaction (E.g. Important firmware update) - Integer,...
Definition: types.h:435
kT5_ParamSys_UTF8_Service_Version
@ kT5_ParamSys_UTF8_Service_Version
Version of the service software - UTF8.
Definition: types.h:431
kT5_GameboardType_LE
@ kT5_GameboardType_LE
An LE gameboard.
Definition: types.h:103
kT5_GameboardType_XE
@ kT5_GameboardType_XE
An XE gameboard, flap laid flat.
Definition: types.h:106
kT5_GameboardType_None
@ kT5_GameboardType_None
No gameboard.
Definition: types.h:100
kT5_GameboardType_XE_Raised
@ kT5_GameboardType_XE_Raised
An XE gameboard, flap raised at an angle on the kickstand.
Definition: types.h:109
kT5_WandStreamEventType_Connect
@ kT5_WandStreamEventType_Connect
Wand connected.
Definition: types.h:321
kT5_WandStreamEventType_Disconnect
@ kT5_WandStreamEventType_Disconnect
Wand disconnected.
Definition: types.h:324
kT5_WandStreamEventType_Report
@ kT5_WandStreamEventType_Report
Wand report (Pose, Buttons, Trigger, Stick, Battery)
Definition: types.h:330
kT5_WandStreamEventType_Desync
@ kT5_WandStreamEventType_Desync
Stream has desynchronized.
Definition: types.h:327
atomic
chrono
functional
iomanip
iostream
map
memory
mutex
set
string
thread
utility
vector
cv_status::timeout
@ timeout
pair
constexpr pair(piecewise_construct_t, tuple< _Args1... >, tuple< _Args2... >)
start
size_t start() const
size
valarray< size_t > size() const
transform
constexpr _OutputIterator transform(_InputIterator __first, _InputIterator __last, _OutputIterator __result, _UnaryOperation __unary_op)
move
constexpr _OI move(_II __first, _II __last, _OI __result)
lock
void lock(_L1 &__l1, _L2 &__l2, _L3 &... __l3)
inserter
insert_iterator< _Container > inserter(_Container &__x, typename _Container::iterator __i)
string
basic_string< char > string
empty
constexpr auto empty(const _Container &__cont) noexcept(noexcept(__cont.empty())) -> decltype(__cont.empty())
std::this_thread::sleep_for
void sleep_for(const chrono::duration< _Rep, _Period > &__rtime)
std::atomic< std::error_code >
std::atomic< bool >
std::chrono::duration
basic_ostream< char >
std::error_code
tiltfive::Result
Templated return type with support for error conditions.
Definition: result.hpp:37
tiltfive::Result< void >
Specialization of tiltfive::Result for functions with 'no return'.
Definition: result.hpp:199
tiltfive::Client
Client for communicating with the Tilt Five™ API.
Definition: TiltFiveNative.hpp:82
tiltfive::Client::getServiceVersion
auto getServiceVersion() -> Result< std::string >
Get the version of the Tilt Five™ service.
Definition: TiltFiveNative.hpp:208
tiltfive::Client::getGameboardSize
auto getGameboardSize(T5_GameboardType type) -> Result< T5_GameboardSize >
Obtain the dimensions of the gameboard.
Definition: TiltFiveNative.hpp:279
tiltfive::Client::getChangedParams
auto getChangedParams() -> Result< std::vector< T5_ParamSys > >
Get a system-wide list of changed parameters.
Definition: TiltFiveNative.hpp:227
tiltfive::Client::obtainClient
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.
Definition: TiltFiveNative.hpp:1351
tiltfive::Client::isTiltFiveUiRequestingAttention
auto isTiltFiveUiRequestingAttention() -> Result< bool >
Check if the Tilt Five™ UI wants the users attention.
Definition: TiltFiveNative.hpp:261
tiltfive::Client::listGlasses
auto listGlasses() -> Result< std::vector< std::string > >
Enumerate glasses.
Definition: TiltFiveNative.hpp:164
tiltfive::Client::createParamChangedHelper
auto createParamChangedHelper(std::weak_ptr< ParamChangeListener > listener, std::chrono::milliseconds pollInterval=std::chrono::milliseconds(100)) -> std::unique_ptr< ParamChangeHelper >
Create a ParamChangeHelper.
Definition: TiltFiveNative.hpp:297
tiltfive::Glasses
Represents an instance of Tilt Five™ glasses.
Definition: TiltFiveNative.hpp:323
tiltfive::Glasses::initGraphicsContext
auto initGraphicsContext(T5_GraphicsApi graphicsApi, void *graphicsContext) -> Result< void >
Initialize the glasses for graphics operations.
Definition: TiltFiveNative.hpp:576
tiltfive::Glasses::getIpd
auto getIpd() -> Result< double >
Get the current IPD setting for this glasses.
Definition: TiltFiveNative.hpp:458
tiltfive::Glasses::getHandle
auto getHandle() const -> T5_Glasses
Obtain the C handle for this glasses object.
Definition: TiltFiveNative.hpp:376
tiltfive::Glasses::getFilledCamImageBuffer
auto getFilledCamImageBuffer() -> Result< T5_CamImage >
Get the latest camera image for this glasses.
Definition: TiltFiveNative.hpp:599
tiltfive::Glasses::getChangedParams
auto getChangedParams() -> Result< std::vector< T5_ParamGlasses > >
Get a system-wide list of changed parameters.
Definition: TiltFiveNative.hpp:428
tiltfive::Glasses::release
auto release() -> Result< void >
Release previously-reserved glasses.
Definition: TiltFiveNative.hpp:547
tiltfive::Glasses::submitEmptyCamImageBuffer
auto submitEmptyCamImageBuffer(T5_CamImage *imgBuffer) -> Result< void >
Submit a buffer to the camera image stream to hold Camera Frame data.
Definition: TiltFiveNative.hpp:612
tiltfive::Glasses::sendFrame
auto sendFrame(const T5_FrameInfo *const frameInfo) -> Result< void >
Send a frame to display on the glasses.
Definition: TiltFiveNative.hpp:637
tiltfive::Glasses::configureWandStream
auto configureWandStream(const T5_WandStreamConfig *const config) -> Result< void >
Configure the wand event stream.
Definition: TiltFiveNative.hpp:680
tiltfive::Glasses::getLatestGlassesPose
auto getLatestGlassesPose(T5_GlassesPoseUsage usage) -> Result< T5_GlassesPose >
Get the latest pose for this glasses.
Definition: TiltFiveNative.hpp:561
tiltfive::Glasses::obtainGlasses
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.
Definition: TiltFiveNative.hpp:1365
tiltfive::Glasses::cancelCamImageBuffer
auto cancelCamImageBuffer(uint8_t *buffer) -> Result< void >
Cancel an image buffer in use by the service for freeing.
Definition: TiltFiveNative.hpp:625
tiltfive::Glasses::readWandStream
auto readWandStream(std::chrono::milliseconds timeout=std::chrono::milliseconds(100)) -> Result< T5_WandStreamEvent >
Read from the wands event stream.
Definition: TiltFiveNative.hpp:703
tiltfive::Glasses::reserve
auto reserve(const std::string &displayName) -> Result< void >
Reserve glasses for exclusive operations by the client.
Definition: TiltFiveNative.hpp:508
tiltfive::Glasses::configureCameraStream
auto configureCameraStream(T5_CameraStreamConfig config) -> Result< void >
Configure the wand event stream.
Definition: TiltFiveNative.hpp:587
tiltfive::Glasses::getConnectionState
auto getConnectionState() -> Result< ConnectionState >
Get the current connection state of the glasses.
Definition: TiltFiveNative.hpp:396
tiltfive::Glasses::listWands
auto listWands() -> Result< std::vector< T5_WandHandle > >
Obtain a list of connected wands.
Definition: TiltFiveNative.hpp:649
tiltfive::Glasses::createConnectionHelper
auto createConnectionHelper(const std::string &displayName, std::chrono::milliseconds connectionPollInterval=std::chrono::milliseconds(100)) -> std::unique_ptr< GlassesConnectionHelper >
Create a GlassesConnectionHelper.
Definition: TiltFiveNative.hpp:736
tiltfive::Glasses::getWandStreamHelper
auto getWandStreamHelper() -> std::shared_ptr< WandStreamHelper >
Get a WandStreamHelper.
Definition: TiltFiveNative.hpp:719
tiltfive::Glasses::getIdentifier
auto getIdentifier() const -> std::string
Obtain a hardware (not user facing) identifier for the glasses.
Definition: TiltFiveNative.hpp:389
tiltfive::Glasses::ensureReady
auto ensureReady() -> Result< void >
Ensure that reserved glasses are ready for exclusive operations.
Definition: TiltFiveNative.hpp:531
tiltfive::Glasses::getFriendlyName
auto getFriendlyName() -> Result< std::string >
Get the user-facing name of the glasses.
Definition: TiltFiveNative.hpp:476
tiltfive::GlassesConnectionHelper
Utility class to automate the Glasses exclusive connection process.
Definition: TiltFiveNative.hpp:764
tiltfive::GlassesConnectionHelper::awaitConnection
auto awaitConnection(const std::chrono::milliseconds timeout) -> Result< void >
Block until a connection is established or timed out.
Definition: TiltFiveNative.hpp:871
tiltfive::GlassesConnectionHelper::awaitConnection
auto awaitConnection() -> Result< void >
Block until a connection is established.
Definition: TiltFiveNative.hpp:850
tiltfive::GlassesConnectionHelper::glasses
auto glasses() -> Glasses &
Obtain a reference to the wrapped tiltfive::Glasses object.
Definition: TiltFiveNative.hpp:845
tiltfive::GlassesConnectionHelper::obtainGlassesConnectionHelper
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.
Definition: TiltFiveNative.hpp:1384
tiltfive::GlassesConnectionHelper::consumeLastAsyncError
auto consumeLastAsyncError() -> std::error_code
Obtain and consume the last asynchronous error.
Definition: TiltFiveNative.hpp:901
tiltfive::WandStreamHelper
Utility class to manage the wand stream.
Definition: TiltFiveNative.hpp:923
tiltfive::WandStreamHelper::listWands
auto listWands() -> Result< std::vector< std::shared_ptr< Wand > > >
Obtain a list of tiltfive::Wand.
Definition: TiltFiveNative.hpp:1089
tiltfive::WandStreamHelper::obtainWandStreamHelper
friend auto obtainWandStreamHelper(std::shared_ptr< Glasses > glasses, std::chrono::milliseconds pollTimeout) -> std::shared_ptr< WandStreamHelper >
Internal utility function - Do not call directly.
Definition: TiltFiveNative.hpp:1371
tiltfive::WandStreamHelper::consumeLastAsyncError
auto consumeLastAsyncError() -> std::error_code
Obtain and consume the last asynchronous error.
Definition: TiltFiveNative.hpp:1078
tiltfive::ParamChangeListener
Virtual base class for use with tiltfive::ParamChangeHelper.
Definition: TiltFiveNative.hpp:1129
tiltfive::ParamChangeListener::onSysParamChanged
virtual auto onSysParamChanged(const std::vector< T5_ParamSys > &changed) -> void=0
Called by a tiltfive::ParamChangeHelper when system-wide (T5_ParamSys) params have changed.
tiltfive::ParamChangeListener::onGlassesParamChanged
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.
tiltfive::ParamChangeHelper
Utility class to track changes to parameters Using ParamChangeHelper.
Definition: TiltFiveNative.hpp:1147
tiltfive::ParamChangeHelper::registerGlasses
auto registerGlasses(const std::shared_ptr< Glasses > &glasses) -> void
Register glasses for parameter change tracking.
Definition: TiltFiveNative.hpp:1293
tiltfive::ParamChangeHelper::obtainParamChangeHelper
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.
Definition: TiltFiveNative.hpp:1394
tiltfive::ParamChangeHelper::deregisterGlasses
auto deregisterGlasses(const std::shared_ptr< Glasses > &glasses) -> void
De-register glasses for parameter change tracking.
Definition: TiltFiveNative.hpp:1299
tiltfive::ParamChangeHelper::consumeLastAsyncError
auto consumeLastAsyncError() -> std::error_code
Obtain and consume the last asynchronous error.
Definition: TiltFiveNative.hpp:1287
tiltfive::Wand
Represents an abstract instance of a Tilt Five™ wand Used with tiltfive::WandStreamHelper.
Definition: TiltFiveNative.hpp:1307
tiltfive::Wand::handle
T5_WandHandle handle() const
Get the wand handle.
Definition: TiltFiveNative.hpp:1337
tiltfive::Wand::obtainWand
friend auto obtainWand(T5_WandHandle handle, std::shared_ptr< WandStreamHelper > wandStreamHelper) -> std::shared_ptr< Wand >
Internal utility function - Do not call directly.
Definition: TiltFiveNative.hpp:1378
tiltfive::Wand::getLatestReport
auto getLatestReport() const -> Result< T5_WandReport >
Get the latest wand report for this wand.
Definition: TiltFiveNative.hpp:1332
T5_GameboardSize
Physical dimensions of a gameboard.
Definition: types.h:113
T5_ClientInfo
Client provided information for use with t5CreateGlasses()
Definition: types.h:136
T5_ClientInfo::sdkType
uint8_t sdkType
The SDK type.
Definition: types.h:146
T5_ClientInfo::applicationId
const char * applicationId
The application ID.
Definition: types.h:138
T5_ClientInfo::applicationVersion
const char * applicationVersion
The application version.
Definition: types.h:141
T5_GlassesPose
Glasses pose information to be retrieved with t5GetGlassesPose()
Definition: types.h:199
T5_GlassesPose::timestampNanos
uint64_t timestampNanos
The timestamp of the pose.
Definition: types.h:201
T5_GlassesPose::posGLS_GBD
T5_Vec3 posGLS_GBD
The position of the origin of the GLS (glasses) frame relative to the GBD (gameboard) frame.
Definition: types.h:205
T5_GlassesPose::rotToGLS_GBD
T5_Quat rotToGLS_GBD
The rotation that transforms points in the GBD (gameboard) frame orientation to the GLS (glasses) fra...
Definition: types.h:209
T5_GlassesPose::gameboardType
T5_GameboardType gameboardType
The type of gameboard visible for this pose.
Definition: types.h:212
T5_CameraStreamConfig
Camera stream configuration.
Definition: types.h:216
T5_FrameInfo
Render information to be used with t5SendFrameToGlasses()
Definition: types.h:225
T5_CamImage
Camera Frame information to be retrieved with t5GetFilledCamImageBuffer()
Definition: types.h:276
T5_WandStreamConfig
Wand stream configuration.
Definition: types.h:312
T5_WandReport
Contains wand related information (Pose, Buttons, Trigger, Stick, Battery)
Definition: types.h:346
T5_WandReport::poseValid
bool poseValid
Validity of pose parameters. True = valid.
Definition: types.h:360
T5_WandReport::rotToWND_GBD
T5_Quat rotToWND_GBD
WND/GBD rotation unit quaternion.
Definition: types.h:388
T5_WandReport::buttons
struct T5_WandReport::@5 buttons
Buttons state. True = Pressed.
T5_WandReport::posGrip_GBD
T5_Vec3 posGrip_GBD
Position (Grip) - Vector3f.
Definition: types.h:397
T5_WandReport::trigger
float trigger
Trigger - Analog, Range [0.0 - 1.0], 1.0 = Fully depressed.
Definition: types.h:363
T5_WandReport::analogValid
bool analogValid
Validity of analog parameters. True = valid.
Definition: types.h:351
T5_WandReport::buttonsValid
bool buttonsValid
Validity of button parameters. True = valid.
Definition: types.h:357
T5_WandReport::stick
T5_Vec2 stick
Stick (X/Y) - Analog, Range [-1.0 - 1.0], 0 = Centered, 1.0 = Top/Right.
Definition: types.h:366
T5_WandStreamEvent
Represents an event from the wand stream.
Definition: types.h:404
basic_string< char >
basic_string< char >::c_str
const char * c_str() const noexcept
basic_string< char >::length
size_type length() const noexcept
std::shared_ptr< tiltfive::Client >
std::weak_ptr
std::enable_shared_from_this
std::mutex
std::lock_guard
std::thread
std::map< T5_WandHandle, T5_WandReport >
std::map::insert
void insert(_InputIterator __first, _InputIterator __last)
std::map::end
const_iterator end() const noexcept
std::map::cend
const_iterator cend() const noexcept
std::map::erase
size_type erase(const key_type &__x)
std::map::find
iterator find(const key_type &__x)
std::map::cbegin
const_iterator cbegin() const noexcept
std::set
std::set::end
iterator end() const noexcept
std::set::insert
void insert(_InputIterator __first, _InputIterator __last)
std::set::erase
size_type erase(const key_type &__x)
std::pair
std::vector
std::vector::resize
void resize(size_type __new_size)
std::vector::push_back
void push_back(const value_type &__x)
std::vector::reserve
void reserve(size_type __n)
std::vector::data
_Tp * data() noexcept
std::vector::size
size_type size() const noexcept
std::unique_ptr
std::unique_ptr::get
pointer get() const noexcept