/** * @file * * @author Mamadou Babaei * * @section LICENSE * * (The MIT License) * * Copyright (c) 2020 - 2026 SenseGlove * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * @section DESCRIPTION * * */ #include "SGBackend/SGBackend.h" #include "Engine/Engine.h" #include "Templates/Atomic.h" #if PLATFORM_ANDROID #include "SGConnect/SGConnect.h" #endif /* PLATFORM_ANDROID */ #include "SGCore/SGDeviceList.h" #include "SGCore/SGHandLayer.h" #include "SGLog/SGLog.h" struct USGBackend::FImpl { /************************ * Member variables ************************/ TAtomic bInitialized; /************************ * Owner object ************************/ USGBackend* Owner; /************************ * Constructor / Destructor ************************/ explicit FImpl(USGBackend* InOwner); ~FImpl(); /************************ * Default copy constructor & copy assignment operator ************************/ FImpl(const FImpl& Rhs) = delete; FImpl& operator=(const FImpl& Rhs) = delete; }; USGBackend* USGBackend::GetInstance() { if (!ensureAlwaysMsgf(GEngine, TEXT("The engine has not been initialized!"))) { return nullptr; } USGBackend* Instance{GEngine->GetEngineSubsystem()}; return Instance; } USGBackend::USGBackend() : Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter)) { } bool USGBackend::IsBackendInitialized() const { return Pimpl->bInitialized; } bool USGBackend::InitializeBackend() { if (!IsBackendInitialized()) { #if PLATFORM_ANDROID const int32 Result = FSGConnect::Init(); switch (Result) { case -103: SGLOG_ERROR("Android JNI-specific Error: Invalid Java method ID!"); return false; case -102: SGLOG_ERROR("Android JNI-specific Error: Invalid Java class!"); return false; case -101: SGLOG_ERROR("Android JNI-specific Error: Invalid Java environment!"); return false; case -100: SGLOG_ERROR("Android JNI-specific Error: Generic Error! Note: you should never see this!"); return false; case -3: SGLOG_ERROR("An Unexpected error occurred. Please try again."); return false; case -2: SGLOG_ERROR("Device Scanning is already running within a different program."); return false; case -1: SGLOG_ERROR("Device Scanning already being initialized (function called twice in short succession)."); return false; case 0: SGLOG_ERROR("Device Scanning is already running within this program."); return false; case 1: SGLOG("Successfully started up DeviceScanning from the current program."); break; default: ensureAlwaysMsgf(false, TEXT("Unhandled Init return status code!")); return false; } #endif /* PLATFORM_ANDROID */ // Ensure that the device list is empty before doing anything. FSGDeviceList::Dispose(); FSGDeviceList::Reinitialize(); Pimpl->bInitialized = true; return true; } return false; } bool USGBackend::UninitializeBackend() { if (IsBackendInitialized()) { FSGDeviceList::Dispose(); #if PLATFORM_ANDROID const int32 Result = FSGConnect::Dispose(); switch (Result) { case -103: SGLOG_ERROR("Android JNI-specific Error: Invalid Java method ID!"); return false; case -102: SGLOG_ERROR("Android JNI-specific Error: Invalid Java class!"); return false; case -101: SGLOG_ERROR("Android JNI-specific Error: Invalid Java environment!"); return false; case -100: SGLOG_ERROR("Android JNI-specific Error: Generic Error! Note: you should never see this!"); return false; case -3: SGLOG_ERROR("An Unexpected error occurred. Please try again."); return false; case -2: SGLOG_ERROR("Not allowed to dispose of Device Scanning because this is not the program which started it."); return false; case -1: SGLOG_ERROR("Device Scanning is currently being disposed off. This takes a second or two. (function called twice in short succession)."); return false; case 0: SGLOG_ERROR("There is no deviceScanner running from any program, so disposing is skipped."); return false; case 1: SGLOG("Successfully disposed of DeviceScanner resources."); break; default: ensureAlwaysMsgf(false, TEXT("Unhandled Dispose return status code!")); return false; } #endif /* PLATFORM_ANDROID */ Pimpl->bInitialized = false; return true; } return false; } bool USGBackend::ShouldCreateSubsystem(UObject* Outer) const { return true; } void USGBackend::Initialize(FSubsystemCollectionBase& Collection) { SGLOG("Initializing the SenseGlove Backend singleton with the engine lifetime..."); Super::Initialize(Collection); SGLOG("Initializing the SenseGlove Backend singleton with the engine lifetime..."); if (InitializeBackend()) { SGLOG("The bridge to the SenseGlove Backend has been initialized successfully!") } else { SGLOG_ERROR("Failed to initialize the bridge to the SenseGlove Backend!") } SGLOG("The SenseGlove Backend singleton has been successfully initialized with the engine lifetime!"); } void USGBackend::Deinitialize() { SGLOG("De-initializing the SenseGlove Backend singleton due to the engine lifetime expiration..."); Super::Deinitialize(); if (UninitializeBackend()) { SGLOG("The bridge to the SenseGlove Backend has been uninitialized successfully!") } else { SGLOG_ERROR("Failed to uninitialize the bridge to the SenseGlove Backend!") } (void) Pimpl.Release(); SGLOG("The SenseGlove Backend singleton has been successfully de-initialized!"); } USGBackend::FImpl::FImpl(USGBackend* InOwner) : bInitialized(false), Owner(InOwner) { } USGBackend::FImpl::~FImpl() = default; void USGBackend::FImplDeleter::operator()(const FImpl* P) const { delete P; }