Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/openxr/src/common/platform_utils.hpp
9903 views
1
// Copyright (c) 2017-2025 The Khronos Group Inc.
2
// Copyright (c) 2017-2019 Valve Corporation
3
// Copyright (c) 2017-2019 LunarG, Inc.
4
//
5
// SPDX-License-Identifier: Apache-2.0 OR MIT
6
//
7
// Initial Authors: Mark Young <[email protected]>, Dave Houlton <[email protected]>
8
//
9
10
#pragma once
11
12
#include "xr_dependencies.h"
13
#include <string>
14
#include <stdint.h>
15
#include <stdlib.h>
16
17
// OpenXR paths and registry key locations
18
#define OPENXR_RELATIVE_PATH "openxr/"
19
#define OPENXR_IMPLICIT_API_LAYER_RELATIVE_PATH "/api_layers/implicit.d"
20
#define OPENXR_EXPLICIT_API_LAYER_RELATIVE_PATH "/api_layers/explicit.d"
21
#ifdef XR_OS_WINDOWS
22
#define OPENXR_REGISTRY_LOCATION "SOFTWARE\\Khronos\\OpenXR\\"
23
#define OPENXR_IMPLICIT_API_LAYER_REGISTRY_LOCATION "\\ApiLayers\\Implicit"
24
#define OPENXR_EXPLICIT_API_LAYER_REGISTRY_LOCATION "\\ApiLayers\\Explicit"
25
#endif
26
27
// OpenXR Loader environment variables of interest
28
#define OPENXR_RUNTIME_JSON_ENV_VAR "XR_RUNTIME_JSON"
29
#define OPENXR_API_LAYER_PATH_ENV_VAR "XR_API_LAYER_PATH"
30
31
// This is a CMake generated file with #defines for any functions/includes
32
// that it found present and build-time configuration.
33
// If you don't have this file, on non-Windows you'll need to define
34
// one of HAVE_SECURE_GETENV or HAVE___SECURE_GETENV depending on which
35
// of secure_getenv or __secure_getenv are present
36
#ifdef OPENXR_HAVE_COMMON_CONFIG
37
#include "common_config.h"
38
#endif // OPENXR_HAVE_COMMON_CONFIG
39
40
#if defined(__x86_64__) && defined(__ILP32__)
41
#define XR_ARCH_ABI "x32"
42
#elif defined(_M_X64) || defined(__x86_64__)
43
#define XR_ARCH_ABI "x86_64"
44
#elif defined(_M_IX86) || defined(__i386__) || defined(_X86_)
45
#define XR_ARCH_ABI "i686"
46
#elif (defined(__aarch64__) && defined(__LP64__)) || defined(_M_ARM64)
47
#define XR_ARCH_ABI "aarch64"
48
#elif (defined(__ARM_ARCH) && __ARM_ARCH >= 7 && (defined(__ARM_PCS_VFP) || defined(__ANDROID__))) || defined(_M_ARM)
49
#define XR_ARCH_ABI "armv7a-vfp"
50
#elif defined(__ARM_ARCH_5TE__) || (defined(__ARM_ARCH) && __ARM_ARCH > 5)
51
#define XR_ARCH_ABI "armv5te"
52
#elif defined(__mips64)
53
#define XR_ARCH_ABI "mips64"
54
#elif defined(__mips)
55
#define XR_ARCH_ABI "mips"
56
#elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
57
#define XR_ARCH_ABI "ppc64"
58
#elif defined(__powerpc__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
59
#define XR_ARCH_ABI "ppc64el"
60
#elif defined(__s390x__) || defined(__zarch__)
61
#define XR_ARCH_ABI "s390x"
62
#elif defined(__hppa__)
63
#define XR_ARCH_ABI "hppa"
64
#elif defined(__alpha__)
65
#define XR_ARCH_ABI "alpha"
66
#elif defined(__ia64__) || defined(_M_IA64)
67
#define XR_ARCH_ABI "ia64"
68
#elif defined(__m68k__)
69
#define XR_ARCH_ABI "m68k"
70
#elif defined(__riscv_xlen) && (__riscv_xlen == 64)
71
#define XR_ARCH_ABI "riscv64"
72
#elif defined(__sparc__) && defined(__arch64__)
73
#define XR_ARCH_ABI "sparc64"
74
#elif defined(__loongarch64)
75
#define XR_ARCH_ABI "loongarch64"
76
#else
77
#error "No architecture string known!"
78
#endif
79
80
// Consumers of this file must ensure this function is implemented. For example, the loader will implement this function so that it
81
// can route messages through the loader's logging system.
82
void LogPlatformUtilsError(const std::string& message);
83
84
// Environment variables
85
#if defined(XR_OS_LINUX) || defined(XR_OS_APPLE)
86
87
#include <unistd.h>
88
#include <fcntl.h>
89
#include <iostream>
90
#include <sys/stat.h>
91
92
namespace detail {
93
94
static inline char* ImplGetEnv(const char* name) { return getenv(name); }
95
96
// clang-format off
97
static inline char* ImplGetSecureEnv(const char* name) {
98
#ifdef HAVE_SECURE_GETENV
99
return secure_getenv(name);
100
#elif defined(HAVE___SECURE_GETENV)
101
return __secure_getenv(name);
102
#else
103
#pragma message( \
104
"Warning: Falling back to non-secure getenv for environmental" \
105
"lookups! Consider updating to a different libc.")
106
107
return ImplGetEnv(name);
108
#endif
109
}
110
// clang-format on
111
112
} // namespace detail
113
114
#endif // defined(XR_OS_LINUX) || defined(XR_OS_APPLE)
115
116
#if defined(XR_OS_ANDROID) || defined(XR_OS_APPLE)
117
118
#include <sys/stat.h>
119
120
namespace detail {
121
122
static inline bool ImplTryRuntimeFilename(const char* rt_dir_prefix, uint16_t major_version, std::string& file_name) {
123
auto decorated_path = rt_dir_prefix + std::to_string(major_version) + "/active_runtime." XR_ARCH_ABI ".json";
124
auto undecorated_path = rt_dir_prefix + std::to_string(major_version) + "/active_runtime.json";
125
126
struct stat buf {};
127
if (0 == stat(decorated_path.c_str(), &buf)) {
128
file_name = decorated_path;
129
return true;
130
}
131
if (0 == stat(undecorated_path.c_str(), &buf)) {
132
file_name = undecorated_path;
133
return true;
134
}
135
return false;
136
}
137
138
} // namespace detail
139
#endif // defined(XR_OS_ANDROID) || defined(XR_OS_APPLE)
140
#if defined(XR_OS_LINUX)
141
142
static inline std::string PlatformUtilsGetEnv(const char* name) {
143
auto str = detail::ImplGetEnv(name);
144
if (str == nullptr) {
145
return {};
146
}
147
return str;
148
}
149
150
static inline std::string PlatformUtilsGetSecureEnv(const char* name) {
151
auto str = detail::ImplGetSecureEnv(name);
152
if (str == nullptr) {
153
str = detail::ImplGetEnv(name);
154
if (str != nullptr && !std::string(str).empty()) {
155
LogPlatformUtilsError(std::string("!!! WARNING !!! Environment variable ") + name +
156
" is being ignored due to running with secure execution. The value '" + str +
157
"' will NOT be used.");
158
}
159
return {};
160
}
161
return str;
162
}
163
164
static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; }
165
166
#elif defined(XR_OS_APPLE)
167
168
static inline std::string PlatformUtilsGetEnv(const char* name) {
169
auto str = detail::ImplGetEnv(name);
170
if (str == nullptr) {
171
return {};
172
}
173
return str;
174
}
175
176
static inline std::string PlatformUtilsGetSecureEnv(const char* name) {
177
auto str = detail::ImplGetSecureEnv(name);
178
if (str == nullptr) {
179
return {};
180
}
181
return str;
182
}
183
184
static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; }
185
186
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) {
187
return detail::ImplTryRuntimeFilename("/usr/local/share/openxr/", major_version, file_name);
188
}
189
190
#elif defined(XR_OS_WINDOWS)
191
192
inline std::wstring utf8_to_wide(const std::string& utf8Text) {
193
if (utf8Text.empty()) {
194
return {};
195
}
196
197
std::wstring wideText;
198
const int wideLength = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.data(), (int)utf8Text.size(), nullptr, 0);
199
if (wideLength == 0) {
200
LogPlatformUtilsError("utf8_to_wide get size error: " + std::to_string(::GetLastError()));
201
return {};
202
}
203
204
// MultiByteToWideChar returns number of chars of the input buffer, regardless of null terminator
205
wideText.resize(wideLength, 0);
206
wchar_t* wideString = const_cast<wchar_t*>(wideText.data()); // mutable data() only exists in c++17
207
const int length = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.data(), (int)utf8Text.size(), wideString, wideLength);
208
if (length != wideLength) {
209
LogPlatformUtilsError("utf8_to_wide convert string error: " + std::to_string(::GetLastError()));
210
return {};
211
}
212
213
return wideText;
214
}
215
216
inline std::string wide_to_utf8(const std::wstring& wideText) {
217
if (wideText.empty()) {
218
return {};
219
}
220
221
std::string narrowText;
222
int narrowLength = ::WideCharToMultiByte(CP_UTF8, 0, wideText.data(), (int)wideText.size(), nullptr, 0, nullptr, nullptr);
223
if (narrowLength == 0) {
224
LogPlatformUtilsError("wide_to_utf8 get size error: " + std::to_string(::GetLastError()));
225
return {};
226
}
227
228
// WideCharToMultiByte returns number of chars of the input buffer, regardless of null terminator
229
narrowText.resize(narrowLength, 0);
230
char* narrowString = const_cast<char*>(narrowText.data()); // mutable data() only exists in c++17
231
const int length =
232
::WideCharToMultiByte(CP_UTF8, 0, wideText.data(), (int)wideText.size(), narrowString, narrowLength, nullptr, nullptr);
233
if (length != narrowLength) {
234
LogPlatformUtilsError("wide_to_utf8 convert string error: " + std::to_string(::GetLastError()));
235
return {};
236
}
237
238
return narrowText;
239
}
240
241
// Returns true if the current process has an integrity level > SECURITY_MANDATORY_MEDIUM_RID.
242
static inline bool IsHighIntegrityLevel() {
243
// Execute this check once and save the value as a static bool.
244
static bool isHighIntegrityLevel = ([] {
245
HANDLE processToken;
246
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_QUERY_SOURCE, &processToken)) {
247
// Maximum possible size of SID_AND_ATTRIBUTES is maximum size of a SID + size of attributes DWORD.
248
uint8_t mandatoryLabelBuffer[SECURITY_MAX_SID_SIZE + sizeof(DWORD)]{};
249
DWORD bufferSize;
250
if (GetTokenInformation(processToken, TokenIntegrityLevel, mandatoryLabelBuffer, sizeof(mandatoryLabelBuffer),
251
&bufferSize) != 0) {
252
const auto mandatoryLabel = reinterpret_cast<const TOKEN_MANDATORY_LABEL*>(mandatoryLabelBuffer);
253
if (mandatoryLabel->Label.Sid != 0) {
254
const DWORD subAuthorityCount = *GetSidSubAuthorityCount(mandatoryLabel->Label.Sid);
255
const DWORD integrityLevel = *GetSidSubAuthority(mandatoryLabel->Label.Sid, subAuthorityCount - 1);
256
CloseHandle(processToken);
257
return integrityLevel > SECURITY_MANDATORY_MEDIUM_RID;
258
}
259
}
260
261
CloseHandle(processToken);
262
}
263
264
return false;
265
})();
266
267
return isHighIntegrityLevel;
268
}
269
270
// Returns true if the given environment variable exists.
271
// The name is a case-sensitive UTF8 string.
272
static inline bool PlatformUtilsGetEnvSet(const char* name) {
273
const std::wstring wname = utf8_to_wide(name);
274
const DWORD valSize = ::GetEnvironmentVariableW(wname.c_str(), nullptr, 0);
275
// GetEnvironmentVariable returns 0 when environment variable does not exist or there is an error.
276
return 0 != valSize;
277
}
278
279
// Returns the environment variable value for the given name.
280
// Returns an empty string if the environment variable doesn't exist or if it exists but is empty.
281
// Use PlatformUtilsGetEnvSet to tell if it exists.
282
// The name is a case-sensitive UTF8 string.
283
static inline std::string PlatformUtilsGetEnv(const char* name) {
284
const std::wstring wname = utf8_to_wide(name);
285
const DWORD valSize = ::GetEnvironmentVariableW(wname.c_str(), nullptr, 0);
286
// GetEnvironmentVariable returns 0 when environment variable does not exist or there is an error.
287
// The size includes the null-terminator, so a size of 1 is means the variable was explicitly set to empty.
288
if (valSize == 0 || valSize == 1) {
289
return {};
290
}
291
292
// GetEnvironmentVariable returns size including null terminator for "query size" call.
293
std::wstring wValue(valSize, 0);
294
wchar_t* wValueData = &wValue[0];
295
296
// GetEnvironmentVariable returns string length, excluding null terminator for "get value"
297
// call if there was enough capacity. Else it returns the required capacity (including null terminator).
298
const DWORD length = ::GetEnvironmentVariableW(wname.c_str(), wValueData, (DWORD)wValue.size());
299
if ((length == 0) || (length >= wValue.size())) { // If error or the variable increased length between calls...
300
LogPlatformUtilsError("GetEnvironmentVariable get value error: " + std::to_string(::GetLastError()));
301
return {};
302
}
303
304
wValue.resize(length); // Strip the null terminator.
305
306
return wide_to_utf8(wValue);
307
}
308
309
// Acts the same as PlatformUtilsGetEnv except returns an empty string if IsHighIntegrityLevel.
310
static inline std::string PlatformUtilsGetSecureEnv(const char* name) {
311
// No secure version for Windows so the below integrity check is needed.
312
const std::string envValue = PlatformUtilsGetEnv(name);
313
314
// Do not allow high integrity processes to act on data that can be controlled by medium integrity processes.
315
// Specifically, medium integrity processes can set environment variables which could then
316
// be read by high integrity processes.
317
if (IsHighIntegrityLevel()) {
318
if (!envValue.empty()) {
319
LogPlatformUtilsError(std::string("!!! WARNING !!! Environment variable ") + name +
320
" is being ignored due to running from an elevated context. The value '" + envValue +
321
"' will NOT be used.");
322
}
323
return {};
324
}
325
326
return envValue;
327
}
328
329
#elif defined(XR_OS_ANDROID)
330
331
#include <sys/system_properties.h>
332
333
static inline bool PlatformUtilsGetEnvSet(const char* /* name */) {
334
// Stub func
335
return false;
336
}
337
338
static inline std::string PlatformUtilsGetEnv(const char* /* name */) {
339
// Stub func
340
return {};
341
}
342
343
static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) {
344
// Stub func
345
return {};
346
}
347
348
// Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases
349
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) {
350
// Prefix for the runtime JSON file name
351
static const char* rt_dir_prefixes[] = {"/product", "/odm", "/oem", "/vendor", "/system"};
352
353
static const std::string subdir = "/etc/openxr/";
354
for (const auto prefix : rt_dir_prefixes) {
355
const std::string rt_dir_prefix = prefix + subdir;
356
if (detail::ImplTryRuntimeFilename(rt_dir_prefix.c_str(), major_version, file_name)) {
357
return true;
358
}
359
}
360
361
return false;
362
}
363
364
// Android system properties are sufficiently different from environment variables that we are not re-using
365
// PlatformUtilsGetEnv for this purpose
366
static inline std::string PlatformUtilsGetAndroidSystemProperty(const char* name) {
367
std::string result;
368
const prop_info* pi = __system_property_find(name);
369
if (pi == nullptr) {
370
return {};
371
}
372
373
#if __ANDROID_API__ >= 26
374
// use callback to retrieve > 92 character sys prop values, if available
375
__system_property_read_callback(
376
pi,
377
[](void* cookie, const char*, const char* value, uint32_t) {
378
auto property_value = reinterpret_cast<std::string*>(cookie);
379
*property_value = value;
380
},
381
reinterpret_cast<void*>(&result));
382
#endif // __ANDROID_API__ >= 26
383
// fallback to __system_property_get if no value retrieved via callback
384
if (result.empty()) {
385
char value[PROP_VALUE_MAX] = {};
386
if (__system_property_get(name, value) != 0) {
387
result = value;
388
}
389
}
390
391
return result;
392
}
393
394
#else // Not Linux, Apple, nor Windows
395
396
static inline bool PlatformUtilsGetEnvSet(const char* /* name */) {
397
// Stub func
398
return false;
399
}
400
401
static inline std::string PlatformUtilsGetEnv(const char* /* name */) {
402
// Stub func
403
return {};
404
}
405
406
static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) {
407
// Stub func
408
return {};
409
}
410
411
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t /* major_version */, std::string const& /* file_name */) {
412
// Stub func
413
return false;
414
}
415
416
#endif
417
418