Initial release 0.1.0

This commit is contained in:
KominoVR committed 2026-09-25 15:50:08 +02:00
commit c4fc8662d1
62 files changed
+69433

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
* text=auto
*.sh text eol=lf
*.json text eol=lf
*.md text eol=lf
*.cpp text eol=lf
*.hpp text eol=lf
+30
View File
@@ -0,0 +1,30 @@
name: Bug report
description: Report a problem with passthrough shortcuts.
title: "[Bug] "
body:
- type: textarea
id: problem
attributes:
label: What happened?
description: Include what you expected and how to reproduce the problem.
validations:
required: true
- type: input
id: versions
attributes:
label: Versions
description: App version, SteamOS version, and SteamVR version.
validations:
required: true
- type: dropdown
id: camera
attributes:
label: Arcturus RGB camera
options:
- Connected
- Not connected
- type: textarea
id: additional
attributes:
label: Additional details
description: Include custom bindings or relevant log excerpts. Remove personal information.
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: true
@@ -0,0 +1,11 @@
name: Feature request
description: Suggest an improvement.
title: "[Feature] "
body:
- type: textarea
id: request
attributes:
label: What would you like to do?
description: Describe the problem and the behavior you would like.
validations:
required: true
+4
View File
@@ -0,0 +1,4 @@
/build*/
/dist/
/.zig-cache/
*.log
+59
View File
@@ -0,0 +1,59 @@
cmake_minimum_required(VERSION 3.16)
project(FramePassthroughShortcuts VERSION 0.1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
foreach(config Debug Release RelWithDebInfo MinSizeRel)
string(TOUPPER "${config}" config_upper)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config_upper} "${CMAKE_BINARY_DIR}/bin")
endforeach()
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message(FATAL_ERROR "Only 64-bit builds are supported.")
endif()
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux" OR NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)$")
message(FATAL_ERROR "Build for Linux ARM64 (Steam Frame).")
endif()
include(cmake/OpenVRLoader.cmake)
find_package(Threads REQUIRED)
add_library(fps_core STATIC src/config.cpp src/controller.cpp src/openvr_camera_source.cpp)
target_include_directories(fps_core PUBLIC src third_party/json)
target_link_libraries(fps_core PUBLIC openvr_loader Threads::Threads)
target_compile_definitions(fps_core PUBLIC FPS_VERSION="${PROJECT_VERSION}")
add_executable(frame-passthrough-shortcuts src/main.cpp)
target_link_libraries(frame-passthrough-shortcuts PRIVATE fps_core)
target_compile_options(fps_core PRIVATE -Wall -Wextra -Wpedantic)
target_compile_options(frame-passthrough-shortcuts PRIVATE -Wall -Wextra -Wpedantic)
# Keep the device bundle independent of the compiler's libstdc++ version.
target_link_options(frame-passthrough-shortcuts PRIVATE -static-libgcc -static-libstdc++)
add_custom_command(TARGET frame-passthrough-shortcuts POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_CURRENT_SOURCE_DIR}/assets"
"$<TARGET_FILE_DIR:frame-passthrough-shortcuts>"
COMMENT "Copying configuration and SteamVR bindings")
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
# SteamVR identifies clients by executable path, even Utility clients.
# Management commands need a distinct real executable to avoid taking the
# resident shortcut application's process identity.
add_custom_command(TARGET frame-passthrough-shortcuts POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$<TARGET_FILE:frame-passthrough-shortcuts>"
"$<TARGET_FILE_DIR:frame-passthrough-shortcuts>/frame-passthrough-control"
COMMENT "Copying SteamVR management companion")
endif()
install(TARGETS frame-passthrough-shortcuts RUNTIME DESTINATION .)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
install(PROGRAMS "$<TARGET_FILE:frame-passthrough-shortcuts>"
DESTINATION . RENAME frame-passthrough-control)
endif()
install(DIRECTORY assets/ DESTINATION .)
install(FILES README.md LICENSE DESTINATION .)
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Frame Passthrough Shortcuts contributors
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.
+71
View File
@@ -0,0 +1,71 @@
# Frame Passthrough Shortcuts
Change Steam Frame passthrough modes from your controllers. Switch between
translucent passthrough, which shows your surroundings alongside immersive VR content,
and opaque passthrough, which only shows your surroundings.
It works with the Arcturus RGB camera!
You can switch between RGB and monochrome cameras (Day and Night vision 😎).
[![Install with FrameDrop](images/framedrop-button.svg)](https://framedropvr.com/install?manifest=https%3A%2F%2Fgithub.com%2FKominoVR%2Fframe-passthrough-shortcuts%2Freleases%2Flatest%2Fdownload%2Fframe-passthrough-shortcuts.framedrop.json)
Download the [Linux ARM64 package](https://github.com/KominoVR/frame-passthrough-shortcuts/releases/download/v0.1.0/frame-passthrough-shortcuts-0.1.0-linux-arm64.zip) if you prefer to install manually.
## Controls
| Gesture on either thumbstick | Action |
| --- | --- |
| Double-press | Toggle translucent / opaque passthrough |
| Hold for one second | Toggle Arcturus RGB camera / built-in monochrome cameras |
**These only work with the SteamVR dashboard closed!**
**Open passthrough first with its usual system button (double-press
the headset side button or the Steam button on either controller)**
All actions can be changed in **SteamVR controller bindings → Frame Passthrough
Shortcuts**, including separate translucent, opaque, RGB, and monochrome
actions.
## Compatibility
- Runs locally on Steam Frame.
- Arcturus RGB camera is optional.
- Shortcuts require the SteamVR dashboard to be closed.
- Selecting monochrome only changes the camera source; it does not power
off the Arcturus RGB camera.
- Camera switching uses SteamVR’s internal camera controls. Updates may affect
compatibility. (Please report any issues)
## Manual installation
Copy the Linux ARM64 ZIP to the Frame, connect over SSH, and run:
```sh
unzip -oq ~/frame-passthrough-shortcuts-0.1.0-linux-arm64.zip -d ~/devkit-game/Frame_Passthrough_Shortcuts
~/devkit-game/Frame_Passthrough_Shortcuts/frame-passthrough-shortcuts --install
```
`--install` registers the OpenVR manifest and enables SteamVR autostart. To
remove the registration, package files, bindings, and app settings:
```sh
~/devkit-game/Frame_Passthrough_Shortcuts/frame-passthrough-shortcuts --uninstall
```
## Build
On Linux ARM64, with CMake and a C++17 compiler installed:
```sh
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
node scripts/package.mjs --build-dir build --platform linux-arm64
```
Packaging requires Node.js and writes ZIP archives and SHA-256 checksums to
`dist/`.
[MIT licensed](LICENSE).
AI disclosure: Developed with assistance from coding agents.
+58
View File
@@ -0,0 +1,58 @@
# Third-party notices
The application links the following vendored dependencies. Their license
notices accompany source distributions and packaged binaries.
## Compiler support libraries
The ARM64 cross-build uses Zig 0.13.0 and statically links its LLVM C++
support libraries. Their notices are included in `third_party/toolchain-licenses/`:
Zig's MIT license, and the libc++, libc++abi, and libunwind licenses. Source:
[Zig 0.13.0](https://ziglang.org/download/0.13.0/release-notes.html).
Linux builds using GCC link with the GCC Runtime Library Exception.
The Linux C library and SteamVR runtime are loaded from the device, not bundled.
## OpenVR SDK
- Source: [ValveSoftware/openvr](https://github.com/ValveSoftware/openvr/tree/0924064316de3effbcd1acf1e309182a2deb1c05)
- Pinned commit: `0924064316de3effbcd1acf1e309182a2deb1c05`
- Copyright (c) 2015, Valve Corporation. All rights reserved.
- License: BSD 3-Clause. The full notice is included at
[third_party/openvr/LICENSE](third_party/openvr/LICENSE).
- The portable loader is compiled from source. Valve's runtime is not bundled.
The SDK includes JsonCpp, used by the loader. Its embedded notice offers public
domain or MIT terms; the MIT notice is reproduced below for binary distributions.
## JSON for Modern C++
- Source: [nlohmann/json v3.11.3](https://github.com/nlohmann/json/tree/v3.11.3)
- Copyright (c) 2013-2022 Niels Lohmann.
- License: MIT. The full notice is included at
[third_party/json/LICENSE.MIT](third_party/json/LICENSE.MIT).
## JsonCpp notice from the OpenVR SDK
```text
Copyright (c) 2007-2010 Baptiste Lepilleur
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.
```
+31
View File
@@ -0,0 +1,31 @@
{
"version": 1,
"minimum_required_version": 1,
"action_sets": [
{ "name": "/actions/shortcuts", "usage": "leftright" }
],
"actions": [
{ "name": "/actions/shortcuts/in/toggle_style", "type": "boolean", "requirement": "optional" },
{ "name": "/actions/shortcuts/in/translucent", "type": "boolean", "requirement": "optional" },
{ "name": "/actions/shortcuts/in/opaque", "type": "boolean", "requirement": "optional" },
{ "name": "/actions/shortcuts/in/toggle_rgb", "type": "boolean", "requirement": "optional" },
{ "name": "/actions/shortcuts/in/rgb", "type": "boolean", "requirement": "optional" },
{ "name": "/actions/shortcuts/in/monochrome", "type": "boolean", "requirement": "optional" }
],
"default_bindings": [
{ "controller_type": "frame_controller", "binding_url": "bindings/frame_controller.json" },
{ "controller_type": "frame_hmd", "binding_url": "bindings/frame_hmd.json" }
],
"localization": [
{
"language_tag": "en_us",
"/actions/shortcuts": "Passthrough shortcuts",
"/actions/shortcuts/in/toggle_style": "Toggle translucent / opaque",
"/actions/shortcuts/in/translucent": "Use translucent passthrough",
"/actions/shortcuts/in/opaque": "Use opaque passthrough",
"/actions/shortcuts/in/toggle_rgb": "Toggle RGB / monochrome",
"/actions/shortcuts/in/rgb": "Use RGB camera",
"/actions/shortcuts/in/monochrome": "Use monochrome cameras"
}
]
}
+44
View File
@@ -0,0 +1,44 @@
{
"action_manifest_version": 1,
"alias_info": {},
"app_key": "local.frame.passthrough.shortcuts",
"bindings": {
"/actions/shortcuts": {
"chords": [],
"haptics": [],
"poses": [],
"skeleton": [],
"sources": [
{
"path": "/user/hand/left/input/thumbstick",
"mode": "button",
"parameters": {
"double_press_delay": 0.25,
"long_press_delay": 1.0
},
"inputs": {
"double": { "output": "/actions/shortcuts/in/toggle_style" },
"long": { "output": "/actions/shortcuts/in/toggle_rgb" }
}
},
{
"path": "/user/hand/right/input/thumbstick",
"mode": "button",
"parameters": {
"double_press_delay": 0.25,
"long_press_delay": 1.0
},
"inputs": {
"double": { "output": "/actions/shortcuts/in/toggle_style" },
"long": { "output": "/actions/shortcuts/in/toggle_rgb" }
}
}
]
}
},
"controller_type": "frame_controller",
"description": "Either thumbstick: double press toggles translucent/opaque; hold for one second toggles RGB/monochrome.",
"name": "Frame Passthrough Shortcuts - thumbstick gestures",
"options": {},
"simulated_actions": []
}
+19
View File
@@ -0,0 +1,19 @@
{
"action_manifest_version": 1,
"alias_info": {},
"app_key": "local.frame.passthrough.shortcuts",
"bindings": {
"/actions/shortcuts": {
"chords": [],
"haptics": [],
"poses": [],
"skeleton": [],
"sources": []
}
},
"controller_type": "frame_hmd",
"description": "No headset buttons assigned; use your configured controller shortcuts.",
"name": "Frame headset — unassigned",
"options": {},
"simulated_actions": []
}
+4
View File
@@ -0,0 +1,4 @@
{
"poll_interval_ms": 20,
"debounce_ms": 250
}
+21
View File
@@ -0,0 +1,21 @@
# Build Valve's portable loader from source; prebuilt SDK binaries are x86-only
# in some SDK releases and cannot be used on Steam Frame's ARM64 CPU.
set(openvr_dir "${CMAKE_CURRENT_SOURCE_DIR}/third_party/openvr")
set(openvr_sources
src/openvr_api_public.cpp
src/jsoncpp.cpp
src/vrcore/dirtools_public.cpp
src/vrcore/envvartools_public.cpp
src/vrcore/pathtools_public.cpp
src/vrcore/sharedlibtools_public.cpp
src/vrcore/hmderrors_public.cpp
src/vrcore/vrpathregistry_public.cpp
src/vrcore/strtools_public.cpp)
list(TRANSFORM openvr_sources PREPEND "${openvr_dir}/")
add_library(openvr_loader STATIC ${openvr_sources})
target_include_directories(openvr_loader SYSTEM PUBLIC "${openvr_dir}/headers")
target_include_directories(openvr_loader PRIVATE "${openvr_dir}/src" "${openvr_dir}/src/vrcore")
target_compile_definitions(openvr_loader PRIVATE VR_API_PUBLIC VRCORE_NO_PLATFORM)
target_compile_definitions(openvr_loader PUBLIC OPENVR_BUILD_STATIC)
target_compile_definitions(openvr_loader PRIVATE LINUX POSIX LINUXARM64)
target_link_libraries(openvr_loader PRIVATE ${CMAKE_DL_LIBS})
+10
View File
@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="260" height="46" viewBox="0 0 260 46"><rect x="0.5" y="0.5" width="259" height="45" fill="#080809" stroke="#e6b54a"/><svg x="10" y="8" xmlns="http://www.w3.org/2000/svg" viewBox="24 11 40 40" width="28" height="28" fill="none" aria-hidden="true">
<path d="M58 23V48H26V42" stroke="#e6b54a" stroke-width="2.5" stroke-linejoin="miter"/>
<path d="M56 16H41" stroke="#c41e3a" stroke-width="2.5"/>
<path d="M41 16H26V20" stroke="#cb3c3d" stroke-width="2.5" stroke-linejoin="miter"/>
<path d="M26 20V26" stroke="#d25d41" stroke-width="2.5"/>
<path d="M26 26V32" stroke="#d97c44" stroke-width="2.5"/>
<path d="M26 32V37" stroke="#df9747" stroke-width="2.5"/>
<path d="M26 37V42" stroke="#e3a949" stroke-width="2.5"/>
<rect x="55" y="13" width="6" height="6" fill="#c41e3a"/>
</svg><text x="48" y="28" fill="#e6b54a" font-family="ui-monospace,monospace" font-size="13" font-weight="600" letter-spacing="0.78">INSTALL WITH FRAMEDROP</text></svg>

After

Width:  |  Height:  |  Size: 1018 B

+42
View File
@@ -0,0 +1,42 @@
param(
[Parameter(Mandatory = $true)][string]$ZigPath,
[string]$BuildDirectory = 'build-arm64'
)
$ErrorActionPreference = 'Stop'
$projectRoot = Split-Path -Parent $PSScriptRoot
$compiler = (Resolve-Path -LiteralPath $ZigPath).Path
$build = [IO.Path]::GetFullPath((Join-Path $projectRoot $BuildDirectory))
New-Item -ItemType Directory -Force -Path (Join-Path $build 'bin') | Out-Null
$oldGlobalCache = $env:ZIG_GLOBAL_CACHE_DIR
$oldLocalCache = $env:ZIG_LOCAL_CACHE_DIR
try {
$env:ZIG_GLOBAL_CACHE_DIR = Join-Path $build 'zig-global-cache'
$env:ZIG_LOCAL_CACHE_DIR = Join-Path $build 'zig-local-cache'
$sources = @(
'src/main.cpp', 'src/config.cpp', 'src/controller.cpp', 'src/openvr_camera_source.cpp',
'third_party/openvr/src/openvr_api_public.cpp',
'third_party/openvr/src/jsoncpp.cpp',
'third_party/openvr/src/vrcore/dirtools_public.cpp',
'third_party/openvr/src/vrcore/envvartools_public.cpp',
'third_party/openvr/src/vrcore/pathtools_public.cpp',
'third_party/openvr/src/vrcore/sharedlibtools_public.cpp',
'third_party/openvr/src/vrcore/hmderrors_public.cpp',
'third_party/openvr/src/vrcore/vrpathregistry_public.cpp',
'third_party/openvr/src/vrcore/strtools_public.cpp'
) | ForEach-Object { Join-Path $projectRoot $_ }
$includes = @('src', 'third_party/json', 'third_party/openvr/headers', 'third_party/openvr/src', 'third_party/openvr/src/vrcore') |
ForEach-Object { '-I' + (Join-Path $projectRoot $_) }
# Tested with Zig 0.13.0. glibc stays dynamic for SteamVR's dlopen loader;
# Zig links libc++ and its C++ ABI implementation statically.
& $compiler c++ -target aarch64-linux-gnu.2.31 -std=c++17 -O2 -fvisibility=hidden -Wno-deprecated-declarations `
-DLINUX -DPOSIX -DLINUXARM64 -DVR_API_PUBLIC -DVRCORE_NO_PLATFORM -DOPENVR_BUILD_STATIC `
@includes @sources -ldl -lpthread -o (Join-Path $build 'bin/frame-passthrough-shortcuts')
if ($LASTEXITCODE -ne 0) { throw "ARM64 compilation failed with exit code $LASTEXITCODE" }
Copy-Item -LiteralPath (Join-Path $build 'bin/frame-passthrough-shortcuts') `
-Destination (Join-Path $build 'bin/frame-passthrough-control') -Force
Copy-Item -Path (Join-Path $projectRoot 'assets/*') -Destination (Join-Path $build 'bin') -Recurse -Force
Write-Output "ARM64 build ready: $build/bin/frame-passthrough-shortcuts"
} finally {
$env:ZIG_GLOBAL_CACHE_DIR = $oldGlobalCache
$env:ZIG_LOCAL_CACHE_DIR = $oldLocalCache
}
+29
View File
@@ -0,0 +1,29 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { createHash } from 'node:crypto';
import { createZip } from './package.mjs';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const entries = [];
function collect(name) {
const full = path.join(root, name);
const stat = fs.lstatSync(full);
if (stat.isSymbolicLink()) throw new Error(`Unexpected symlink: ${name}`);
if (stat.isDirectory()) {
for (const item of fs.readdirSync(full).sort()) {
if (item === '.git' || item === '__pycache__') continue;
collect(`${name}/${item}`);
}
} else entries.push({ name, data: fs.readFileSync(full), mode: 0o644 });
}
for (const name of ['CMakeLists.txt', 'LICENSE', 'README.md', 'THIRD_PARTY_NOTICES.md',
'.gitignore', '.gitattributes', 'src', 'assets', 'images', 'cmake', 'scripts', '.github',
'third_party/json', 'third_party/toolchain-licenses', 'third_party/openvr/src',
'third_party/openvr/headers', 'third_party/openvr/LICENSE']) collect(name);
const archive = 'frame-passthrough-shortcuts-0.1.0-source.zip';
const bytes = createZip(entries);
fs.mkdirSync(path.join(root, 'dist'), { recursive: true });
fs.writeFileSync(path.join(root, 'dist', archive), bytes);
fs.writeFileSync(path.join(root, 'dist', `${archive}.sha256`), `${createHash('sha256').update(bytes).digest('hex')} ${archive}\n`);
console.log(`${entries.length} source files: dist/${archive}`);
+201
View File
@@ -0,0 +1,201 @@
#!/usr/bin/env node
// Dependency-free release packaging. Never executes the supplied binary.
import fs from 'node:fs';
import path from 'node:path';
import { createHash } from 'node:crypto';
import { deflateRawSync } from 'node:zlib';
import { fileURLToPath, pathToFileURL } from 'node:url';
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const appName = 'frame-passthrough-shortcuts';
const controlName = 'frame-passthrough-control';
function crc32(bytes) {
let crc = 0xffffffff;
for (const byte of bytes) {
crc ^= byte;
for (let bit = 0; bit < 8; bit++) crc = (crc >>> 1) ^ (0xedb88320 & -(crc & 1));
}
return (crc ^ 0xffffffff) >>> 0;
}
// Small ZIP32 writer with explicit Unix modes, so Linux executable bits survive
// cross-platform packaging. All file names are relative, UTF-8, and use '/'.
export function createZip(entries) {
if (entries.length > 65535) throw new Error('Too many ZIP entries');
const local = [], central = [];
let offset = 0;
for (const entry of entries) {
if (entry.name.startsWith('/') || entry.name.includes('\\') || entry.name.split('/').includes('..')) {
throw new Error(`Unsafe ZIP path: ${entry.name}`);
}
const name = Buffer.from(entry.name, 'utf8');
const data = Buffer.from(entry.data);
const compressed = deflateRawSync(data, { level: 9 });
if (data.length > 0xffffffff || offset > 0xffffffff) throw new Error('ZIP64 is not supported');
const checksum = crc32(data);
const header = Buffer.alloc(30);
header.writeUInt32LE(0x04034b50, 0);
header.writeUInt16LE(20, 4);
header.writeUInt16LE(0x800, 6);
header.writeUInt16LE(8, 8);
header.writeUInt16LE(33, 12); // 1980-01-01, for reproducible archives.
header.writeUInt32LE(checksum, 14);
header.writeUInt32LE(compressed.length, 18);
header.writeUInt32LE(data.length, 22);
header.writeUInt16LE(name.length, 26);
local.push(header, name, compressed);
const directory = Buffer.alloc(46);
directory.writeUInt32LE(0x02014b50, 0);
directory.writeUInt16LE(0x314, 4); // Unix, ZIP 2.0.
directory.writeUInt16LE(20, 6);
directory.writeUInt16LE(0x800, 8);
directory.writeUInt16LE(8, 10);
directory.writeUInt16LE(33, 14);
directory.writeUInt32LE(checksum, 16);
directory.writeUInt32LE(compressed.length, 20);
directory.writeUInt32LE(data.length, 24);
directory.writeUInt16LE(name.length, 28);
directory.writeUInt32LE(((0o100000 | entry.mode) << 16) >>> 0, 38);
directory.writeUInt32LE(offset, 42);
central.push(directory, name);
offset += header.length + name.length + compressed.length;
}
const directory = Buffer.concat(central);
const end = Buffer.alloc(22);
end.writeUInt32LE(0x06054b50, 0);
end.writeUInt16LE(entries.length, 8);
end.writeUInt16LE(entries.length, 10);
end.writeUInt32LE(directory.length, 12);
end.writeUInt32LE(offset, 16);
return Buffer.concat([...local, directory, end]);
}
export function verifyBinary(bytes, platform) {
if (platform === 'linux-arm64') {
if (bytes.length < 64 || bytes.subarray(0, 4).toString('hex') !== '7f454c46' ||
bytes[4] !== 2 || bytes[5] !== 1 || bytes.readUInt16LE(18) !== 183) {
throw new Error('linux-arm64 requires a 64-bit little-endian AArch64 ELF executable');
}
if (![2, 3].includes(bytes.readUInt16LE(16))) throw new Error('ELF file is not executable');
} else throw new Error(`Unknown platform: ${platform}`);
}
export function publicBaseUrl(value) {
const url = new URL(value);
if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {
throw new Error('--base-url must be an HTTPS directory URL without credentials, a query, or a fragment');
}
// FrameDrop disallows LAN addresses and credentials. Reject literal IPs and
// local host names rather than emitting a link its public installer rejects.
if (url.hostname === 'localhost' || url.hostname.endsWith('.localhost') ||
!url.hostname.includes('.') || /^\d+(\.\d+){3}$/.test(url.hostname) || url.hostname.includes(':') ||
url.hostname.endsWith('.local')) throw new Error('--base-url must use a public host name');
url.pathname = url.pathname.replace(/\/?$/, '/');
return url;
}
function collectFiles(directory, prefix, entries) {
for (const item of fs.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
if (item.isSymbolicLink()) throw new Error(`Symlinks are not allowed in the bundle: ${item.name}`);
const source = path.join(directory, item.name);
const name = prefix ? `${prefix}/${item.name}` : item.name;
if (item.isDirectory()) collectFiles(source, name, entries);
else if (item.isFile()) entries.push({ name, data: fs.readFileSync(source), mode: 0o644 });
}
}
export function packageRelease(options) {
const root = path.resolve(options.root ?? projectRoot);
const version = options.version ?? '0.1.0';
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) throw new Error('Version must be semver, e.g. 0.1.0');
const platform = options.platform ?? 'linux-arm64';
const binaryName = appName;
const binaryPath = path.resolve(options.buildDir ?? path.join(root, 'build'), 'bin', binaryName);
const binary = fs.readFileSync(binaryPath);
verifyBinary(binary, platform);
const entries = [{ name: binaryName, data: binary, mode: 0o755 }];
if (platform === 'linux-arm64') {
const controlPath = path.join(path.dirname(binaryPath), controlName);
if (!fs.existsSync(controlPath) || !fs.lstatSync(controlPath).isFile()) {
throw new Error(`Linux release requires a regular management companion: ${controlName}`);
}
const control = fs.readFileSync(controlPath);
verifyBinary(control, platform);
if (!control.equals(binary)) {
throw new Error(`${controlName} must match the main executable exactly; rebuild the Linux bundle`);
}
entries.push({ name: controlName, data: control, mode: 0o755 });
}
for (const filename of ['config.json', 'actions.json']) {
JSON.parse(fs.readFileSync(path.join(root, 'assets', filename), 'utf8'));
}
collectFiles(path.join(root, 'assets'), '', entries);
for (const filename of ['README.md', 'LICENSE', 'third_party/openvr/LICENSE', 'third_party/json/LICENSE.MIT']) {
entries.push({ name: filename, data: fs.readFileSync(path.join(root, filename)), mode: 0o644 });
}
for (const filename of ['THIRD_PARTY_NOTICES.md', 'images/framedrop-button.svg']) {
const source = path.join(root, filename);
if (fs.existsSync(source)) entries.push({ name: filename, data: fs.readFileSync(source), mode: 0o644 });
}
const compilerNotices = path.join(root, 'third_party/toolchain-licenses');
if (fs.existsSync(compilerNotices)) collectFiles(compilerNotices, 'third_party/toolchain-licenses', entries);
const names = new Set();
for (const entry of entries) {
if (names.has(entry.name)) throw new Error(`Duplicate archive entry: ${entry.name}`);
names.add(entry.name);
}
const zipName = `${appName}-${version}-${platform}.zip`;
const outputDir = path.resolve(options.outputDir ?? path.join(root, 'dist'));
const base = options.baseUrl ? publicBaseUrl(options.baseUrl) : undefined;
if (options.fileUrl && !base) throw new Error('--file-url requires --base-url');
const zip = createZip(entries);
const sha256 = createHash('sha256').update(zip).digest('hex');
fs.mkdirSync(outputDir, { recursive: true });
fs.writeFileSync(path.join(outputDir, zipName), zip);
fs.writeFileSync(path.join(outputDir, `${zipName}.sha256`), `${sha256} ${zipName}\n`);
const result = { archive: path.join(outputDir, zipName), sha256 };
if (base && platform === 'linux-arm64') {
const manifestName = `${appName}.framedrop.json`;
const fileUrl = options.fileUrl ? new URL(options.fileUrl) : new URL(zipName, base);
if (fileUrl.protocol !== 'https:' || fileUrl.username || fileUrl.password || fileUrl.search || fileUrl.hash ||
!fileUrl.pathname.endsWith(`/${zipName}`)) {
throw new Error('--file-url must be an HTTPS URL ending in the release ZIP filename');
}
const manifest = { schema: 'framedrop.install/v1', name: 'Frame Passthrough Shortcuts', files: [{ url: fileUrl.href, sha256 }] };
fs.writeFileSync(path.join(outputDir, manifestName), JSON.stringify(manifest, null, 2) + '\n');
const manifestUrl = options.installManifestUrl ?? latestManifestUrl(base, manifestName);
const installUrl = 'https://framedropvr.com/install?manifest=' + encodeURIComponent(manifestUrl);
result.manifest = path.join(outputDir, manifestName);
result.installUrl = installUrl;
}
return result;
}
function latestManifestUrl(base, manifestName) {
const match = base.pathname.match(/^\/([^/]+)\/([^/]+)\/releases\/download\/[^/]+\/$/);
if (base.hostname === 'github.com' && match) {
return new URL(`/${match[1]}/${match[2]}/releases/latest/download/${manifestName}`, base.origin).href;
}
return new URL(manifestName, base).href;
}
function main(args) {
if (args.includes('--help')) {
console.log('node scripts/package.mjs --build-dir build --version 0.1.0 --platform linux-arm64 [--output-dir dist] [--base-url https://host/releases/v0.1.0/] [--file-url https://host/download/frame-passthrough-shortcuts-0.1.0-linux-arm64.zip] [--install-manifest-url https://host/releases/latest/download/frame-passthrough-shortcuts.framedrop.json]');
return;
}
const mapping = { '--build-dir': 'buildDir', '--version': 'version', '--platform': 'platform', '--output-dir': 'outputDir', '--base-url': 'baseUrl', '--file-url': 'fileUrl', '--install-manifest-url': 'installManifestUrl' };
const options = {};
for (let i = 0; i < args.length; i += 2) {
if (!mapping[args[i]] || !args[i + 1] || args[i + 1].startsWith('--')) throw new Error(`Unknown option or missing value: ${args[i]}`);
options[mapping[args[i]]] = args[i + 1];
}
console.log(JSON.stringify(packageRelease(options), null, 2));
}
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
try { main(process.argv.slice(2)); }
catch (error) { console.error(`Packaging failed: ${error.message}`); process.exitCode = 1; }
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <array>
#include <cstdint>
namespace fps {
// Runtime configuration: enabled, stereo, RGB source, disable devignetting,
// sharpening. Source selection must preserve every other byte.
using CameraConfig = std::array<std::uint8_t, 5>;
class CameraSource {
public:
virtual ~CameraSource() = default;
virtual bool rgb_available() { return true; }
virtual CameraConfig read() = 0;
virtual void write(const CameraConfig& config) = 0;
};
} // namespace fps
+206
View File
@@ -0,0 +1,206 @@
#include "config.hpp"
#include <json.hpp>
#include <algorithm>
#include <fstream>
#include <initializer_list>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <vector>
namespace fps {
namespace {
using Json = nlohmann::json;
[[noreturn]] void invalid(const std::string& detail) {
throw std::runtime_error("Invalid configuration: " + detail);
}
void check_keys(const Json& object, std::initializer_list<const char*> allowed,
const std::string& location) {
if (!object.is_object()) {
invalid(location + " must be an object");
}
for (const auto& item : object.items()) {
if (std::none_of(allowed.begin(), allowed.end(), [&](const char* key) {
return item.key() == key;
})) {
invalid("unknown key " + location + "." + item.key());
}
}
}
std::int64_t integer(const Json& value, const std::string& location,
std::int64_t minimum, std::int64_t maximum) {
if (!value.is_number_integer()) {
invalid(location + " must be an integer");
}
// Check unsigned JSON integers before conversion to avoid overflow.
if (value.is_number_unsigned()) {
const auto number = value.get<std::uint64_t>();
if (number > static_cast<std::uint64_t>(maximum)) {
invalid(location + " is out of range");
}
}
const auto number = value.get<std::int64_t>();
if (number < minimum || number > maximum) {
invalid(location + " is out of range");
}
return number;
}
std::string string_value(const Json& value, const std::string& location) {
if (!value.is_string()) {
invalid(location + " must be a string");
}
auto result = value.get<std::string>();
if (result.find('\0') != std::string::npos) {
invalid(location + " must not contain NUL characters");
}
return result;
}
Value setting_value(const Json& value, const std::string& location) {
if (value.is_boolean()) {
return value.get<bool>();
}
if (value.is_number_integer()) {
return static_cast<std::int32_t>(integer(value, location,
std::numeric_limits<std::int32_t>::min(),
std::numeric_limits<std::int32_t>::max()));
}
if (value.is_string()) {
return string_value(value, location);
}
invalid(location + " must be a boolean, 32-bit integer, or string");
}
bool empty_name(const std::string& name) {
return name.empty() || name.find_first_not_of(" \t\r\n") == std::string::npos;
}
void check_no_nul(const std::string& value, const std::string& location) {
if (value.find('\0') != std::string::npos) {
invalid(location + " must not contain NUL characters");
}
}
} // namespace
void validate_config(const Config& config) {
if (config.poll_interval_ms < 10 || config.poll_interval_ms > 1000) {
invalid("poll_interval_ms must be between 10 and 1000");
}
if (config.debounce_ms > 5000) {
invalid("debounce_ms must be between 0 and 5000");
}
const auto& rgb = config.rgb;
if (rgb.backend != "settings" && rgb.backend != "steamvr_camera") {
invalid("rgb.backend must be settings or steamvr_camera");
}
check_no_nul(rgb.section, "rgb.section");
check_no_nul(rgb.key, "rgb.key");
if (rgb.backend == "steamvr_camera" && (!rgb.section.empty() || !rgb.key.empty())) {
invalid("rgb.section and rgb.key must be empty for the steamvr_camera backend");
}
if (rgb.rgb_value.index() != rgb.monochrome_value.index()) {
invalid("rgb.rgb_value and rgb.monochrome_value must have the same type");
}
if (rgb.rgb_value == rgb.monochrome_value) {
invalid("rgb.rgb_value and rgb.monochrome_value must be distinct");
}
if (const auto* value = std::get_if<std::string>(&rgb.rgb_value)) {
check_no_nul(*value, "rgb.rgb_value");
check_no_nul(std::get<std::string>(rgb.monochrome_value), "rgb.monochrome_value");
}
if (rgb.enabled && rgb.backend == "settings" &&
(empty_name(rgb.section) || empty_name(rgb.key))) {
invalid("enabled RGB control requires rgb.section and rgb.key");
}
if (rgb.section == "camera" && rgb.key == "enableCamera") {
invalid("camera.enableCamera requires a restart and does not select RGB or monochrome cameras");
}
if (rgb.section == "camera" && (rgb.key == "roomViewStyle" || rgb.key == "roomView")) {
invalid("RGB control must not overwrite camera." + rgb.key);
}
}
Config parse_config(const std::string& json_text) {
Json document;
try {
// Reject duplicate keys instead of silently accepting the last value.
std::vector<std::vector<std::string>> keys;
document = Json::parse(json_text, [&](int, Json::parse_event_t event, Json& parsed) {
if (event == Json::parse_event_t::object_start) {
keys.emplace_back();
} else if (event == Json::parse_event_t::object_end) {
keys.pop_back();
} else if (event == Json::parse_event_t::key) {
auto key = parsed.get<std::string>();
auto& object_keys = keys.back();
if (std::find(object_keys.begin(), object_keys.end(), key) != object_keys.end()) {
invalid("duplicate key " + key);
}
object_keys.push_back(std::move(key));
}
return true;
});
} catch (const Json::exception& error) {
invalid(error.what());
}
check_keys(document, {"poll_interval_ms", "debounce_ms", "rgb"}, "config");
Config config;
if (document.contains("poll_interval_ms")) {
config.poll_interval_ms = static_cast<std::uint32_t>(integer(
document.at("poll_interval_ms"), "poll_interval_ms", 10, 1000));
}
if (document.contains("debounce_ms")) {
config.debounce_ms = static_cast<std::uint32_t>(integer(
document.at("debounce_ms"), "debounce_ms", 0, 5000));
}
if (document.contains("rgb")) {
const auto& rgb = document.at("rgb");
check_keys(rgb, {"enabled", "backend", "section", "key", "rgb_value", "monochrome_value"}, "rgb");
if (rgb.contains("enabled")) {
if (!rgb.at("enabled").is_boolean()) {
invalid("rgb.enabled must be a boolean");
}
config.rgb.enabled = rgb.at("enabled").get<bool>();
}
if (rgb.contains("backend")) {
config.rgb.backend = string_value(rgb.at("backend"), "rgb.backend");
}
if (rgb.contains("section")) {
config.rgb.section = string_value(rgb.at("section"), "rgb.section");
}
if (rgb.contains("key")) {
config.rgb.key = string_value(rgb.at("key"), "rgb.key");
}
if (rgb.contains("rgb_value")) {
config.rgb.rgb_value = setting_value(rgb.at("rgb_value"), "rgb.rgb_value");
}
if (rgb.contains("monochrome_value")) {
config.rgb.monochrome_value = setting_value(rgb.at("monochrome_value"), "rgb.monochrome_value");
}
}
validate_config(config);
return config;
}
Config load_config(const std::filesystem::path& path) {
std::ifstream stream(path, std::ios::binary);
if (!stream) {
throw std::runtime_error("Cannot open configuration: " + path.string());
}
std::ostringstream contents;
contents << stream.rdbuf();
if (stream.bad()) {
throw std::runtime_error("Cannot read configuration: " + path.string());
}
return parse_config(contents.str());
}
} // namespace fps
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include <cstdint>
#include <filesystem>
#include <string>
#include <variant>
namespace fps {
using Value = std::variant<bool, std::int32_t, std::string>;
struct RgbConfig {
bool enabled = false;
std::string backend = "settings";
std::string section;
std::string key;
Value rgb_value = true;
Value monochrome_value = false;
};
struct Config {
std::uint32_t poll_interval_ms = 20;
std::uint32_t debounce_ms = 250;
RgbConfig rgb;
};
Config parse_config(const std::string& json_text);
Config load_config(const std::filesystem::path& path);
void validate_config(const Config& config);
} // namespace fps
+207
View File
@@ -0,0 +1,207 @@
#include "controller.hpp"
#include <exception>
#include <stdexcept>
#include <utility>
namespace fps {
namespace {
constexpr std::int32_t kTranslucent = 3;
constexpr std::int32_t kOpaque = 4;
Value read_typed(Settings& settings, const std::string& section, const std::string& key,
const Value& type_hint) {
auto value = settings.read(section, key, type_hint);
if (value.index() != type_hint.index()) {
throw std::runtime_error("Setting " + section + "." + key + " returned an unexpected type");
}
return value;
}
} // namespace
Controller::Controller(Settings& settings, Config config, CameraSource* camera_source)
: settings_(settings), config_(std::move(config)), camera_source_(camera_source) {
validate_config(config_);
}
void Controller::write_verified(const std::string& section, const std::string& key,
const Value& target) {
settings_.write(section, key, target);
if (read_typed(settings_, section, key, target) != target) {
throw std::runtime_error("Setting " + section + "." + key +
" did not retain the requested value; SteamVR or the driver may have rejected it");
}
}
bool Controller::reconcile_camera_source() {
if (!config_.rgb.enabled || config_.rgb.backend != "steamvr_camera") return false;
if (!camera_source_) throw std::runtime_error("SteamVR camera source backend is unavailable");
// XRService owns physical disconnect and streaming fallback. Only restore a
// chosen source while the RGB module is available; never write on removal.
if (!preferred_rgb_ || !camera_source_->rgb_available()) return false;
const bool desired = *preferred_rgb_;
auto current = camera_source_->read();
for (auto value : current) if (value > 1) throw std::runtime_error("Invalid camera configuration during source restoration");
// Wait for SteamVR to initialize/reopen its camera renderer. Never turn it on.
if (!current[0] || current[2] == static_cast<std::uint8_t>(desired)) return false;
current[2] = desired ? 1 : 0;
camera_source_->write(current);
if (camera_source_->read() != current) throw std::runtime_error("Camera source restoration was rejected");
return true;
}
std::string Controller::apply(Action action) {
switch (action) {
case Action::ToggleStyle:
case Action::Translucent:
case Action::Opaque: {
const auto current = std::get<std::int32_t>(
read_typed(settings_, "camera", "roomViewStyle", Value{kTranslucent}));
const auto target = action == Action::Opaque ? kOpaque
: action == Action::Translucent ? kTranslucent
: current == kTranslucent ? kOpaque : kTranslucent;
write_verified("camera", "roomViewStyle", Value{target});
return target == kOpaque ? "Passthrough style set to opaque"
: "Passthrough style set to translucent";
}
case Action::ToggleRgb:
case Action::Rgb:
case Action::Monochrome: {
const auto& rgb = config_.rgb;
if (!rgb.enabled) {
throw std::runtime_error("RGB switching is disabled: no verified Arcturus RGB camera selection backend is enabled");
}
if (rgb.backend == "steamvr_camera") {
if (!camera_source_) {
throw std::runtime_error("SteamVR camera source backend is unavailable");
}
if (!camera_source_->rgb_available()) {
return "Arcturus RGB camera not detected; using built-in monochrome cameras";
}
// Read all five bytes for every action so changes made by SteamVR
// or another application are preserved, including explicit actions.
auto target = camera_source_->read();
for (const auto value : target) {
if (value > 1) {
throw std::runtime_error("SteamVR camera configuration is invalid: expected five Boolean values");
}
}
if (target[0] != 1) {
throw std::runtime_error("SteamVR camera configuration is inactive; refusing to enable cameras");
}
// Renderer reopen/reset may temporarily overwrite byte 2. Once a
// choice exists, toggle that choice rather than the transient flag.
target[2] = action == Action::ToggleRgb ? static_cast<std::uint8_t>(!preferred_rgb_.value_or(target[2] != 0))
: action == Action::Rgb ? 1 : 0;
camera_source_->write(target);
if (camera_source_->read() != target) {
throw std::runtime_error("SteamVR camera configuration did not retain the requested value; "
"the runtime may have rejected the source change or changed another camera option");
}
preferred_rgb_ = target[2] != 0;
return target[2] ? "Camera source set to RGB" : "Camera source set to monochrome";
}
const auto current = read_typed(settings_, rgb.section, rgb.key, rgb.rgb_value);
Value target;
if (action == Action::ToggleRgb) {
if (current == rgb.rgb_value) {
target = rgb.monochrome_value;
} else if (current == rgb.monochrome_value) {
target = rgb.rgb_value;
} else {
throw std::runtime_error("Setting " + rgb.section + "." + rgb.key +
" is neither the configured RGB nor monochrome value; refusing to guess");
}
} else {
target = action == Action::Rgb ? rgb.rgb_value : rgb.monochrome_value;
}
write_verified(rgb.section, rgb.key, target);
return target == rgb.rgb_value ? "Camera source set to RGB" : "Camera source set to monochrome";
}
}
throw std::runtime_error("Unknown control action");
}
std::string smoke_test(Settings& settings, const Config& config) {
Controller controls(settings, config);
// Do not attempt any writes unless the original setting was read correctly.
const auto original = read_typed(settings, "camera", "roomViewStyle", Value{std::int32_t{0}});
const auto original_text = std::to_string(std::get<std::int32_t>(original));
const auto describe = [](const std::exception_ptr& failure) -> std::string {
try { std::rethrow_exception(failure); }
catch (const std::exception& error) { return error.what(); }
catch (...) { return "unknown exception"; }
};
std::exception_ptr test_failure;
try {
controls.apply(Action::Translucent);
controls.apply(Action::Opaque);
controls.apply(Action::ToggleStyle);
} catch (...) {
test_failure = std::current_exception();
}
// Restoration is explicit so its errors can take precedence over a test
// failure without throwing from a destructor during exception unwinding.
try {
settings.write("camera", "roomViewStyle", original);
if (read_typed(settings, "camera", "roomViewStyle", original) != original) {
throw std::runtime_error("restored value did not retain the original setting");
}
} catch (...) {
const auto restore_failure = std::current_exception();
auto message = "Self-test restoration FAILED. Manual recovery required: restore "
"camera.roomViewStyle=" + original_text + " in SteamVR settings. Restore error: " +
describe(restore_failure);
if (test_failure) message += ". Original test error: " + describe(test_failure);
throw std::runtime_error(message);
}
if (test_failure) {
throw std::runtime_error("Self-test failed; restored camera.roomViewStyle=" +
original_text + ": " + describe(test_failure));
}
return "Passthrough settings self-test passed: translucent, opaque, toggle; restored "
"camera.roomViewStyle=" + original_text +
". Visual output and physical controller bindings were not tested.";
}
PressGate::PressGate(std::uint32_t debounce_ms) : debounce_ms_(debounce_ms) {}
bool PressGate::update(bool active, bool down, bool changed, std::uint64_t monotonic_ms) {
if (!active) {
reset();
return false;
}
if (!down) {
armed_ = true;
return false;
}
if (!armed_) {
return false;
}
// Consume this press even if it is rejected, so a hold can never fire later.
armed_ = false;
if (!changed) {
return false;
}
if (fired_ && (monotonic_ms < last_fire_ms_ ||
monotonic_ms - last_fire_ms_ < debounce_ms_)) {
return false;
}
fired_ = true;
last_fire_ms_ = monotonic_ms;
return true;
}
void PressGate::reset() {
// SteamVR can activate a gesture action on its first rising edge. Requiring
// an active release sample here would discard that first double/long press.
// update still requires bChanged and consumes a down sample until release.
armed_ = true;
}
} // namespace fps
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include "config.hpp"
#include "camera_source.hpp"
#include <cstdint>
#include <string>
#include <optional>
namespace fps {
class Settings {
public:
virtual ~Settings() = default;
virtual Value read(const std::string& section, const std::string& key,
const Value& type_hint) = 0;
virtual void write(const std::string& section, const std::string& key,
const Value& value) = 0;
};
enum class Action {
ToggleStyle,
Translucent,
Opaque,
ToggleRgb,
Rgb,
Monochrome,
};
class Controller {
public:
Controller(Settings& settings, Config config, CameraSource* camera_source = nullptr);
std::string apply(Action action);
void remember_rgb(bool rgb) { preferred_rgb_ = rgb; }
std::optional<bool> preferred_rgb() const { return preferred_rgb_; }
bool reconcile_camera_source();
private:
Settings& settings_;
Config config_;
CameraSource* camera_source_;
std::optional<bool> preferred_rgb_;
void write_verified(const std::string& section, const std::string& key, const Value& target);
};
// Exercise only Room View style settings, restoring the exact original value.
// Throws on a test or restoration failure; never changes camera enable/source.
std::string smoke_test(Settings& settings, const Config& config);
class PressGate {
public:
explicit PressGate(std::uint32_t debounce_ms = 250);
bool update(bool active, bool down, bool changed, std::uint64_t monotonic_ms);
void reset();
private:
std::uint32_t debounce_ms_;
bool armed_ = true;
bool fired_ = false;
std::uint64_t last_fire_ms_ = 0;
};
} // namespace fps
+682
View File
@@ -0,0 +1,682 @@
#include "config.hpp"
#include "controller.hpp"
#include "openvr_camera_source.hpp"
#include <openvr.h>
#include <json.hpp>
#include <algorithm>
#include <array>
#include <chrono>
#include <csignal>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
#include <cerrno>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <unistd.h>
namespace fs = std::filesystem;
using json = nlohmann::json;
namespace {
constexpr const char* app_key = "local.frame.passthrough.shortcuts";
constexpr const char* service_name = "frame-passthrough-shortcuts.service";
constexpr const char* devkit_game_id = "Frame_Passthrough_Shortcuts";
volatile std::sig_atomic_t stopping = 0;
std::ofstream application_log;
void stop(int) { stopping = 1; }
void log(const std::string& message) {
std::cout << message << std::endl;
if (application_log.is_open()) application_log << message << std::endl;
}
fs::path executable() {
return fs::canonical("/proc/self/exe");
}
fs::path config_home() {
if (const auto xdg = std::getenv("XDG_CONFIG_HOME"); xdg && *xdg) {
if (!fs::path(xdg).is_absolute()) throw std::runtime_error("XDG_CONFIG_HOME must be absolute");
return xdg;
}
const auto base = std::getenv("HOME");
if (!base || !*base) throw std::runtime_error("HOME is unavailable");
return fs::path(base) / ".config";
}
fs::path app_directory() { return config_home() / "frame-passthrough-shortcuts"; }
void write_file(const fs::path& path, const std::string& contents) {
fs::create_directories(path.parent_path());
std::ofstream stream(path, std::ios::binary | std::ios::trunc);
if (!(stream << contents) || !stream.flush())
throw std::runtime_error("Cannot write " + path.string());
}
class InstanceLock {
int fd_ = -1;
public:
InstanceLock() {
fs::create_directories(app_directory());
fd_ = ::open((app_directory() / "instance.lock").c_str(), O_CREAT | O_RDWR | O_CLOEXEC, 0600);
if (fd_ < 0) throw std::runtime_error("Cannot open instance lock");
if (::flock(fd_, LOCK_EX | LOCK_NB) < 0) {
::close(fd_); fd_ = -1;
throw std::runtime_error("Shortcuts are already running (or instance lock unavailable). Use --stop before diagnostics or one-shot actions.");
}
}
~InstanceLock() {
if (fd_ >= 0) ::close(fd_);
}
};
class VrSession {
public:
explicit VrSession(bool wait, bool utility = false, bool overlay = false) {
unsigned attempts = 0;
while (!stopping) {
vr::EVRInitError error = vr::VRInitError_None;
vr::VR_Init(&error, utility ? vr::VRApplication_Utility : overlay ? vr::VRApplication_Overlay : vr::VRApplication_Background);
if (error == vr::VRInitError_None) {
if (vr::VRApplications() && (utility || (vr::VRSystem() && vr::VRInput() && vr::VRSettings()))) return;
vr::VR_Shutdown();
throw std::runtime_error("SteamVR lacks a required OpenVR interface. Update SteamVR to use this build.");
}
const std::string reason = vr::VR_GetVRInitErrorAsEnglishDescription(error);
if (!wait) throw std::runtime_error("SteamVR unavailable: " + reason);
if (attempts++ % 6 == 0) log("Waiting for SteamVR: " + reason);
for (int i = 0; i < 50 && !stopping; ++i)
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
throw std::runtime_error("Stopped while waiting for SteamVR");
}
~VrSession() { vr::VR_Shutdown(); }
VrSession(const VrSession&) = delete;
};
class VrSettings final : public fps::Settings {
static void check(vr::EVRSettingsError error, const std::string& section, const std::string& key) {
if (error != vr::VRSettingsError_None)
throw std::runtime_error(section + "." + key + ": " + vr::VRSettings()->GetSettingsErrorNameFromEnum(error));
}
public:
fps::Value read(const std::string& section, const std::string& key, const fps::Value& hint) override {
vr::EVRSettingsError error = vr::VRSettingsError_None;
fps::Value result;
if (std::holds_alternative<bool>(hint))
result = vr::VRSettings()->GetBool(section.c_str(), key.c_str(), &error);
else if (std::holds_alternative<int32_t>(hint))
result = vr::VRSettings()->GetInt32(section.c_str(), key.c_str(), &error);
else {
std::array<char, 4096> value{};
vr::VRSettings()->GetString(section.c_str(), key.c_str(), value.data(), static_cast<uint32_t>(value.size()), &error);
result = std::string(value.data());
}
check(error, section, key);
return result;
}
void write(const std::string& section, const std::string& key, const fps::Value& value) override {
vr::EVRSettingsError error = vr::VRSettingsError_None;
if (const auto v = std::get_if<bool>(&value)) vr::VRSettings()->SetBool(section.c_str(), key.c_str(), *v, &error);
else if (const auto integer = std::get_if<int32_t>(&value)) vr::VRSettings()->SetInt32(section.c_str(), key.c_str(), *integer, &error);
else vr::VRSettings()->SetString(section.c_str(), key.c_str(), std::get<std::string>(value).c_str(), &error);
check(error, section, key);
}
};
void cleanup_removed_install(const fs::path& binary);
void remove_openvr_user_state();
void check_app(vr::EVRApplicationError error, const char* action) {
if (error != vr::VRApplicationError_None)
throw std::runtime_error(std::string(action) + ": " + vr::VRApplications()->GetApplicationsErrorNameFromEnum(error));
}
void check_input(vr::EVRInputError error, const char* action) {
if (error != vr::VRInputError_None)
throw std::runtime_error(std::string(action) + " failed, SteamVR input error " + std::to_string(error));
}
void register_app(const fs::path& binary, const fs::path& assets, const fs::path& config, bool attach = true) {
// Frame's manifest argument parser keeps quote characters literally. Store
// the selected path in JSON so spaces and quotes never cross that parser.
write_file(app_directory() / "launch.json", json{{"config", config.generic_u8string()}}.dump(2));
json app = {
{"app_key", app_key}, {"launch_type", "binary"},
{"is_dashboard_overlay", true},
{"action_manifest_path", (assets / "actions.json").generic_u8string()},
{"arguments", "--run --registered-config"},
{"strings", {{"en_us", {{"name", "Frame Passthrough Shortcuts"},
{"description", "Passthrough shortcuts; no overlay surface is rendered"}}}}}
};
app["binary_path_linux"] = binary.generic_u8string();
// Frame's native ARM runtime selects this key, not binary_path_linux.
app["binary_path_linux_arm"] = binary.generic_u8string();
const auto pid = static_cast<uint32_t>(getpid());
const auto manifest = app_directory() / "app.vrmanifest";
write_file(manifest, json{{"source", "builtin"}, {"applications", json::array({app})}}.dump(2));
check_app(vr::VRApplications()->AddApplicationManifest(manifest.u8string().c_str(), false), "Register application");
if (!attach) return;
std::array<char, vr::k_unMaxApplicationKeyLength> existing_key{};
const auto identity_error = vr::VRApplications()->GetApplicationKeyByProcessId(pid, existing_key.data(), static_cast<uint32_t>(existing_key.size()));
if (identity_error != vr::VRApplicationError_None || std::string(existing_key.data()) != app_key)
check_app(vr::VRApplications()->IdentifyApplication(pid, app_key), "Identify shortcut application");
check_input(vr::VRInput()->SetActionManifestPath((assets / "actions.json").u8string().c_str()), "Load actions");
}
constexpr std::array<const char*, 6> action_names = {"toggle_style", "translucent", "opaque", "toggle_rgb", "rgb", "monochrome"};
constexpr std::array<fps::Action, 6> action_kinds = {fps::Action::ToggleStyle, fps::Action::Translucent, fps::Action::Opaque,
fps::Action::ToggleRgb, fps::Action::Rgb, fps::Action::Monochrome};
fps::Action parse_action(std::string name) {
for (auto& c : name) if (c == '-') c = '_';
for (size_t i = 0; i < action_names.size(); ++i) if (name == action_names[i]) return action_kinds[i];
throw std::runtime_error("Unknown action: " + name);
}
void load_camera_preference(fps::Controller& controls) {
const auto path = app_directory() / "camera-preference.json";
if (!fs::exists(path)) return;
try {
std::ifstream stream(path);
json saved;
if (!(stream >> saved) || !saved.at("rgb").is_boolean()) throw std::runtime_error("expected Boolean rgb");
controls.remember_rgb(saved.at("rgb").get<bool>());
} catch (const std::exception& error) {
log(std::string("Cannot load saved camera preference: ") + error.what());
}
}
void save_camera_preference(const fps::Controller& controls) {
if (!controls.preferred_rgb()) return;
const auto temporary = app_directory() / "camera-preference.pending.json";
write_file(temporary, json{{"rgb", *controls.preferred_rgb()}}.dump());
fs::rename(temporary, app_directory() / "camera-preference.json");
}
int run(const fs::path& binary, const fs::path& assets, const fs::path& config_path, const fps::Config& config, bool bindings) {
InstanceLock lock;
application_log.open(app_directory() / "application.log", std::ios::app);
VrSession session(!bindings, false, true);
register_app(binary, assets, config_path);
VrSettings settings;
fps::OpenVrCameraSource camera;
fps::Controller controls(settings, config, &camera);
load_camera_preference(controls);
vr::VRActionSetHandle_t action_set = vr::k_ulInvalidActionSetHandle;
check_input(vr::VRInput()->GetActionSetHandle("/actions/shortcuts", &action_set), "Find action set");
if (bindings) {
check_input(vr::VRInput()->OpenBindingUI(app_key, action_set, vr::k_ulInvalidInputValueHandle, false), "Open bindings");
return 0;
}
std::array<vr::VRActionHandle_t, 6> handles{};
std::array<fps::PressGate, 6> gates;
for (size_t i = 0; i < handles.size(); ++i) {
const auto path = std::string("/actions/shortcuts/in/") + action_names[i];
check_input(vr::VRInput()->GetActionHandle(path.c_str(), &handles[i]), "Find action");
gates[i] = fps::PressGate(config.debounce_ms);
}
const auto pid = vr::VRApplications()->GetApplicationProcessId(app_key);
if (!config.rgb.enabled) log("RGB / monochrome switching is disabled in configuration.");
else if (config.rgb.backend == "steamvr_camera") {
try {
const auto current = camera.read();
if (current[0] != 1) throw std::runtime_error("camera rendering is not initialized yet");
log(std::string("Steam Frame camera source control ready: ") + (current[2] ? "RGB" : "monochrome"));
} catch (const std::exception& error) {
log(std::string("Camera source control is not ready: ") + error.what() + ". Actions will retry.");
}
}
vr::VRActiveActionSet_t active{};
active.ulActionSet = action_set;
active.nPriority = vr::k_nActionSetOverlayGlobalPriorityMax;
// Respect the user's global-input setting. On the tested Frame runtime,
// even the highest public priority cannot override dashboard input focus.
log("Ready as VRApplication_Overlay. No overlay surface is created or rendered.");
log("Overlay input priority configured; SteamVR dashboard focus can still block actions.");
bool warned_inactive = false;
std::string camera_error;
std::string input_error;
json last_status;
const auto started = std::chrono::steady_clock::now();
auto removal_seen = std::chrono::steady_clock::time_point{};
while (!stopping) {
vr::VREvent_t event{};
while (vr::VRSystem()->PollNextEvent(&event, sizeof(event))) {
if (event.eventType == vr::VREvent_Quit) {
vr::VRSystem()->AcknowledgeQuit_Exiting();
log("SteamVR is shutting down.");
return 0;
}
if (event.eventType == vr::VREvent_ApplicationListUpdated &&
!vr::VRApplications()->IsApplicationInstalled(app_key) && removal_seen == std::chrono::steady_clock::time_point{})
removal_seen = std::chrono::steady_clock::now();
if (event.eventType == vr::VREvent_Input_BindingLoadFailed) log("SteamVR could not load the selected bindings. Select a new binding in SteamVR.");
}
const bool removed_from_steamvr = !vr::VRApplications()->IsApplicationInstalled(app_key);
const bool install_files_missing = !fs::exists(binary);
if (removed_from_steamvr || install_files_missing) {
if (removal_seen == std::chrono::steady_clock::time_point{}) removal_seen = std::chrono::steady_clock::now();
// FrameDrop replaces a title by removing its directory before copying
// the update. Wait out that short gap, then clean residual registration.
if (std::chrono::steady_clock::now() - removal_seen >= std::chrono::seconds(15)) {
log("SteamVR registration or installed files were removed; cleaning the remaining application data.");
cleanup_removed_install(binary);
return 0;
}
} else {
removal_seen = std::chrono::steady_clock::time_point{};
}
try {
if (controls.reconcile_camera_source()) log("Updated camera source for camera availability and saved preference.");
camera_error.clear();
} catch (const std::exception& error) {
if (camera_error != error.what()) log(std::string("Camera restoration deferred: ") + error.what());
camera_error = error.what();
}
const auto update_error = vr::VRInput()->UpdateActionState(&active, sizeof(active), 1);
if (update_error != vr::VRInputError_None) {
for (auto& gate : gates) gate.reset();
const auto message = "SteamVR input update deferred, error " + std::to_string(update_error);
if (input_error != message) log(message);
input_error = message;
std::this_thread::sleep_for(std::chrono::milliseconds(config.poll_interval_ms));
continue;
}
bool any_active = false;
bool read_failed = false;
json input_active = json::object();
const auto now = std::chrono::steady_clock::now();
const auto ms = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count());
for (size_t i = 0; i < handles.size(); ++i) {
vr::InputDigitalActionData_t data{};
const auto error = vr::VRInput()->GetDigitalActionData(handles[i], &data, sizeof(data), vr::k_ulInvalidInputValueHandle);
if (error != vr::VRInputError_None) {
gates[i].reset();
read_failed = true;
const auto message = std::string("SteamVR action read deferred: ") + action_names[i] +
", error " + std::to_string(error);
if (input_error != message) log(message);
input_error = message;
input_active[action_names[i]] = false;
continue;
}
any_active |= data.bActive;
input_active[action_names[i]] = data.bActive;
if (data.bActive && data.bState && data.bChanged)
log(std::string("Input press: ") + action_names[i]);
if (gates[i].update(data.bActive, data.bState, data.bChanged, ms)) {
try { log(controls.apply(action_kinds[i])); save_camera_preference(controls); }
catch (const std::exception& e) { log(std::string("Action failed: ") + e.what()); }
}
}
if (!read_failed) input_error.clear();
json status{{"pid", pid}, {"ready", true}, {"input_active", input_active},
{"dashboard_visible", vr::VROverlay()->IsDashboardVisible()}, {"input_priority", active.nPriority}};
try { status["arcturus_rgb_camera_connected"] = camera.rgb_available(); }
catch (const std::exception& error) {
status["arcturus_rgb_camera_connected"] = nullptr;
if (camera_error != error.what()) log(std::string("Camera detection deferred: ") + error.what());
camera_error = error.what();
}
if (controls.preferred_rgb()) status["preferred_rgb"] = *controls.preferred_rgb();
if (status != last_status) {
log(std::string("Input state: ") + status.dump());
write_file(app_directory() / "runtime.json", status.dump()); last_status = std::move(status);
}
if (!any_active && !warned_inactive && now - started > std::chrono::seconds(30)) {
log("No active bindings. Choose Frame Passthrough Shortcuts in SteamVR controller bindings.");
warned_inactive = true;
}
std::this_thread::sleep_for(std::chrono::milliseconds(config.poll_interval_ms));
}
return 0;
}
int run_process(std::vector<std::string> arguments) {
if (arguments.empty()) throw std::runtime_error("Cannot start an empty command");
std::vector<char*> argv;
for (auto& arg : arguments) argv.push_back(arg.data());
argv.push_back(nullptr);
const auto child = fork();
if (child < 0) throw std::runtime_error("Cannot start " + arguments.front());
if (child == 0) { execvp(argv[0], argv.data()); _exit(127); }
int status = 0;
while (waitpid(child, &status, 0) < 0) if (errno != EINTR) throw std::runtime_error("Cannot wait for " + arguments.front());
return WIFEXITED(status) ? WEXITSTATUS(status) : 1;
}
int systemctl(const std::vector<std::string>& arguments) {
std::vector<std::string> all{"systemctl", "--user"};
all.insert(all.end(), arguments.begin(), arguments.end());
return run_process(all);
}
void require_systemctl(const std::vector<std::string>& arguments) {
if (systemctl(arguments) != 0) throw std::runtime_error("systemctl --user failed. Run --run in a SteamVR session or inspect journalctl --user -u " + std::string(service_name));
}
void remove_legacy_service() {
const auto unit = config_home() / "systemd/user" / service_name;
if (!fs::exists(unit)) return;
require_systemctl({"disable", "--now", service_name});
fs::remove(unit);
require_systemctl({"daemon-reload"});
}
fs::path managed_install_directory() {
const auto home = std::getenv("HOME");
if (!home || !*home) throw std::runtime_error("HOME is unavailable");
return fs::path(home) / "devkit-game" / devkit_game_id;
}
void remove_devkit_shortcut() {
const auto home = std::getenv("HOME");
if (!home || !*home) throw std::runtime_error("HOME is unavailable");
const auto helper = fs::path(home) / "devkit-utils" / "steamos-delete";
if (!fs::is_regular_file(helper)) {
log("Steam library shortcut cleanup helper is unavailable; remove the Devkit Game entry from Steam manually.");
return;
}
if (run_process({"python3", helper.string(), "--delete-title", devkit_game_id}) != 0)
throw std::runtime_error("SteamOS could not remove the Devkit Game shortcut");
}
void remove_application_registration(const fs::path& manifest) {
if (vr::VRApplications()->IsApplicationInstalled(app_key)) {
if (vr::VRApplications()->GetApplicationPropertyBool(app_key, vr::VRApplicationProperty_IsDashboardOverlay_Bool))
check_app(vr::VRApplications()->SetApplicationAutoLaunch(app_key, false), "Disable SteamVR autostart");
check_app(vr::VRApplications()->RemoveApplicationManifest(manifest.u8string().c_str()), "Unregister application");
}
std::error_code error;
fs::remove(manifest, error);
if (error) throw std::runtime_error("Cannot remove the SteamVR application manifest: " + error.message());
}
void cleanup_removed_install(const fs::path& binary) {
remove_legacy_service();
remove_application_registration(app_directory() / "app.vrmanifest");
remove_devkit_shortcut();
remove_openvr_user_state();
const auto home = std::getenv("HOME");
if (!home || !*home) throw std::runtime_error("HOME is unavailable");
const auto devkit_root = fs::path(home) / "devkit-game";
for (const auto* suffix : {"-argv.json", "-settings.json"}) {
std::error_code sidecar_error;
fs::remove(devkit_root / (std::string(devkit_game_id) + suffix), sidecar_error);
if (sidecar_error) throw std::runtime_error("Cannot remove Devkit Game metadata: " + sidecar_error.message());
}
std::error_code error;
fs::remove_all(app_directory(), error);
if (error) throw std::runtime_error("Cannot remove Frame Passthrough Shortcuts settings: " + error.message());
const auto managed = managed_install_directory();
std::error_code parent_error, expected_error;
const auto parent = fs::weakly_canonical(binary.parent_path(), parent_error);
const auto expected = fs::weakly_canonical(managed, expected_error);
if (!parent_error && !expected_error && parent == expected) {
fs::remove_all(managed, error);
if (error) throw std::runtime_error("Cannot remove Frame Passthrough Shortcuts files: " + error.message());
}
log("Removed SteamVR registration, autostart, Devkit shortcut, and application files.");
}
void remove_openvr_user_state() {
const auto openvr_config = config_home() / "openvr" / "config";
std::error_code error;
fs::remove(openvr_config / "vrappconfig" / "local.frame.passthrough.shortcuts.vrappconfig", error);
if (error) throw std::runtime_error("Cannot remove SteamVR app settings: " + error.message());
const auto settings_path = openvr_config / "steamvr.vrsettings";
if (fs::exists(settings_path)) {
std::ifstream input(settings_path);
json settings;
if (!input || !(input >> settings) || !settings.is_object())
throw std::runtime_error("Cannot parse SteamVR settings while removing this app's binding state");
if (settings.erase(app_key) != 0) {
const auto temporary = settings_path.string() + ".frame-passthrough-shortcuts.tmp";
write_file(temporary, settings.dump(3) + "\n");
fs::rename(temporary, settings_path);
}
}
const auto home = std::getenv("HOME");
if (!home || !*home) throw std::runtime_error("HOME is unavailable");
const auto logs = fs::path(home) / ".local/share/Steam/logs";
if (fs::is_directory(logs)) {
for (const auto& entry : fs::directory_iterator(logs)) {
const auto name = entry.path().filename().string();
if (name.rfind("vrclient_frame-passthrough-shortcuts", 0) == 0 ||
name.rfind("vrclient_frame-passthrough-control", 0) == 0) {
fs::remove(entry.path(), error);
if (error) throw std::runtime_error("Cannot remove this app's SteamVR logs: " + error.message());
}
}
}
}
void stop_registered_application() {
const auto pid = vr::VRApplications()->GetApplicationProcessId(app_key);
if (!pid) return;
std::array<char, 4096> registered_binary{};
vr::EVRApplicationError error = vr::VRApplicationError_None;
vr::VRApplications()->GetApplicationPropertyString(app_key, vr::VRApplicationProperty_BinaryPath_String,
registered_binary.data(), static_cast<uint32_t>(registered_binary.size()), &error);
check_app(error, "Read registered binary before stopping");
const auto process = fs::path("/proc") / std::to_string(pid);
std::error_code file_error;
const auto running_binary = fs::read_symlink(process / "exe", file_error);
if (file_error) {
if (!fs::exists(process)) return;
throw std::runtime_error("Cannot verify the shortcut process executable before stopping");
}
if (fs::weakly_canonical(running_binary) != fs::weakly_canonical(registered_binary.data()) || pid == static_cast<uint32_t>(getpid()))
throw std::runtime_error("Refusing to stop a process that does not match the registered shortcut executable");
if (::kill(static_cast<pid_t>(pid), SIGTERM) != 0 && errno != ESRCH)
throw std::runtime_error("Cannot stop the registered shortcut process");
for (int attempt = 0; attempt < 50; ++attempt) {
if (!fs::exists(process) && vr::VRApplications()->GetApplicationProcessId(app_key) != pid) return;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
throw std::runtime_error("Shortcut process did not exit within five seconds");
}
json application_status() {
const bool installed = vr::VRApplications()->IsApplicationInstalled(app_key);
const bool overlay = installed && vr::VRApplications()->GetApplicationPropertyBool(app_key, vr::VRApplicationProperty_IsDashboardOverlay_Bool);
const auto pid = installed ? vr::VRApplications()->GetApplicationProcessId(app_key) : 0;
bool ready = false;
if (pid) {
std::ifstream stream(app_directory() / "runtime.json");
try {
json marker;
if (stream >> marker) ready = marker.value("pid", uint32_t{0}) == pid && marker.value("ready", false);
} catch (const json::exception&) {}
}
return {{"installed", installed}, {"autolaunch", overlay && vr::VRApplications()->GetApplicationAutoLaunch(app_key)},
{"running", pid != 0}, {"pid", pid}, {"ready", ready}, {"application_type", overlay ? "overlay" : "background"}, {"renders_overlay", false}};
}
void verify_control_companion(const fs::path& binary, const fs::path& helper) {
if (!fs::is_regular_file(helper) || fs::is_symlink(helper) || ::access(helper.c_str(), X_OK) != 0)
throw std::runtime_error("Missing executable frame-passthrough-control companion; reinstall the complete bundle");
if (fs::file_size(binary) != fs::file_size(helper))
throw std::runtime_error("Control companion does not match this build; reinstall the complete bundle");
std::ifstream primary(binary, std::ios::binary), control(helper, std::ios::binary);
if (!primary || !control) throw std::runtime_error("Cannot read the control companion for verification");
std::array<char, 16384> first{}, second{};
while (primary) {
primary.read(first.data(), first.size());
control.read(second.data(), second.size());
if (primary.gcount() != control.gcount() || !std::equal(first.begin(), first.begin() + primary.gcount(), second.begin()))
throw std::runtime_error("Control companion does not match this build; reinstall the complete bundle");
}
if (primary.bad() || control.bad()) throw std::runtime_error("Cannot verify the control companion");
}
void help() {
log("Frame Passthrough Shortcuts 0.1.0\n"
"Creates no overlay. Supports Steam Frame RGB / monochrome camera selection.\n\n"
"Default: Register and start SteamVR autolaunch.\n"
" --run Run an overlay application without creating an overlay surface\n"
" --install Enable SteamVR autostart and launch shortcuts\n"
" --stop Stop shortcuts for diagnostics; retain autostart\n"
" --status Report registration, autostart and running PID as JSON\n"
" --uninstall Remove SteamVR registration, shortcut, files, and settings\n"
" --doctor Read runtime settings and report support; no setting changes\n"
" --self-test Test style changes, then restore the original style\n"
" --bindings Open bindings (use --stop first)\n"
" --action NAME toggle-style, translucent, opaque, toggle-rgb, rgb, monochrome\n"
" --validate Check config and bundled action manifest without SteamVR\n"
" --config PATH Use this JSON config (default: config.json beside executable)\n"
" --help Show this help");
}
}
int main(int argc, char** argv) {
std::signal(SIGINT, stop);
std::signal(SIGTERM, stop);
try {
const auto process_binary = executable();
const bool control_process = process_binary.filename() == "frame-passthrough-control";
const auto binary = control_process ? process_binary.parent_path() / "frame-passthrough-shortcuts" : process_binary;
const auto assets = binary.parent_path();
auto config_path = assets / "config.json";
std::string command = "--install";
bool explicit_command = false;
bool registered_config = false;
bool explicit_config = false;
std::string action;
for (int i = 1; i < argc; ++i) {
const std::string arg = argv[i];
if (arg == "--help" || arg == "-h") { help(); return 0; }
if (arg == "--config") {
if (++i == argc) throw std::runtime_error("--config needs a path");
config_path = fs::absolute(argv[i]); explicit_config = true; continue;
}
if (arg == "--registered-config") { registered_config = true; continue; }
if (explicit_command) throw std::runtime_error("Specify only one command");
explicit_command = true; command = arg;
if (arg == "--action") {
if (++i == argc) throw std::runtime_error("--action needs a name");
action = argv[i];
(void)parse_action(action);
} else if (arg != "--run" && arg != "--install" && arg != "--uninstall" && arg != "--stop" && arg != "--status" && arg != "--doctor" && arg != "--self-test" && arg != "--bindings" && arg != "--validate")
throw std::runtime_error("Unknown command: " + arg);
}
const bool management = command == "--install" || command == "--uninstall" || command == "--stop" || command == "--status";
if (control_process && !management)
throw std::runtime_error("The control companion accepts management commands only");
if (management) {
// SteamVR assigns app identity by executable path even for Utility
// sessions. A distinct real executable keeps status/install/stop
// requests from taking over the resident application's PID.
const auto helper = binary.parent_path() / "frame-passthrough-control";
verify_control_companion(binary, helper);
if (!control_process) {
std::vector<char*> arguments;
auto helper_text = helper.string();
arguments.push_back(helper_text.data());
for (int i = 1; i < argc; ++i) arguments.push_back(argv[i]);
arguments.push_back(nullptr);
::execv(helper.c_str(), arguments.data());
throw std::runtime_error("Cannot start frame-passthrough-control companion");
}
}
if (command == "--status" || command == "--stop") {
VrSession session(false, true);
if (command == "--status") log(application_status().dump());
else { stop_registered_application(); log("Shortcuts stopped. SteamVR autostart remains enabled."); }
return 0;
}
if (command == "--uninstall") {
VrSession session(false, true);
stop_registered_application();
cleanup_removed_install(binary);
return 0;
}
if (registered_config) {
if (explicit_config) throw std::runtime_error("Specify --config or --registered-config, not both");
std::ifstream stream(app_directory() / "launch.json");
json launch;
if (!stream || !(stream >> launch)) throw std::runtime_error("Missing saved launch configuration; run --install again");
config_path = fs::u8path(launch.at("config").get<std::string>());
if (!config_path.is_absolute()) throw std::runtime_error("Saved launch configuration must use an absolute path");
}
auto config = fps::load_config(config_path);
// Camera selection is automatic on Frame, including older config files.
config.rgb.enabled = true;
config.rgb.backend = "steamvr_camera";
config.rgb.section.clear();
config.rgb.key.clear();
std::ifstream manifest_stream(assets / "actions.json");
json manifest;
if (!manifest_stream || !(manifest_stream >> manifest) || !manifest.contains("actions"))
throw std::runtime_error("Missing or invalid bundled actions.json");
if (command == "--validate") {
for (const auto& binding : manifest.at("default_bindings")) {
std::ifstream stream(assets / binding.at("binding_url").get<std::string>());
json value;
if (!stream || !(stream >> value) || !value.contains("bindings")) throw std::runtime_error("Missing or invalid default binding");
}
log("Configuration and action files are valid.");
if (config.rgb.enabled && config.rgb.backend == "steamvr_camera")
log("Steam Frame RGB / monochrome backend configured; run --doctor on the Frame to check support.");
else log(config.rgb.enabled ? "RGB backend configured; hardware behavior is unverified." : "RGB switching disabled: no verified Arcturus RGB camera selector configured.");
return 0;
}
if (command == "--install") {
remove_legacy_service();
VrSession session(false, true);
register_app(binary, assets, config_path, false);
check_app(vr::VRApplications()->SetApplicationAutoLaunch(app_key, true), "Enable SteamVR autostart");
if (!vr::VRApplications()->GetApplicationAutoLaunch(app_key))
throw std::runtime_error("SteamVR did not retain the autostart setting");
if (!vr::VRApplications()->GetApplicationProcessId(app_key))
check_app(vr::VRApplications()->LaunchDashboardOverlay(app_key), "Launch shortcuts");
log("Registered for SteamVR overlay autostart. No overlay surface is created or rendered.");
return 0;
}
if (command == "--self-test") {
InstanceLock lock;
VrSession session(false);
VrSettings settings;
log(fps::smoke_test(settings, config));
return 0;
}
if (command == "--doctor" || command == "--action") {
// SteamVR associates clients by executable path. A second client can
// take the background app's PID, so serialize all live VR commands.
InstanceLock lock;
VrSession session(false);
VrSettings settings;
fps::OpenVrCameraSource camera;
if (command == "--action") {
fps::Controller controls(settings, config, &camera);
load_camera_preference(controls);
log(controls.apply(parse_action(action)));
save_camera_preference(controls);
return 0;
}
log("Connected as VRApplication_Background. No overlay created.");
const auto style = settings.read("camera", "roomViewStyle", int32_t(0));
log("camera.roomViewStyle=" + std::to_string(std::get<int32_t>(style)) + " (3=translucent, 4=opaque)");
log("camera.roomView=" + std::to_string(std::get<int32_t>(settings.read("camera", "roomView", int32_t(0)))));
if (config.rgb.enabled && config.rgb.backend == "steamvr_camera") {
const auto current = camera.read();
if (current[0] != 1) throw std::runtime_error("Steam Frame camera rendering is not initialized; open passthrough and retry");
log(std::string("Steam Frame camera source: ") + (current[2] ? "RGB" : "monochrome"));
log("Camera flags: stereo=" + std::to_string(current[1]) + ", disable_devignetting=" +
std::to_string(current[3]) + ", sharpening=" + std::to_string(current[4]));
} else if (config.rgb.enabled) {
const auto current = settings.read(config.rgb.section, config.rgb.key, config.rgb.rgb_value);
if (current != config.rgb.rgb_value && current != config.rgb.monochrome_value)
throw std::runtime_error("Configured RGB setting has an unknown value; source switching is unavailable");
log(current == config.rgb.rgb_value ? "Configured RGB source setting: RGB" : "Configured RGB source setting: monochrome");
} else log("RGB / monochrome: UNSUPPORTED until a verified source selector is configured.");
return 0;
}
return run(binary, assets, config_path, config, command == "--bindings");
} catch (const std::exception& e) {
if (stopping) return 0;
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
}
+150
View File
@@ -0,0 +1,150 @@
#include "openvr_camera_source.hpp"
#include <algorithm>
#include <stdexcept>
#include <string>
#include <filesystem>
#include <fstream>
#if defined(__linux__) && defined(__aarch64__)
#include <openvr.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#endif
namespace fps {
namespace {
void validate_camera_config(const CameraConfig& config) {
if (!std::all_of(config.begin(), config.end(), [](std::uint8_t value) {
return value <= 1;
})) {
throw std::runtime_error(
"Steam Frame camera configuration contains invalid boolean values; "
"the private SteamVR camera interface may have changed");
}
}
#if defined(__linux__) && defined(__aarch64__)
// Verified on Steam Frame firmware build 20260918. This private interface may
// change with SteamVR updates. Slots 9/10 get/set exactly five bytes and use
// SteamVR's shared-state mutex; the setter notifies the compositor via event 812.
// The getter's bool is config[0] (override enabled), not a success/error value.
constexpr const char* kCameraInterface = "IVRCameraPassthroughInternal_001";
constexpr std::size_t kGetConfigSlot = 9;
constexpr std::size_t kSetConfigSlot = 10;
struct CameraInterface {
void* instance;
void** methods;
};
CameraInterface camera_interface() {
vr::EVRInitError error = vr::VRInitError_None;
void* instance = vr::VR_GetGenericInterface(kCameraInterface, &error);
if (!instance || error != vr::VRInitError_None) {
const char* description = error == vr::VRInitError_None
? "the runtime did not provide the camera interface"
: vr::VR_GetVRInitErrorAsEnglishDescription(error);
throw std::runtime_error(std::string("Steam Frame camera source is unavailable: ") +
(description ? description : "unknown SteamVR interface error"));
}
auto** methods = *reinterpret_cast<void***>(instance);
if (!methods || !methods[kGetConfigSlot] || !methods[kSetConfigSlot]) {
throw std::runtime_error(
"Steam Frame camera source is unavailable: the private SteamVR "
"camera interface has no configuration accessors");
}
return {instance, methods};
}
#else
[[noreturn]] void camera_source_unavailable() {
throw std::runtime_error(
"Native Steam Frame camera source switching requires Linux ARM64; "
"this platform supports diagnostics only");
}
#endif
} // namespace
bool OpenVrCameraSource::rgb_available() {
#if defined(__linux__) && defined(__aarch64__)
const auto now = std::chrono::steady_clock::now();
if (now < next_detection_) return detected_;
next_detection_ = now + std::chrono::milliseconds(250);
std::error_code error;
auto node = std::filesystem::directory_iterator("/sys/class/video4linux", error);
if (error) return detected_;
const auto end = std::filesystem::directory_iterator();
bool found_sensor = false;
bool valid_read = false;
bool any_connected = false;
for (; node != end; node.increment(error)) {
if (error) return detected_; // Hotplug can invalidate the directory while scanning.
std::ifstream file(node->path() / "name");
std::string name;
std::getline(file, name);
if (name.rfind("arcimx616 ", 0) != 0) continue;
found_sensor = true;
const auto device = std::filesystem::path("/dev") / node->path().filename();
const int fd = ::open(device.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (fd < 0) continue;
// Read-only physical connection query used by the camera driver.
// Discover sensor nodes by name; their device numbers are not stable.
std::uint32_t sensor_connected = 0;
const int result = ::ioctl(fd, 0x800456c1UL, &sensor_connected);
::close(fd);
if (result == 0) {
valid_read = true;
if (sensor_connected == 1) any_connected = true;
}
}
if (error || (found_sensor && !valid_read)) return detected_;
if (any_connected) {
detected_ = true;
missing_since_ = {};
} else if (detected_) {
// A transient zero must not drive repeated camera source transitions.
if (missing_since_ == std::chrono::steady_clock::time_point{}) missing_since_ = now;
if (now - missing_since_ >= std::chrono::seconds(2)) detected_ = false;
}
return detected_;
#else
return false;
#endif
}
CameraConfig OpenVrCameraSource::read() {
#if defined(__linux__) && defined(__aarch64__)
const auto camera = camera_interface();
CameraConfig config;
config.fill(0xff);
using GetConfig = bool (*)(void*, std::uint8_t*);
const auto get = reinterpret_cast<GetConfig>(camera.methods[kGetConfigSlot]);
(void)get(camera.instance, config.data());
validate_camera_config(config);
return config;
#else
camera_source_unavailable();
#endif
}
void OpenVrCameraSource::write(const CameraConfig& config) {
validate_camera_config(config);
#if defined(__linux__) && defined(__aarch64__)
const auto camera = camera_interface();
using SetConfig = void (*)(void*, const std::uint8_t*);
const auto set = reinterpret_cast<SetConfig>(camera.methods[kSetConfigSlot]);
set(camera.instance, config.data());
#else
camera_source_unavailable();
#endif
}
} // namespace fps
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include "camera_source.hpp"
#include <chrono>
namespace fps {
class OpenVrCameraSource final : public CameraSource {
public:
bool rgb_available() override;
CameraConfig read() override;
void write(const CameraConfig& config) override;
private:
std::chrono::steady_clock::time_point next_detection_{};
std::chrono::steady_clock::time_point missing_since_{};
bool detected_ = false;
};
} // namespace fps
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2022 Niels Lohmann
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.
+24765
View File
File diff suppressed because it is too large. Load diff
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2015, Valve Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+118
View File
@@ -0,0 +1,118 @@
# Project name.
project(openvr_api)
set( LIBNAME "openvr_api" )
set(OPENVR_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../headers)
# Set some properies for specific files.
if(APPLE)
set(CMAKE_MACOSX_RPATH 1)
if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
set_source_files_properties(vrcore/pathtools_public.cpp vrcore/vrpathregistry_public.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++")
endif()
if(BUILD_SHARED OR BUILD_FRAMEWORK)
find_library(FOUNDATION_FRAMEWORK Foundation)
mark_as_advanced(FOUNDATION_FRAMEWORK)
set(EXTRA_LIBS ${EXTRA_LIBS} ${FOUNDATION_FRAMEWORK})
endif(BUILD_SHARED OR BUILD_FRAMEWORK)
elseif(WIN32)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
add_definitions( -DWIN64 )
set( LIBNAME "openvr_api64" )
endif()
endif()
# Add include folders.
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../headers ${CMAKE_CURRENT_SOURCE_DIR}/vrcore)
if(USE_CUSTOM_LIBCXX)
link_directories(
${LIBCXX_LIB_DIR}
)
endif()
# Set the source group and files.
set(CORE_FILES
openvr_api_public.cpp
jsoncpp.cpp
)
set(VRCORE_FILES
vrcore/dirtools_public.cpp
vrcore/envvartools_public.cpp
vrcore/pathtools_public.cpp
vrcore/sharedlibtools_public.cpp
vrcore/hmderrors_public.cpp
vrcore/vrpathregistry_public.cpp
vrcore/strtools_public.cpp
)
add_compile_definitions(VRCORE_NO_PLATFORM)
set(SOURCE_FILES
${CORE_FILES}
${VRCORE_FILES}
)
set(PUBLIC_HEADER_FILES
${OPENVR_HEADER_DIR}/openvr_driver.h
${OPENVR_HEADER_DIR}/openvr_capi.h
${OPENVR_HEADER_DIR}/openvr.h
)
source_group("Src" FILES
${CORE_FILES}
)
source_group("VRCore" FILES
${VRCORE_FILES}
)
# Build the library.
if(BUILD_SHARED)
add_library(${LIBNAME} SHARED ${SOURCE_FILES})
elseif(BUILD_FRAMEWORK)
set( LIBNAME "OpenVR" )
add_library( ${LIBNAME}
SHARED ${SOURCE_FILES}
${CMAKE_SOURCE_DIR}/headers/openvr.h
${CMAKE_SOURCE_DIR}/headers/openvr_api.cs
${CMAKE_SOURCE_DIR}/headers/openvr_api.json
${CMAKE_SOURCE_DIR}/headers/openvr_capi.h
${CMAKE_SOURCE_DIR}/headers/openvr_driver.h
)
set_target_properties(OpenVR PROPERTIES
FRAMEWORK TRUE
FRAMEWORK_VERSION A
MACOSX_FRAMEWORK_IDENTIFIER com.valvesoftware.OpenVR.framework
MACOSX_FRAMEWORK_INFO_PLIST ${CMAKE_SOURCE_DIR}/src/Info.plist
# "current version" in semantic format in Mach-O binary file
VERSION 1.0.6
# "compatibility version" in semantic format in Mach-O binary file
SOVERSION 1.0.0
PUBLIC_HEADER "${CMAKE_SOURCE_DIR}/headers/openvr.h;${CMAKE_SOURCE_DIR}/headers/openvr_api.cs;${CMAKE_SOURCE_DIR}/headers/openvr_api.json;${CMAKE_SOURCE_DIR}/headers/openvr_capi.h;${CMAKE_SOURCE_DIR}/headers/openvr_driver.h"
LINKER_LANGUAGE CXX
)
else()
add_library(${LIBNAME} STATIC ${SOURCE_FILES})
endif()
if(USE_CUSTOM_LIBCXX)
set(EXTRA_LIBS ${EXTRA_LIBS} c++ c++abi)
endif()
target_link_libraries(${LIBNAME} ${EXTRA_LIBS} ${CMAKE_DL_LIBS})
target_include_directories(${LIBNAME} PUBLIC ${OPENVR_HEADER_DIR})
install(TARGETS ${LIBNAME} DESTINATION lib)
install(FILES ${PUBLIC_HEADER_FILES} DESTINATION include/openvr)
# Generate a .pc file for linux environments
if(PLATFORM_NAME MATCHES "linux")
set(INSTALL_PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" CACHE PATH "Installation directory for pkgconfig (.pc) files")
CONFIGURE_FILE("openvr.pc.in" "openvr.pc" @ONLY)
set(OPENVR_PC ${CMAKE_CURRENT_BINARY_DIR}/openvr.pc)
if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL )
install(FILES ${OPENVR_PC} DESTINATION "${INSTALL_PKGCONFIG_DIR}")
endif()
endif()
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>com.valvesoftware.OpenVR.framework</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>OpenVR</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
</plist>
+39
View File
@@ -0,0 +1,39 @@
This is the source code for the OpenVR API client binding library which connects
OpenVR applications to the SteamVR runtime, taking into account the version
of the OpenVR interface they were compiled against.
The client binding library - openvr_api.dll on Windows, openvr_api.so on
Linux, and openvr_api.dylib or OpenVR.framework on macOS - knows how to find
and read the SteamVR runtime installation information which allows it to
find and dynamically connect to the installed runtime. In combination with the
interface version identifiers from /include/openvr.h which are baked
into applications at the time they are built, the OpenVR API client
binding library captures and conveys to the SteamVR runtime the version
of the OpenVR API interface behavior that the application expects.
Applications carry with them a private/local copy of the client binding
library when they ship, and they should install it locally to their
application. Applications should not install the client binding library
globally or attempt to link to a globally installed client binding library.
Doing so negates at least part of the ability for the client binding library
to accurately reflect the version of the OpenVR API that the application
was built against, and so hinders compatibility support in the face of
API changes.
Most applications should simply link to and redistribute with their application
the pre-built client binding library found in the /bin directory of this
repository. Some small number of applications which have specific requirements
around redistributing only binaries they build themselves should build
the client library from this source and either statically link it into
their application or redistribute the binary they build.
This is a cmake project, to build it use the version of cmake appropriate
for your platform. For example, to build on a POSIX system simply perform
cd src; mkdir _build; cd _build; cmake ..; make
and you will end up with the static library /src/bin/<arch>/libopenvr_api.a
To build a shared library, pass -DBUILD_SHARED=1 to cmake.
To build as a framework on apple platforms, pass -DBUILD_FRAMEWORK=1 to cmake.
To see a complete list of configurable build options, use `cmake -LAH`
+35
View File
@@ -0,0 +1,35 @@
//========= Copyright Valve Corporation ============//
#pragma once
namespace vr
{
class IVRClientCore
{
public:
/** Initializes the system */
virtual EVRInitError Init( vr::EVRApplicationType eApplicationType, const char *pStartupInfo ) = 0;
/** cleans up everything in vrclient.dll and prepares the DLL to be unloaded */
virtual void Cleanup() = 0;
/** checks to see if the specified interface/version is supported in this vrclient.dll */
virtual EVRInitError IsInterfaceVersionValid( const char *pchInterfaceVersion ) = 0;
/** Retrieves any interface from vrclient.dll */
virtual void *GetGenericInterface( const char *pchNameAndVersion, EVRInitError *peError ) = 0;
/** Returns true if any driver has an HMD attached. Can be called outside of Init/Cleanup */
virtual bool BIsHmdPresent() = 0;
/** Returns an English error string from inside vrclient.dll which might be newer than the API DLL */
virtual const char *GetEnglishStringForHmdError( vr::EVRInitError eError ) = 0;
/** Returns an error symbol from inside vrclient.dll which might be newer than the API DLL */
virtual const char *GetIDForVRInitError( vr::EVRInitError eError ) = 0;
};
static const char * const IVRClientCore_Version = "IVRClientCore_003";
}
+284
View File
@@ -0,0 +1,284 @@
/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).
/// It is intended to be used with #include "json/json-forwards.h"
/// This header provides forward declaration for all JsonCpp types.
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
/*
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
The author (Baptiste Lepilleur) explicitly disclaims copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
Copyright (c) 2007-2010 Baptiste Lepilleur
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.
========================================================================
(END LICENSE TEXT)
The MIT license is compatible with both the GPL and commercial
software, affording one all of the rights of Public Domain with the
minor nuisance of being required to keep the above copyright notice
and license text in the source code. Note also that by accepting the
Public Domain "license" you can re-license your copy using whatever
license you like.
*/
// //////////////////////////////////////////////////////////////////////
// End of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
# define JSON_FORWARD_AMALGATED_H_INCLUDED
/// If defined, indicates that the source file is amalgated
/// to prevent private header inclusion.
#define JSON_IS_AMALGAMATION
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
/// If defined, indicates that json library is embedded in CppTL library.
//# define JSON_IN_CPPTL 1
/// If defined, indicates that json may leverage CppTL library
//# define JSON_USE_CPPTL 1
/// If defined, indicates that cpptl vector based map should be used instead of
/// std::map
/// as Value container.
//# define JSON_USE_CPPTL_SMALLMAP 1
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION
#define JSON_USE_EXCEPTION 1
#endif
/// If defined, indicates that the source file is amalgated
/// to prevent private header inclusion.
/// Remarks: it is automatically defined in the generated amalgated header.
// #define JSON_IS_AMALGAMATION
#ifdef JSON_IN_CPPTL
#include <cpptl/config.h>
#ifndef JSON_USE_CPPTL
#define JSON_USE_CPPTL 1
#endif
#endif
#ifdef JSON_IN_CPPTL
#define JSON_API CPPTL_API
#elif defined(JSON_DLL_BUILD)
#if defined(_MSC_VER)
#define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#elif defined(JSON_DLL)
#if defined(_MSC_VER)
#define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#endif // ifdef JSON_IN_CPPTL
#if !defined(JSON_API)
#define JSON_API
#endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
#if defined(_MSC_VER) // MSVC
# if _MSC_VER <= 1200 // MSVC 6
// Microsoft Visual Studio 6 only support conversion from __int64 to double
// (no conversion from unsigned __int64).
# define JSON_USE_INT64_DOUBLE_CONVERSION 1
// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
// characters in the debug information)
// All projects I've ever seen with VS6 were using this globally (not bothering
// with pragma push/pop).
# pragma warning(disable : 4786)
# endif // MSVC 6
# if _MSC_VER >= 1500 // MSVC 2008
/// Indicates that the following function is deprecated.
# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
# endif
#endif // defined(_MSC_VER)
#ifndef JSON_HAS_RVALUE_REFERENCES
#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // MSVC >= 2010
#ifdef __clang__
#if __has_feature(cxx_rvalue_references)
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // has_feature
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // GXX_EXPERIMENTAL
#endif // __clang__ || __GNUC__
#endif // not defined JSON_HAS_RVALUE_REFERENCES
#ifndef JSON_HAS_RVALUE_REFERENCES
#define JSON_HAS_RVALUE_REFERENCES 0
#endif
#ifdef __clang__
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
# endif // GNUC version
#endif // __clang__ || __GNUC__
#if !defined(JSONCPP_DEPRECATED)
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
namespace Json {
typedef int Int;
typedef unsigned int UInt;
#if defined(JSON_NO_INT64)
typedef int LargestInt;
typedef unsigned int LargestUInt;
#undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio
typedef __int64 Int64;
typedef unsigned __int64 UInt64;
#else // if defined(_MSC_VER) // Other platforms, use long long
typedef long long int Int64;
typedef unsigned long long int UInt64;
#endif // if defined(_MSC_VER)
typedef Int64 LargestInt;
typedef UInt64 LargestUInt;
#define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64)
} // end namespace Json
#endif // JSON_CONFIG_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
// writer.h
class FastWriter;
class StyledWriter;
// reader.h
class Reader;
// features.h
class Features;
// value.h
typedef unsigned int ArrayIndex;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
+2077
View File
File diff suppressed because it is too large. Load diff
+5266
View File
File diff suppressed because it is too large. Load diff
+11
View File
@@ -0,0 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=${prefix}/lib
includedir=${prefix}/include/openvr
Name: openvr
Description: OpenVR is an API and runtime that allos access to VR hardware.
Version: @OPENVR_VERSION@
Libs: -L${libdir} -lopenvr_api -ldl
Cflags: -I${includedir}
+353
View File
@@ -0,0 +1,353 @@
//========= Copyright Valve Corporation ============//
#define VR_API_EXPORT 1
#include "openvr.h"
#include "ivrclientcore.h"
#include <vrcore/pathtools_public.h>
#include <vrcore/sharedlibtools_public.h>
#include <vrcore/envvartools_public.h>
#include "hmderrors_public.h"
#include <vrcore/strtools_public.h>
#include <vrcore/vrpathregistry_public.h>
#include <mutex>
using vr::EVRInitError;
using vr::IVRSystem;
using vr::IVRClientCore;
using vr::VRInitError_None;
// figure out how to import from the VR API dll
#if defined(_WIN32)
#if !defined(OPENVR_BUILD_STATIC)
#define VR_EXPORT_INTERFACE extern "C" __declspec( dllexport )
#else
#define VR_EXPORT_INTERFACE extern "C"
#endif
#elif defined(__GNUC__) || defined(COMPILER_GCC) || defined(__APPLE__)
#define VR_EXPORT_INTERFACE extern "C" __attribute__((visibility("default")))
#else
#error "Unsupported Platform."
#endif
namespace vr
{
static void *g_pVRModule = NULL;
static IVRClientCore *g_pHmdSystem = NULL;
static std::recursive_mutex g_mutexSystem;
typedef void* (*VRClientCoreFactoryFn)(const char *pInterfaceName, int *pReturnCode);
static uint32_t g_nVRToken = 0;
uint32_t VR_GetInitToken()
{
return g_nVRToken;
}
EVRInitError VR_LoadHmdSystemInternal();
void CleanupInternalInterfaces();
uint32_t VR_InitInternal2( EVRInitError *peError, vr::EVRApplicationType eApplicationType, const char *pStartupInfo )
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
EVRInitError err = VR_LoadHmdSystemInternal();
if ( err == vr::VRInitError_None )
{
err = g_pHmdSystem->Init( eApplicationType, pStartupInfo );
}
if ( peError )
*peError = err;
if ( err != VRInitError_None )
{
SharedLib_Unload( g_pVRModule );
g_pHmdSystem = NULL;
g_pVRModule = NULL;
return 0;
}
return ++g_nVRToken;
}
VR_INTERFACE uint32_t VR_CALLTYPE VR_InitInternal( EVRInitError *peError, EVRApplicationType eApplicationType );
uint32_t VR_InitInternal( EVRInitError *peError, vr::EVRApplicationType eApplicationType )
{
return VR_InitInternal2( peError, eApplicationType, nullptr );
}
void VR_ShutdownInternal()
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
#if !defined( VR_API_PUBLIC )
CleanupInternalInterfaces();
#endif
if ( g_pHmdSystem )
{
g_pHmdSystem->Cleanup();
g_pHmdSystem = NULL;
}
if ( g_pVRModule )
{
SharedLib_Unload( g_pVRModule );
g_pVRModule = NULL;
}
++g_nVRToken;
}
EVRInitError VR_LoadHmdSystemInternal()
{
std::string sRuntimePath, sConfigPath, sLogPath;
bool bReadPathRegistry = CVRPathRegistry_Public::GetPaths( &sRuntimePath, &sConfigPath, &sLogPath, NULL, NULL );
if( !bReadPathRegistry )
{
return vr::VRInitError_Init_PathRegistryNotFound;
}
// figure out where we're going to look for vrclient.dll
// see if the specified path actually exists.
if( !Path_IsDirectory( sRuntimePath ) )
{
return vr::VRInitError_Init_InstallationNotFound;
}
// Because we don't have a way to select debug vs. release yet we'll just
// use debug if it's there
#if defined( WIN32 ) || defined( LINUX32 )
std::string sTestPath = Path_Join( sRuntimePath, "bin" );
#else
std::string sTestPath = Path_Join( sRuntimePath, "bin", PLATSUBDIR );
#endif
if( !Path_IsDirectory( sTestPath ) )
{
return vr::VRInitError_Init_InstallationCorrupt;
}
#if defined( WIN64 )
std::string sDLLPath = Path_Join( sTestPath, "vrclient_x64" DYNAMIC_LIB_EXT );
#else
std::string sDLLPath = Path_Join( sTestPath, "vrclient" DYNAMIC_LIB_EXT );
#endif
// only look in the override
void *pMod = SharedLib_Load( sDLLPath.c_str() );
// nothing more to do if we can't load the DLL
if( !pMod )
{
return vr::VRInitError_Init_VRClientDLLNotFound;
}
VRClientCoreFactoryFn fnFactory = ( VRClientCoreFactoryFn )( SharedLib_GetFunction( pMod, "VRClientCoreFactory" ) );
if( !fnFactory )
{
SharedLib_Unload( pMod );
return vr::VRInitError_Init_FactoryNotFound;
}
int nReturnCode = 0;
g_pHmdSystem = static_cast< IVRClientCore * > ( fnFactory( vr::IVRClientCore_Version, &nReturnCode ) );
if( !g_pHmdSystem )
{
SharedLib_Unload( pMod );
return vr::VRInitError_Init_InterfaceNotFound;
}
g_pVRModule = pMod;
return VRInitError_None;
}
void *VR_GetGenericInterface(const char *pchInterfaceVersion, EVRInitError *peError)
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
if (!g_pHmdSystem)
{
if (peError)
*peError = vr::VRInitError_Init_NotInitialized;
return NULL;
}
return g_pHmdSystem->GetGenericInterface(pchInterfaceVersion, peError);
}
bool VR_IsInterfaceVersionValid(const char *pchInterfaceVersion)
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
if (!g_pHmdSystem)
{
return false;
}
return g_pHmdSystem->IsInterfaceVersionValid(pchInterfaceVersion) == VRInitError_None;
}
bool VR_IsHmdPresent()
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
if( g_pHmdSystem )
{
// if we're already initialized, just call through
return g_pHmdSystem->BIsHmdPresent();
}
else
{
// otherwise we need to do a bit more work
EVRInitError err = VR_LoadHmdSystemInternal();
if( err != VRInitError_None )
return false;
bool bHasHmd = g_pHmdSystem->BIsHmdPresent();
g_pHmdSystem = NULL;
SharedLib_Unload( g_pVRModule );
g_pVRModule = NULL;
return bHasHmd;
}
}
/** Returns true if the OpenVR runtime is installed. */
bool VR_IsRuntimeInstalled()
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
if( g_pHmdSystem )
{
// if we're already initialized, OpenVR is obviously installed
return true;
}
else
{
// otherwise we need to do a bit more work
std::string sRuntimePath, sConfigPath, sLogPath;
bool bReadPathRegistry = CVRPathRegistry_Public::GetPaths( &sRuntimePath, &sConfigPath, &sLogPath, NULL, NULL );
if( !bReadPathRegistry )
{
return false;
}
// figure out where we're going to look for vrclient.dll
// see if the specified path actually exists.
if( !Path_IsDirectory( sRuntimePath ) )
{
return false;
}
// the installation may be corrupt in some way, but it certainly looks installed
return true;
}
}
// -------------------------------------------------------------------------------
// Purpose: This is the old Runtime Path interface that is no longer exported in the
// latest header. We still want to export it from the DLL, though, so updating
// to a new DLL doesn't break old compiled code. This version was not thread
// safe and could change the buffer pointer to by a previous result on a
// subsequent call
// -------------------------------------------------------------------------------
VR_EXPORT_INTERFACE const char *VR_CALLTYPE VR_RuntimePath();
/** Returns where OpenVR runtime is installed. */
const char *VR_RuntimePath()
{
static char rchBuffer[1024];
uint32_t unRequiredSize;
if ( VR_GetRuntimePath( rchBuffer, sizeof( rchBuffer ), &unRequiredSize ) && unRequiredSize < sizeof( rchBuffer ) )
{
return rchBuffer;
}
else
{
return nullptr;
}
}
/** Returns where OpenVR runtime is installed. */
bool VR_GetRuntimePath( char *pchPathBuffer, uint32_t unBufferSize, uint32_t *punRequiredBufferSize )
{
// otherwise we need to do a bit more work
std::string sRuntimePath;
*punRequiredBufferSize = 0;
bool bReadPathRegistry = CVRPathRegistry_Public::GetPaths( &sRuntimePath, nullptr, nullptr, nullptr, nullptr );
if ( !bReadPathRegistry )
{
return false;
}
// figure out where we're going to look for vrclient.dll
// see if the specified path actually exists.
if ( !Path_IsDirectory( sRuntimePath ) )
{
return false;
}
*punRequiredBufferSize = (uint32_t)sRuntimePath.size() + 1;
if ( sRuntimePath.size() >= unBufferSize )
{
*pchPathBuffer = '\0';
}
else
{
strcpy_safe( pchPathBuffer, unBufferSize, sRuntimePath.c_str() );
}
return true;
}
/** Returns the symbol version of an HMD error. */
const char *VR_GetVRInitErrorAsSymbol( EVRInitError error )
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
if( g_pHmdSystem )
return g_pHmdSystem->GetIDForVRInitError( error );
else
return GetIDForVRInitError( error );
}
/** Returns the english string version of an HMD error. */
const char *VR_GetVRInitErrorAsEnglishDescription( EVRInitError error )
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
if ( g_pHmdSystem )
return g_pHmdSystem->GetEnglishStringForHmdError( error );
else
return GetEnglishStringForHmdError( error );
}
VR_INTERFACE const char *VR_CALLTYPE VR_GetStringForHmdError( vr::EVRInitError error );
/** Returns the english string version of an HMD error. */
const char *VR_GetStringForHmdError( EVRInitError error )
{
return VR_GetVRInitErrorAsEnglishDescription( error );
}
}
+639
View File
@@ -0,0 +1,639 @@
//========= Copyright Valve Corporation ============//
#include <vrcore/dirtools_public.h>
#include <vrcore/strtools_public.h>
#include <vrcore/pathtools_public.h>
#include <errno.h>
#include <string.h>
#ifdef _WIN32
#include "windows.h"
#else
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif
#if defined( OSX )
#include <sys/syslimits.h>
#endif
#define HMD_INVALID_HANDLE_VALUE ( ( HMDHANDLE )( LONG_PTR )-1 )
//-----------------------------------------------------------------------------
// Purpose: utility function to create dirs & subdirs
//-----------------------------------------------------------------------------
bool BCreateDirectoryRecursive( const char *pchPath )
{
// Does it already exist?
if ( Path_IsDirectory( pchPath ) )
return true;
// copy the path into something we can munge
int len = (int)strlen( pchPath );
char *path = (char *)malloc( len + 1 );
strcpy( path, pchPath );
// Walk backwards to first non-existing dir that we find
char *s = path + len - 1;
const char slash = Path_GetSlash();
while ( s > path )
{
if ( *s == slash )
{
*s = '\0';
bool bExists = Path_IsDirectory( path );
*s = slash;
if ( bExists )
{
++s;
break;
}
}
--s;
}
// and then move forwards from there
while ( *s )
{
if ( *s == slash )
{
*s = '\0';
BCreateDirectory( path );
*s = slash;
}
s++;
}
bool bRetVal = BCreateDirectory( path );
free( path );
return bRetVal;
}
//-----------------------------------------------------------------------------
// Purpose: Creates the directory, returning true if it is created, or if it already existed
//-----------------------------------------------------------------------------
bool BCreateDirectory( const char *pchPath )
{
#ifdef WIN32
std::wstring wPath = UTF8to16( pchPath );
if ( ::CreateDirectoryW( wPath.c_str(), NULL ) )
return true;
if ( ::GetLastError() == ERROR_ALREADY_EXISTS )
return true;
return false;
#else
int i = mkdir( pchPath, S_IRWXU | S_IRWXG | S_IRWXO );
if ( i == 0 )
return true;
if ( errno == EEXIST )
return true;
return false;
#endif
}
#if !defined( VRCORE_NO_PLATFORM )
#include <vrcore/log.h>
//-----------------------------------------------------------------------------
CDirIterator::CDirIterator( const char *pchPath, const char *pchPattern )
{
m_pFindData = NULL;
// put in the path
if ( pchPath )
{
std::string sPathAndPattern = Path_Join( pchPath, pchPattern );
Init( sPathAndPattern.c_str() );
}
else
{
m_bNoFiles = true;
m_bUsedFirstFile = true;
#if defined( _WIN32 )
m_hFind = HMD_INVALID_HANDLE_VALUE;
m_pFindData = new WIN32_FIND_DATAW;
#else
m_hFind = -1;
m_pFindData = new _finddata_t;
#endif
memset( m_pFindData, 0, sizeof( *m_pFindData ) );
}
}
//-----------------------------------------------------------------------------
// Purpose: Initialize iteration structure
//-----------------------------------------------------------------------------
void CDirIterator::Init( const std::string &sPathAndPattern )
{
#if defined( _WIN32 )
m_pFindData = new WIN32_FIND_DATAW;
memset( m_pFindData, 0, sizeof( *m_pFindData ) );
std::wstring sWPathAndPattern = UTF8to16( sPathAndPattern.c_str() );
m_hFind = FindFirstFileW( sWPathAndPattern.c_str(), m_pFindData );
bool bSuccess = ( m_hFind != HMD_INVALID_HANDLE_VALUE );
// Conversion should never fail with valid filenames...
if ( bSuccess )
{
m_sFilename = UTF16to8( m_pFindData->cFileName );
}
#else
m_pFindData = new _finddata_t;
memset( m_pFindData, 0, sizeof( *m_pFindData ) );
m_hFind = _findfirst( sPathAndPattern.c_str(), m_pFindData );
bool bSuccess = ( m_hFind != -1 );
#endif
if ( !bSuccess )
{
m_bNoFiles = true;
m_bUsedFirstFile = true;
}
else
{
m_bNoFiles = false;
// if we're pointing at . or .., set it as used
// so we'll look for the next item when BNextFile() is called
m_bUsedFirstFile = !BValidFilename();
}
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CDirIterator::~CDirIterator()
{
#if defined( _WIN32 )
if ( m_hFind != HMD_INVALID_HANDLE_VALUE )
{
FindClose( m_hFind );
}
delete m_pFindData;
#else
if ( m_hFind != -1 )
{
_findclose( m_hFind );
}
if ( m_pFindData )
{
for ( int i = 0; i < m_pFindData->numNames; i++ )
{
// scandir allocates with malloc, so free with free
free( m_pFindData->namelist[ i ] );
}
free( m_pFindData->namelist );
delete m_pFindData;
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Check for successful construction
//-----------------------------------------------------------------------------
bool CDirIterator::IsValid() const
{
#if defined( _WIN32 )
return m_hFind != HMD_INVALID_HANDLE_VALUE;
#else
return m_hFind != -1;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Filter out . and ..
//-----------------------------------------------------------------------------
bool CDirIterator::BValidFilename()
{
#if defined( _WIN32 )
const char *pch = m_sFilename.c_str();
#else
const char *pch = m_pFindData->name;
#endif
if ( ( pch[ 0 ] == '.' && pch[ 1 ] == 0 ) || ( pch[ 0 ] == '.' && pch[ 1 ] == '.' && pch[ 2 ] == 0 ) )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: returns true if there is a file to read
//-----------------------------------------------------------------------------
bool CDirIterator::BNextFile()
{
if ( m_bNoFiles )
return false;
// use the first result
if ( !m_bUsedFirstFile )
{
m_bUsedFirstFile = true;
return true;
}
// find the next item
for ( ;; )
{
#if defined( _WIN32 )
bool bFound = ( FindNextFileW( m_hFind, m_pFindData ) != FALSE );
// Conversion should never fail with valid filenames...
if ( bFound )
{
m_sFilename = UTF16to8( m_pFindData->cFileName );
}
#else
bool bFound = ( _findnext( m_hFind, m_pFindData ) == 0 );
#endif
if ( !bFound )
{
// done
m_bNoFiles = true;
return false;
}
// skip over the '.' and '..' paths
if ( !BValidFilename() )
continue;
break;
}
// have one more file
return true;
}
//-----------------------------------------------------------------------------
// Purpose: returns name (filename portion only) of the current file.
// Name is emitted in UTF-8 encoding.
// NOTE: This method returns a pointer into a static buffer, either a member
// or the buffer inside the _finddata_t.
//-----------------------------------------------------------------------------
std::string CDirIterator::CurrentFileName()
{
#if defined( _WIN32 )
return m_sFilename;
#else
return m_pFindData->name;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: returns size of the file
//-----------------------------------------------------------------------------
int64_t CDirIterator::CurrentFileLength() const
{
#if defined( _WIN32 )
LARGE_INTEGER li = { { m_pFindData->nFileSizeLow, ( LONG )m_pFindData->nFileSizeHigh } };
return li.QuadPart;
#else
return ( int64_t )m_pFindData->size;
#endif
}
#if defined( _WIN32 )
//-----------------------------------------------------------------------------
// Purpose: utility for converting a regular time to a system filetime
//-----------------------------------------------------------------------------
FILETIME UnixTimeToFileTime( int64_t t )
{
FILETIME retf;
long long int ll;
ll = ( ( long long )t * 10000000LL );
ll += 116444736000000000LL;
retf.dwHighDateTime = ll >> 32;
retf.dwLowDateTime = ll & 0x00000000FFFFFFFF;
return retf;
}
//-----------------------------------------------------------------------------
// Purpose: utility for converting a system filetime to a regular time
//-----------------------------------------------------------------------------
int64_t FileTimeToUnixTime( FILETIME filetime )
{
long long int t = filetime.dwHighDateTime;
t <<= 32;
t += ( unsigned long )filetime.dwLowDateTime;
t -= 116444736000000000LL;
return t / 10000000;
}
#endif // _WIN32
//-----------------------------------------------------------------------------
// Purpose: returns last write time of the file
//-----------------------------------------------------------------------------
int64_t CDirIterator::CurrentFileWriteTime() const
{
#if defined( _WIN32 )
return FileTimeToUnixTime( m_pFindData->ftLastWriteTime );
#else
return m_pFindData->time_write;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: returns the creation time of the file
//-----------------------------------------------------------------------------
int64_t CDirIterator::CurrentFileCreateTime() const
{
#if defined( _WIN32 )
return FileTimeToUnixTime( m_pFindData->ftCreationTime );
#else
return m_pFindData->time_create;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: returns whether current item under examination is a directory
//-----------------------------------------------------------------------------
bool CDirIterator::BCurrentIsDir() const
{
#if defined( _WIN32 )
return ( m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0;
#else
return ( m_pFindData->attrib & _A_SUBDIR ? true : false );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: returns whether current item under examination is a hidden file
//-----------------------------------------------------------------------------
bool CDirIterator::BCurrentIsHidden() const
{
#if defined( _WIN32 )
return ( m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) != 0;
#else
return ( m_pFindData->attrib & _A_HIDDEN ? true : false );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: returns whether current item under examination is read-only
//-----------------------------------------------------------------------------
bool CDirIterator::BCurrentIsReadOnly() const
{
#if defined( _WIN32 )
return ( m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_READONLY ) != 0;
#else
return ( m_pFindData->attrib & _A_RDONLY ? true : false );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: returns whether current item under examination is marked as a system file
//-----------------------------------------------------------------------------
bool CDirIterator::BCurrentIsSystem() const
{
#if defined( _WIN32 )
return ( m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ) != 0;
#else
return ( m_pFindData->attrib & _A_SYSTEM ? true : false );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: returns whether current item under examination is marked for archiving
//-----------------------------------------------------------------------------
bool CDirIterator::BCurrentIsMarkedForArchive() const
{
#if defined( _WIN32 )
return ( m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE ) != 0;
#else
return ( m_pFindData->attrib & _A_ARCH ? true : false );
#endif
}
//-----------------------------------------------------------------------------
//
// A huge pile of stuff from fileio.cpp in steam/tier1
//
//-----------------------------------------------------------------------------
#if !defined( _WIN32 )
// findfirst/findnext implementation from filesystem/linux_support.[h|cpp]
static char selectBuf[ PATH_MAX ];
#if defined( OSX ) && !defined( __MAC_10_8 )
static int FileSelect( direntBig_t *ent )
#elif defined( LINUX ) || defined( OSX )
static int FileSelect( const direntBig_t *ent )
#else
#error
#endif
{
const char *mask = selectBuf;
const char *name = ent->d_name;
return FileSelect( name, mask );
}
static int FileSelect( const char *name, const char *mask )
{
// printf("Test:%s %s\n",mask,name);
if ( !strcmp( name, "." ) || !strcmp( name, ".." ) )
return 0;
if ( !strcmp( mask, "*.*" ) || !strcmp( mask, "*" ) )
return 1;
while ( *mask && *name )
{
if ( *mask == '*' )
{
mask++; // move to the next char in the mask
if ( !*mask ) // if this is the end of the mask its a match
{
return 1;
}
while ( *name && toupper( *name ) != toupper( *mask ) )
{ // while the two don't meet up again
name++;
}
if ( !*name )
{ // end of the name
break;
}
}
else if ( *mask != '?' )
{
if ( toupper( *mask ) != toupper( *name ) )
{ // mismatched!
return 0;
}
else
{
mask++;
name++;
if ( !*mask && !*name )
{ // if its at the end of the buffer
return 1;
}
}
}
else /* mask is "?", we don't care*/
{
mask++;
name++;
}
}
return ( !*mask && !*name ); // both of the strings are at the end
}
int FillDataStruct( _finddata_t *dat )
{
statBig_t fileStat;
if ( dat->curName >= dat->numNames )
return -1;
strncpy( dat->name, dat->namelist[ dat->curName ]->d_name, sizeof( dat->name ) );
char szFullPath[ MAX_PATH ];
int nWriteSize = snprintf( szFullPath, sizeof( szFullPath ), "%s%c%s", dat->dirBase, Path_GetSlash(), dat->name );
if ( nWriteSize >= sizeof( szFullPath ) )
{
Log( LogError, "File path truncated\n" );
}
if ( statBig( szFullPath, &fileStat ) == 0 )
{
dat->attrib = fileStat.st_mode;
dat->size = fileStat.st_size;
dat->time_write = fileStat.st_mtime;
dat->time_create = fileStat.st_ctime;
}
else
{
dat->attrib = 0;
dat->size = 0;
dat->time_write = 0;
dat->time_create = 0;
}
free( dat->namelist[ dat->curName ] );
dat->namelist[ dat->curName ] = NULL;
dat->curName++;
return 1;
}
int _findfirst( const char *fileName, _finddata_t *dat )
{
char nameStore[ MAX_PATH ];
char *dir = NULL;
int n, iret = -1;
strncpy( nameStore, fileName, sizeof( nameStore ) );
if ( strrchr( nameStore, '/' ) )
{
dir = nameStore;
while ( strrchr( dir, '/' ) )
{
statBig_t dirChk;
// zero this with the dir name
dir = strrchr( nameStore, '/' );
*dir = '\0';
if ( dir == nameStore )
{
strcpy( nameStore, "/" ); // C++11 would prefer no `dir = "/";`
}
else
{
dir = nameStore;
}
if ( statBig( dir, &dirChk ) == 0 && S_ISDIR( dirChk.st_mode ) )
{
break;
}
}
}
else
{
// couldn't find a dir separator...
return -1;
}
if ( strlen( dir ) > 0 )
{
if ( strlen( dir ) == 1 )
strncpy( selectBuf, fileName + 1, sizeof( selectBuf ) );
else
strncpy( selectBuf, fileName + strlen( dir ) + 1, sizeof( selectBuf ) );
n = scandirBig( dir, &dat->namelist, FileSelect, alphasortBig );
if ( n < 0 )
{
// silently return, nothing interesting
}
else
{
dat->curName = 0;
dat->numNames = n; // n is the number of matches
strncpy( dat->dirBase, dir, sizeof( dat->dirBase ) );
iret = FillDataStruct( dat );
if ( iret < 0 )
{
free( dat->namelist );
dat->namelist = NULL;
dat->curName = 0;
dat->numNames = 0;
}
}
}
// printf("Returning: %i \n",iret);
return iret;
}
int _findnext( int64_t handle, _finddata_t *dat )
{
if ( dat->curName >= dat->numNames )
{
free( dat->namelist );
dat->namelist = NULL;
dat->curName = 0;
dat->numNames = 0;
return -1; // no matches left
}
FillDataStruct( dat );
return 0;
}
bool _findclose( int64_t handle )
{
return true;
}
#endif // !defined( _WIN32 )
#endif // VRCORE_NO_PLATFORM
+83
View File
@@ -0,0 +1,83 @@
//========= Copyright Valve Corporation ============//
#pragma once
#include <stdint.h>
#include <string>
#if !defined(_WIN32)
#include <sys/types.h>
#include <sys/stat.h>
#endif
extern bool BCreateDirectoryRecursive( const char *pchPath );
extern bool BCreateDirectory( const char *pchPath );
#if !defined( VRCORE_NO_PLATFORM )
// for finddata_t
#include <vrcore/platform.h>
typedef void *HMDHANDLE;
// iterator class, initialize with the path & pattern you want to want files/dirs for.
//
// all string setters and accessors use UTF-8 encoding.
class CDirIterator
{
public:
CDirIterator( const char *pchPath, const char *pchPattern );
~CDirIterator();
bool IsValid() const;
// fetch the next file
bool BNextFile();
// name of the current file - file portion only, not full path
std::string CurrentFileName();
// size of the current file
int64_t CurrentFileLength() const;
// creation time of the current file
int64_t CurrentFileCreateTime() const;
// mod time of the current file
int64_t CurrentFileWriteTime() const;
// mode/type checks:
// is the current file actually a directory?
bool BCurrentIsDir() const;
// is the current file hidden?
bool BCurrentIsHidden() const;
// is the current file read-only?
bool BCurrentIsReadOnly() const;
// is the current file a system file?
bool BCurrentIsSystem() const;
// is the current file's archive bit set?
bool BCurrentIsMarkedForArchive() const;
private:
void Init( const std::string &sPathAndPattern );
bool BValidFilename();
bool m_bNoFiles, m_bUsedFirstFile;
#if defined( _WIN32 )
HMDHANDLE m_hFind;
struct _WIN32_FIND_DATAW *m_pFindData;
std::string m_sFilename;
#else
int64_t m_hFind;
struct _finddata_t *m_pFindData;
#endif
};
#endif // VRCORE_NO_PLATFORM
+88
View File
@@ -0,0 +1,88 @@
//========= Copyright Valve Corporation ============//
#include <vrcore/envvartools_public.h>
#include <vrcore/strtools_public.h>
#include <stdlib.h>
#include <string>
#include <cctype>
#if defined(_WIN32)
#include <windows.h>
#undef GetEnvironmentVariable
#undef SetEnvironmentVariable
#endif
std::string GetEnvironmentVariable( const char *pchVarName )
{
#if defined(_WIN32)
char rchValue[32767]; // max size for an env var on Windows
DWORD cChars = GetEnvironmentVariableA( pchVarName, rchValue, sizeof( rchValue ) );
if( cChars == 0 )
return "";
else
return rchValue;
#elif defined(POSIX)
char *pchValue = getenv( pchVarName );
if( pchValue )
return pchValue;
else
return "";
#else
#error "Unsupported Platform"
#endif
}
bool GetEnvironmentVariableAsBool( const char *pchVarName, bool bDefault )
{
std::string sValue = GetEnvironmentVariable( pchVarName );
if ( sValue.empty() )
{
return bDefault;
}
sValue = StringToLower( sValue );
std::string sYesValues[] = { "y", "yes", "true" };
std::string sNoValues[] = { "n", "no", "false" };
for ( std::string &sMatch : sYesValues )
{
if ( sMatch == sValue )
{
return true;
}
}
for ( std::string &sMatch : sNoValues )
{
if ( sMatch == sValue )
{
return false;
}
}
if ( std::isdigit( sValue.at(0) ) )
{
return atoi( sValue.c_str() ) != 0;
}
fprintf( stderr,
"GetEnvironmentVariableAsBool(%s): Unable to parse value '%s', using default %d\n",
pchVarName, sValue.c_str(), bDefault );
return bDefault;
}
bool SetEnvironmentVariable( const char *pchVarName, const char *pchVarValue )
{
#if defined(_WIN32)
return 0 != SetEnvironmentVariableA( pchVarName, pchVarValue );
#elif defined(POSIX)
if( pchVarValue == NULL )
return 0 == unsetenv( pchVarName );
else
return 0 == setenv( pchVarName, pchVarValue, 1 );
#else
#error "Unsupported Platform"
#endif
}
+12
View File
@@ -0,0 +1,12 @@
//========= Copyright Valve Corporation ============//
#pragma once
#include <string>
#ifdef SetEnvironmentVariable
#undef SetEnvironmentVariable
#endif
std::string GetEnvironmentVariable( const char *pchVarName );
bool GetEnvironmentVariableAsBool( const char *pchVarName, bool bDefault );
bool SetEnvironmentVariable( const char *pchVarName, const char *pchVarValue );
+382
View File
@@ -0,0 +1,382 @@
//========= Copyright Valve Corporation ============//
#include "openvr.h"
#include "hmderrors_public.h"
#include <stdio.h>
#include <algorithm>
using namespace vr;
#define RETURN_ENUM_AS_STRING(enumValue) case enumValue: return #enumValue;
const char *GetEnglishStringForHmdError( vr::EVRInitError eError )
{
switch( eError )
{
case VRInitError_None: return "No Error (0)";
case VRInitError_Init_InstallationNotFound: return "Installation Not Found (100)";
case VRInitError_Init_InstallationCorrupt: return "Installation Corrupt (101)";
case VRInitError_Init_VRClientDLLNotFound: return "vrclient Shared Lib Not Found (102)";
case VRInitError_Init_FileNotFound: return "File Not Found (103)";
case VRInitError_Init_FactoryNotFound: return "Factory Function Not Found (104)";
case VRInitError_Init_InterfaceNotFound: return "Interface Not Found (105)";
case VRInitError_Init_InvalidInterface: return "Invalid Interface (106)";
case VRInitError_Init_UserConfigDirectoryInvalid: return "User Config Directory Invalid (107)";
case VRInitError_Init_HmdNotFound: return "Hmd Not Found (108)";
case VRInitError_Init_NotInitialized: return "Not Initialized (109)";
case VRInitError_Init_PathRegistryNotFound: return "Installation path could not be located (110)";
case VRInitError_Init_NoConfigPath: return "Config path could not be located (111)";
case VRInitError_Init_NoLogPath: return "Log path could not be located (112)";
case VRInitError_Init_PathRegistryNotWritable: return "Unable to write path registry (113)";
case VRInitError_Init_AppInfoInitFailed: return "App info manager init failed (114)";
case VRInitError_Init_Retry: return "Internal Retry (115)";
case VRInitError_Init_InitCanceledByUser: return "User Canceled Init (116)";
case VRInitError_Init_AnotherAppLaunching: return "Another app was already launching (117)";
case VRInitError_Init_SettingsInitFailed: return "Settings manager init failed (118)";
case VRInitError_Init_ShuttingDown: return "VR system shutting down (119)";
case VRInitError_Init_TooManyObjects: return "Too many tracked objects (120)";
case VRInitError_Init_NoServerForBackgroundApp: return "Not starting vrserver for background app (121)";
case VRInitError_Init_NotSupportedWithCompositor: return "The requested interface is incompatible with the compositor and the compositor is running (122)";
case VRInitError_Init_NotAvailableToUtilityApps: return "This interface is not available to utility applications (123)";
case VRInitError_Init_Internal: return "vrserver internal error (124)";
case VRInitError_Init_HmdDriverIdIsNone: return "Hmd DriverId is invalid (125)";
case VRInitError_Init_HmdNotFoundPresenceFailed: return "Hmd Not Found Presence Failed (126)";
case VRInitError_Init_VRMonitorNotFound: return "VR Monitor Not Found (127)";
case VRInitError_Init_VRMonitorStartupFailed: return "VR Monitor startup failed (128)";
case VRInitError_Init_LowPowerWatchdogNotSupported: return "Low Power Watchdog Not Supported (129)";
case VRInitError_Init_InvalidApplicationType: return "Invalid Application Type (130)";
case VRInitError_Init_NotAvailableToWatchdogApps: return "Not available to watchdog apps (131)";
case VRInitError_Init_WatchdogDisabledInSettings: return "Watchdog disabled in settings (132)";
case VRInitError_Init_VRDashboardNotFound: return "VR Dashboard Not Found (133)";
case VRInitError_Init_VRDashboardStartupFailed: return "VR Dashboard startup failed (134)";
case VRInitError_Init_VRHomeNotFound: return "VR Home Not Found (135)";
case VRInitError_Init_VRHomeStartupFailed: return "VR home startup failed (136)";
case VRInitError_Init_RebootingBusy: return "Rebooting In Progress (137)";
case VRInitError_Init_FirmwareUpdateBusy: return "Firmware Update In Progress (138)";
case VRInitError_Init_FirmwareRecoveryBusy: return "Firmware Recovery In Progress (139)";
case VRInitError_Init_USBServiceBusy: return "USB Service Busy (140)";
case VRInitError_Init_VRDashboardServicePending: return "VR Dashboard startup failed, vrservice was pending for too long (160)";
case VRInitError_Init_VRDashboardServiceTimeout: return "VR Dashboard startup failed, attempt to communicate with vrservice timed out (161)";
case VRInitError_Init_VRDashboardServiceStopped: return "VR Dashboard startup failed, vrservice was stopped (162)";
case VRInitError_Init_VRDashboardAlreadyStarted: return "VR Dashboard startup failed, vrdashboard was already running (163)";
case VRInitError_Init_VRDashboardCopyFailed: return "VR Dashboard startup failed, required files did not copy correctly (164)";
case VRInitError_Init_VRDashboardTokenFailure: return "VR Dashboard startup failed, unable to create appropriate token (165)";
case VRInitError_Init_VRDashboardEnvironmentFailure: return "VR Dashboard startup failed, unable to create appropriate environment (166)";
case VRInitError_Init_VRDashboardPathFailure: return "VR Dashboard startup failed, path error (167)";
case VRInitError_Driver_Failed: return "Driver Failed (200)";
case VRInitError_Driver_Unknown: return "Driver Not Known (201)";
case VRInitError_Driver_HmdUnknown: return "HMD Not Known (202)";
case VRInitError_Driver_NotLoaded: return "Driver Not Loaded (203)";
case VRInitError_Driver_RuntimeOutOfDate: return "Driver runtime is out of date (204)";
case VRInitError_Driver_HmdInUse: return "HMD already in use by another application (205)";
case VRInitError_Driver_NotCalibrated: return "Device is not calibrated (206)";
case VRInitError_Driver_CalibrationInvalid: return "Device Calibration is invalid (207)";
case VRInitError_Driver_HmdDisplayNotFound: return "HMD detected over USB, but Monitor not found (208)";
case VRInitError_Driver_TrackedDeviceInterfaceUnknown: return "Driver Tracked Device Interface unknown (209)";
// case VRInitError_Driver_HmdDisplayNotFoundAfterFix: return "HMD detected over USB, but Monitor not found after attempt to fix (210)"; // taken out upon Ben's request: He thinks that there is no need to separate that error from 208
case VRInitError_Driver_HmdDriverIdOutOfBounds: return "Hmd DriverId is our of bounds (211)";
case VRInitError_Driver_HmdDisplayMirrored: return "HMD detected over USB, but Monitor may be mirrored instead of extended (212)";
case VRInitError_Driver_HmdDisplayNotFoundLaptop: return "On laptop, HMD detected over USB, but Monitor not found (213)";
case VRInitError_Driver_PeerDriverNotInstalled: return "The current HMD requires an additional driver that is not installed (214)";
case VRInitError_Driver_WirelessHmdNotConnected: return "A wireless HMD driver is present, but the wireless HMD has not connected yet (215)";
case VRInitError_IPC_ServerInitFailed: return "VR Server Init Failed (300)";
case VRInitError_IPC_ConnectFailed: return "Connect to VR Server Failed (301)";
case VRInitError_IPC_SharedStateInitFailed: return "Shared IPC State Init Failed (302)";
case VRInitError_IPC_CompositorInitFailed: return "Shared IPC Compositor Init Failed (303)";
case VRInitError_IPC_MutexInitFailed: return "Shared IPC Mutex Init Failed (304)";
case VRInitError_IPC_Failed: return "Shared IPC Failed (305)";
case VRInitError_IPC_CompositorConnectFailed: return "Shared IPC Compositor Connect Failed (306)";
case VRInitError_IPC_CompositorInvalidConnectResponse: return "Shared IPC Compositor Invalid Connect Response (307)";
case VRInitError_IPC_ConnectFailedAfterMultipleAttempts: return "Shared IPC Connect Failed After Multiple Attempts (308)";
case VRInitError_IPC_ConnectFailedAfterTargetExited: return "Shared IPC Connect Failed After Target Exited (309)";
case VRInitError_IPC_NamespaceUnavailable: return "Shared IPC Namespace Unavailable (310)";
case VRInitError_Compositor_Failed: return "Compositor failed to initialize (400)";
case VRInitError_Compositor_D3D11HardwareRequired: return "Compositor failed to find DX11 hardware (401)";
case VRInitError_Compositor_FirmwareRequiresUpdate: return "Compositor requires mandatory firmware update (402)";
case VRInitError_Compositor_OverlayInitFailed: return "Compositor initialization succeeded, but overlay init failed (403)";
case VRInitError_Compositor_ScreenshotsInitFailed: return "Compositor initialization succeeded, but screenshot init failed (404)";
case VRInitError_Compositor_UnableToCreateDevice: return "Compositor unable to create graphics device (405)";
case VRInitError_Compositor_FailedToInitializeEncoder: return "Driver unable to initialize video encoder (499)";
// Oculus
case VRInitError_VendorSpecific_UnableToConnectToOculusRuntime: return "Unable to connect to Oculus Runtime (1000)";
case VRInitError_VendorSpecific_OculusRuntimeBadInstall: return "Unable to connect to Oculus Runtime, possible bad install (1114)";
// Lighthouse
case VRInitError_VendorSpecific_HmdFound_CantOpenDevice: return "HMD found, but can not open device (1101)";
case VRInitError_VendorSpecific_HmdFound_UnableToRequestConfigStart: return "HMD found, but unable to request config (1102)";
case VRInitError_VendorSpecific_HmdFound_NoStoredConfig: return "HMD found, but no stored config (1103)";
case VRInitError_VendorSpecific_HmdFound_ConfigFailedSanityCheck: return "HMD found, but failed configuration check (1113)";
case VRInitError_VendorSpecific_HmdFound_ConfigTooBig: return "HMD found, but config too big (1104)";
case VRInitError_VendorSpecific_HmdFound_ConfigTooSmall: return "HMD found, but config too small (1105)";
case VRInitError_VendorSpecific_HmdFound_UnableToInitZLib: return "HMD found, but unable to init ZLib (1106)";
case VRInitError_VendorSpecific_HmdFound_CantReadFirmwareVersion: return "HMD found, but problems with the data (1107)";
case VRInitError_VendorSpecific_HmdFound_UnableToSendUserDataStart: return "HMD found, but problems with the data (1108)";
case VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataStart: return "HMD found, but problems with the data (1109)";
case VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataNext: return "HMD found, but problems with the data (1110)";
case VRInitError_VendorSpecific_HmdFound_UserDataAddressRange: return "HMD found, but problems with the data (1111)";
case VRInitError_VendorSpecific_HmdFound_UserDataError: return "HMD found, but problems with the data (1112)";
case VRInitError_VendorSpecific_HmdFound_UnexpectedConfiguration_1: return "HMD found, but problems with the data (1115)";
case VRInitError_Steam_SteamInstallationNotFound: return "Unable to find Steam installation (2000)";
default:
return GetIDForVRInitError( eError );
}
}
const char *GetIDForVRInitError( vr::EVRInitError eError )
{
switch( eError )
{
RETURN_ENUM_AS_STRING( VRInitError_None );
RETURN_ENUM_AS_STRING( VRInitError_Unknown );
RETURN_ENUM_AS_STRING( VRInitError_Init_InstallationNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_InstallationCorrupt );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRClientDLLNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_FileNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_FactoryNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_InterfaceNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_InvalidInterface );
RETURN_ENUM_AS_STRING( VRInitError_Init_UserConfigDirectoryInvalid );
RETURN_ENUM_AS_STRING( VRInitError_Init_HmdNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_NotInitialized );
RETURN_ENUM_AS_STRING( VRInitError_Init_PathRegistryNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_NoConfigPath );
RETURN_ENUM_AS_STRING( VRInitError_Init_NoLogPath );
RETURN_ENUM_AS_STRING( VRInitError_Init_PathRegistryNotWritable );
RETURN_ENUM_AS_STRING( VRInitError_Init_AppInfoInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_Retry );
RETURN_ENUM_AS_STRING( VRInitError_Init_InitCanceledByUser );
RETURN_ENUM_AS_STRING( VRInitError_Init_AnotherAppLaunching );
RETURN_ENUM_AS_STRING( VRInitError_Init_SettingsInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_ShuttingDown );
RETURN_ENUM_AS_STRING( VRInitError_Init_TooManyObjects );
RETURN_ENUM_AS_STRING( VRInitError_Init_NoServerForBackgroundApp );
RETURN_ENUM_AS_STRING( VRInitError_Init_NotSupportedWithCompositor );
RETURN_ENUM_AS_STRING( VRInitError_Init_NotAvailableToUtilityApps );
RETURN_ENUM_AS_STRING( VRInitError_Init_Internal );
RETURN_ENUM_AS_STRING( VRInitError_Init_HmdDriverIdIsNone );
RETURN_ENUM_AS_STRING( VRInitError_Init_HmdNotFoundPresenceFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRMonitorNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRMonitorStartupFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_LowPowerWatchdogNotSupported );
RETURN_ENUM_AS_STRING( VRInitError_Init_InvalidApplicationType );
RETURN_ENUM_AS_STRING( VRInitError_Init_NotAvailableToWatchdogApps );
RETURN_ENUM_AS_STRING( VRInitError_Init_WatchdogDisabledInSettings );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardStartupFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRHomeNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRHomeStartupFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_RebootingBusy );
RETURN_ENUM_AS_STRING( VRInitError_Init_FirmwareUpdateBusy );
RETURN_ENUM_AS_STRING( VRInitError_Init_FirmwareRecoveryBusy );
RETURN_ENUM_AS_STRING( VRInitError_Init_USBServiceBusy );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRWebHelperStartupFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_TrackerManagerInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_AlreadyRunning );
RETURN_ENUM_AS_STRING( VRInitError_Init_FailedForVrMonitor);
RETURN_ENUM_AS_STRING( VRInitError_Init_PropertyManagerInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_WebServerFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_IllegalTypeTransition );
RETURN_ENUM_AS_STRING( VRInitError_Init_MismatchedRuntimes );
RETURN_ENUM_AS_STRING( VRInitError_Init_InvalidProcessId );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRServiceStartupFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_PrismNeedsNewDrivers );
RETURN_ENUM_AS_STRING( VRInitError_Init_PrismStartupTimedOut );
RETURN_ENUM_AS_STRING( VRInitError_Init_CouldNotStartPrism );
RETURN_ENUM_AS_STRING( VRInitError_Init_PrismClientInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_PrismClientStartFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_PrismExitedUnexpectedly );
RETURN_ENUM_AS_STRING( VRInitError_Init_BadLuid );
RETURN_ENUM_AS_STRING( VRInitError_Init_NoServerForAppContainer );
RETURN_ENUM_AS_STRING( VRInitError_Init_DuplicateBootstrapper );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardServicePending );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardServiceTimeout );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardServiceStopped );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardAlreadyStarted );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardCopyFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardTokenFailure );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardEnvironmentFailure );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardPathFailure );
RETURN_ENUM_AS_STRING( VRInitError_Init_InstallationTooOld );
RETURN_ENUM_AS_STRING( VRInitError_Init_ClientVersionAlreadyProvided );
RETURN_ENUM_AS_STRING( VRInitError_Driver_Failed );
RETURN_ENUM_AS_STRING( VRInitError_Driver_Unknown );
RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdUnknown);
RETURN_ENUM_AS_STRING( VRInitError_Driver_NotLoaded);
RETURN_ENUM_AS_STRING( VRInitError_Driver_RuntimeOutOfDate);
RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdInUse);
RETURN_ENUM_AS_STRING( VRInitError_Driver_NotCalibrated);
RETURN_ENUM_AS_STRING( VRInitError_Driver_CalibrationInvalid);
RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayNotFound);
RETURN_ENUM_AS_STRING( VRInitError_Driver_TrackedDeviceInterfaceUnknown );
// RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayNotFoundAfterFix );
RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDriverIdOutOfBounds );
RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayMirrored );
RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayNotFoundLaptop );
RETURN_ENUM_AS_STRING( VRInitError_Driver_PeerDriverNotInstalled );
RETURN_ENUM_AS_STRING( VRInitError_Driver_WirelessHmdNotConnected );
RETURN_ENUM_AS_STRING( VRInitError_IPC_ServerInitFailed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_ConnectFailed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_SharedStateInitFailed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_CompositorInitFailed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_MutexInitFailed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_Failed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_CompositorConnectFailed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_CompositorInvalidConnectResponse);
RETURN_ENUM_AS_STRING( VRInitError_IPC_ConnectFailedAfterMultipleAttempts );
RETURN_ENUM_AS_STRING( VRInitError_IPC_ConnectFailedAfterTargetExited );
RETURN_ENUM_AS_STRING( VRInitError_IPC_NamespaceUnavailable );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_Failed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_D3D11HardwareRequired );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FirmwareRequiresUpdate );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_OverlayInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_ScreenshotsInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_UnableToCreateDevice );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_SharedStateIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_NotificationManagerIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_ResourceManagerClientIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_MessageOverlaySharedStateInitFailure );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_PropertiesInterfaceIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFullscreenWindowFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_SettingsInterfaceIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToShowWindow );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_DistortInterfaceIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_DisplayFrequencyFailure );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_RendererInitializationFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_DXGIFactoryInterfaceIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_DXGIFactoryCreateFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_DXGIFactoryQueryFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidAdapterDesktop );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidHmdAttachment );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidOutputDesktop );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidDeviceProvided );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_D3D11RendererInitializationFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToFindDisplayMode );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateSwapChain );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToGetBackBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateRenderTarget );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateDXGI2SwapChain );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedtoGetDXGI2BackBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateDXGI2RenderTarget );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToGetDXGIDeviceInterface );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_SelectDisplayMode );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateNvAPIRenderTargets );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_NvAPISetDisplayMode );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateDirectModeDisplay );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidHmdPropertyContainer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_UpdateDisplayFrequency );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateRasterizerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateWireframeRasterizerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateSamplerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateClampToBorderSamplerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateAnisoSamplerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlaySamplerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreatePanoramaSamplerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFontSamplerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateNoBlendState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateAlphaBlendState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendStateMaskR );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendStateMaskG );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendStateMaskB );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDepthStencilState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDepthStencilStateNoWrite );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDepthStencilStateNoDepth );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFlushTexture );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDistortionSurfaces );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateHmdPoseConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateHmdPoseStagingConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateSharedFrameInfoConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateSceneTextureIndexConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateReadableSceneTextureIndexConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLayerGraphicsTextureIndexConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLayerComputeTextureIndexConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLayerComputeSceneTextureIndexConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateComputeHmdPoseConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateGeomConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreatePanelMaskConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreatePixelSimUBO );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateMSAARenderTextures );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateResolveRenderTextures );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateComputeResolveRenderTextures );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDriverDirectModeResolveTextures );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_OpenDriverDirectModeResolveTextures );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFallbackSyncTexture );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_ShareFallbackSyncTexture );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayIndexBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayVertexBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateTextVertexBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateTextIndexBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateMirrorTextures );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLastFrameRenderTexture );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateMirrorOverlay );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateVirtualDisplayBackbuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_DisplayModeNotSupported );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayInvalidCall );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayAlreadyInitialized );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateMailbox );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_WindowInterfaceIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_SystemLayerCreateInstance );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_SystemLayerCreateSession );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateInverseDistortUVs );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBackbufferDepth );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CannotDRMLeaseDisplay );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CannotConnectToDisplayServer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_GnomeNoDRMLeasing );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToInitializeEncoder );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlurTexture );
// Vendor-specific errors
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_UnableToConnectToOculusRuntime);
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_WindowsNotInDevMode );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_OculusLinkNotEnabled );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_OculusRuntimeBadInstall );
// Lighthouse
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_CantOpenDevice);
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToRequestConfigStart);
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_NoStoredConfig);
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_ConfigFailedSanityCheck );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_ConfigTooBig );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_ConfigTooSmall );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToInitZLib );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_CantReadFirmwareVersion );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToSendUserDataStart );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataStart );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataNext );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UserDataAddressRange );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UserDataError );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnexpectedConfiguration_1 );
// VRLink
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_VRLink_OutdatedDriverMESA );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_VRLink_OutdatedDriverNVIDIA );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_VRLink_NoVideoSupport );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_Oasis_UnlockRequired );
RETURN_ENUM_AS_STRING( VRInitError_Steam_SteamInstallationNotFound );
default:
{
static char buf[128];
sprintf( buf, "Unknown error (%d)", eError );
return buf;
}
}
}
+6
View File
@@ -0,0 +1,6 @@
//========= Copyright Valve Corporation ============//
#pragma once
const char *GetEnglishStringForHmdError( vr::EVRInitError eError );
const char *GetIDForVRInitError( vr::EVRInitError eError );
File diff suppressed because it is too large. Load diff
+160
View File
@@ -0,0 +1,160 @@
//========= Copyright Valve Corporation ============//
#pragma once
#include <string>
#include <vector>
#include <stdint.h>
/** Returns the path (including filename) to the current executable */
std::string Path_GetExecutablePath();
/** Returns the filename (excluding directory and extension) of the current executable */
std::string Path_GetExecutableName();
/** Returns the path of the current working directory */
std::string Path_GetWorkingDirectory();
/** Sets the path of the current working directory. Returns true if this was successful. */
bool Path_SetWorkingDirectory( const std::string & sPath );
/** Gets the path to a temporary directory. */
std::string Path_GetTemporaryDirectory();
/** returns the path (including filename) of the current shared lib or DLL */
std::string Path_GetThisModulePath();
/** Returns the specified path without its filename.
* If slash is unspecified the native path separator of the current platform
* will be used. */
std::string Path_StripFilename( const std::string & sPath, char slash = 0 );
/** returns just the filename from the provided full or relative path. */
std::string Path_StripDirectory( const std::string & sPath, char slash = 0 );
/** returns just the filename with no extension of the provided filename.
* If there is a path the path is left intact. */
std::string Path_StripExtension( const std::string & sPath );
/** returns just extension of the provided filename (if any). */
std::string Path_GetExtension( const std::string & sPath );
/** Returns true if the path is absolute */
bool Path_IsAbsolute( const std::string & sPath );
/** Makes an absolute path from a relative path and a base path */
std::string Path_MakeAbsolute( const std::string & sRelativePath, const std::string & sBasePath );
/** Fixes the directory separators for the current platform.
* If slash is unspecified the native path separator of the current platform
* will be used. */
std::string Path_FixSlashes( const std::string & sPath, char slash = 0 );
/** Returns the path separator for the current platform */
char Path_GetSlash();
/** Jams two paths together with the right kind of slash */
std::string Path_Join( const std::string & first, const std::string & second, char slash = 0 );
std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, char slash = 0 );
std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, const std::string &fourth, char slash = 0 );
std::string Path_Join(
const std::string & first,
const std::string & second,
const std::string & third,
const std::string & fourth,
const std::string & fifth,
char slash = 0 );
/** Removes redundant <dir>/.. elements in the path. Returns an empty path if the
* specified path has a broken number of directories for its number of ..s.
* If slash is unspecified the native path separator of the current platform
* will be used. */
std::string Path_Compact( const std::string & sRawPath, char slash = 0 );
/** Returns true if these two paths are the same without respect for internal . or ..
* sequences, slash type, or case (on case-insensitive platforms). */
bool Path_IsSamePath( const std::string & sPath1, const std::string & sPath2 );
//** Removed trailing slashes */
std::string Path_RemoveTrailingSlash( const std::string & sRawPath, char slash = 0 );
/** returns true if the specified path exists and is a directory */
bool Path_IsDirectory( const std::string & sPath );
/** returns true if the specified path represents an app bundle */
bool Path_IsAppBundle( const std::string & sPath );
/** returns true if the the path exists */
bool Path_Exists( const std::string & sPath );
/** Helper functions to find parent directories or subdirectories of parent directories */
std::string Path_FindParentDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName );
std::string Path_FindParentSubDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName );
/** Make a text file writable. */
bool Path_MakeWritable( const std::string &strFilename );
/** Path operations to read or write text/binary files */
unsigned char * Path_ReadBinaryFile( const std::string &strFilename, int *pSize );
uint32_t Path_ReadBinaryFile( const std::string &strFilename, unsigned char *pBuffer, uint32_t unSize );
std::vector<uint8_t> Path_ReadBinaryFile( const std::string & strFilename );
bool Path_WriteBinaryFile( const std::string &strFilename, unsigned char *pData, unsigned nSize );
std::string Path_ReadTextFile( const std::string &strFilename );
bool Path_WriteStringToTextFile( const std::string &strFilename, const char *pchData );
bool Path_WriteStringToTextFileAtomic( const std::string &strFilename, const char *pchData );
/** Returns a file:// url for paths, or an http or https url if that's what was provided */
std::string Path_FilePathToUrl( const std::string & sRelativePath, const std::string & sBasePath );
/** Strips off file:// off a URL and returns the path. For other kinds of URLs an empty string is returned */
std::string Path_UrlToFilePath( const std::string & sFileUrl );
/** Returns the root of the directory the system wants us to store user documents in */
std::string GetUserDocumentsPath();
/** deletes / unlinks a single file */
bool Path_UnlinkFile( const std::string &strFilename );
std::string Path_SanitizeFilename( const std::string& sFilename );
bool Path_DeleteDirectory( const std::string& sDirectoryPath, bool bDeleteSubdirectories = false );
#ifndef MAX_UNICODE_PATH
#define MAX_UNICODE_PATH 32767
#endif
#ifndef MAX_UNICODE_PATH_IN_UTF8
#define MAX_UNICODE_PATH_IN_UTF8 (MAX_UNICODE_PATH * 4)
#endif
//-----------------------------------------------------------------------------
#if defined(WIN32)
#define DYNAMIC_LIB_EXT ".dll"
#define PROGRAM_EXT ".exe"
#ifdef _WIN64
#define PLATSUBDIR "win64"
#else
#define PLATSUBDIR "win32"
#endif
#elif defined(OSX)
#define DYNAMIC_LIB_EXT ".dylib"
#define PLATSUBDIR "osx32"
#define PROGRAM_EXT ""
#elif defined(LINUX)
#define DYNAMIC_LIB_EXT ".so"
#define PROGRAM_EXT ""
#if defined( LINUX32 )
#define PLATSUBDIR "linux32"
#elif defined( ANDROIDARM32 )
#define PLATSUBDIR "androidarm32"
#elif defined( ANDROIDARM64 )
#define PLATSUBDIR "androidarm64"
#elif defined( LINUXARM64 )
#define PLATSUBDIR "linuxarm64"
#else
#define PLATSUBDIR "linux64"
#endif
#else
#warning "Unknown platform for PLATSUBDIR"
#define PLATSUBDIR "unknown_platform"
#endif
+65
View File
@@ -0,0 +1,65 @@
//========= Copyright Valve Corporation ============//
#include <vrcore/sharedlibtools_public.h>
#include "vrcore/strtools_public.h"
#include <string.h>
#if defined(_WIN32)
#include <windows.h>
#endif
#if defined(POSIX)
#include <dlfcn.h>
#endif
SharedLibHandle SharedLib_Load( const char *pchPath, std::string *pErrStr )
{
SharedLibHandle pHandle = nullptr;
#if defined( _WIN32)
std::wstring wsFixedPath = UTF8to16( pchPath );
pHandle = ( SharedLibHandle )LoadLibraryExW( wsFixedPath.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
#elif defined(POSIX)
pHandle = (SharedLibHandle) dlopen( pchPath, RTLD_LOCAL|RTLD_NOW );
#endif
if ( pHandle == nullptr && pErrStr )
{
#if defined( _WIN32)
// TODO: Consider using FormatMessage to get an error string for this error code
*pErrStr = std::to_string( GetLastError() );
#elif defined(POSIX)
char * pErr = dlerror();
if ( pErr )
{
*pErrStr = std::string ( pErr );
}
#endif
}
return pHandle;
}
void *SharedLib_GetFunction( SharedLibHandle lib, const char *pchFunctionName)
{
#if defined( _WIN32)
return (void*)GetProcAddress( (HMODULE)lib, pchFunctionName );
#elif defined(POSIX)
return dlsym( lib, pchFunctionName );
#endif
}
void SharedLib_Unload( SharedLibHandle lib )
{
if ( !lib )
return;
#if defined( _WIN32)
FreeLibrary( (HMODULE)lib );
#elif defined(POSIX)
dlclose( lib );
#endif
}
+13
View File
@@ -0,0 +1,13 @@
//========= Copyright Valve Corporation ============//
#pragma once
#include <stdint.h>
#include <string>
typedef void *SharedLibHandle;
SharedLibHandle SharedLib_Load( const char *pchPath, std::string *pErrStr = nullptr );
void *SharedLib_GetFunction( SharedLibHandle lib, const char *pchFunctionName);
void SharedLib_Unload( SharedLibHandle lib );
+684
View File
@@ -0,0 +1,684 @@
//========= Copyright Valve Corporation ============//
#include <vrcore/strtools_public.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <codecvt>
#include <iostream>
#include <functional>
#include <locale>
#include <codecvt>
#include <cstdarg>
#if !defined( VRCORE_NO_PLATFORM )
#include <vrcore/assert.h>
#else
#define AssertMsg( cond, ... )
#endif
#if defined( _WIN32 )
#include <windows.h>
#endif
#if defined( OSX ) || defined( LINUX )
//-----------------------------------------------------------------------------
// Purpose: stricmp -> strcasecmp bridge
//-----------------------------------------------------------------------------
int stricmp( const char *pStr1, const char *pStr2 )
{
return strcasecmp( pStr1, pStr2 );
}
//-----------------------------------------------------------------------------
// Purpose: strincmp -> strncasecmp bridge
//-----------------------------------------------------------------------------
int strnicmp( const char *pStr1, const char *pStr2, size_t unBufferLen )
{
return strncasecmp( pStr1, pStr2, unBufferLen );
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool StringHasPrefix( const std::string & sString, const std::string & sPrefix )
{
return 0 == strnicmp( sString.c_str(), sPrefix.c_str(), sPrefix.length() );
}
bool StringHasPrefixCaseSensitive( const std::string & sString, const std::string & sPrefix )
{
return 0 == strncmp( sString.c_str(), sPrefix.c_str(), sPrefix.length() );
}
bool StringHasSuffix( const std::string &sString, const std::string &sSuffix )
{
size_t cStrLen = sString.length();
size_t cSuffixLen = sSuffix.length();
if ( cSuffixLen > cStrLen )
return false;
std::string sStringSuffix = sString.substr( cStrLen - cSuffixLen, cSuffixLen );
return 0 == stricmp( sStringSuffix.c_str(), sSuffix.c_str() );
}
bool StringHasSuffixCaseSensitive( const std::string &sString, const std::string &sSuffix )
{
size_t cStrLen = sString.length();
size_t cSuffixLen = sSuffix.length();
if ( cSuffixLen > cStrLen )
return false;
std::string sStringSuffix = sString.substr( cStrLen - cSuffixLen, cSuffixLen );
return 0 == strncmp( sStringSuffix.c_str(), sSuffix.c_str(),cSuffixLen );
}
std::string StringReplace( const std::string &sString, const std::string &sFind, const std::string &sReplace )
{
size_t cStartPos = 0;
size_t cFindLen = sFind.length();
size_t cReplaceLen = sReplace.length();
// Nothing to find, return unmodified.
if ( cFindLen == 0 )
return sString;
std::string sResult = sString;
while ( ( cStartPos = sResult.find( sFind, cStartPos ) ) != std::string::npos )
{
sResult.replace( cStartPos, cFindLen, sReplace );
cStartPos += cReplaceLen;
}
return sResult;
}
//-----------------------------------------------------------------------------
// Purpose: Converts a UTF-16 formatted string to a UTF-8 formatted string
//-----------------------------------------------------------------------------
std::string UTF16to8( const std::wstring &in )
{
static std::wstring_convert< std::codecvt_utf8_utf16< wchar_t >, wchar_t > s_convert; // construction of this can be expensive (or even serialized) depending on locale
try
{
return s_convert.to_bytes( in );
}
catch ( ... )
{
return std::string();
}
}
std::string UTF16to8( const wchar_t * in )
{
if (in == nullptr)
{
return std::string();
}
std::wstring wstr( in );
return UTF16to8( wstr );
}
//-----------------------------------------------------------------------------
// Purpose: Converts a UTF-8 formatted string to a UTF-16 formatted string
//-----------------------------------------------------------------------------
std::wstring UTF8to16( const std::string &in )
{
static std::wstring_convert< std::codecvt_utf8_utf16< wchar_t >, wchar_t > s_convert; // construction of this can be expensive (or even serialized) depending on locale
try
{
return s_convert.from_bytes( in );
}
catch ( ... )
{
return std::wstring();
}
}
std::wstring UTF8to16( const char * in )
{
if ( in == nullptr )
{
return std::wstring();
}
std::string str( in );
return UTF8to16( str );
}
//-----------------------------------------------------------------------------
// Purpose: Format string to std::string converter
//-----------------------------------------------------------------------------
std::string Format( const char *pchFormat, ... )
{
static constexpr size_t k_ulMaxStackString = 4096;
va_list args;
char pchBuffer[k_ulMaxStackString];
va_start( args, pchFormat );
int unSize = vsnprintf( pchBuffer, sizeof( pchBuffer ), pchFormat, args );
va_end( args );
// Something went fairly wrong
if ( unSize < 0 )
{
AssertMsg( false, "Format string parse failure" );
return "";
}
// Processing on the stack worked, success
if ( unSize < k_ulMaxStackString )
{
return pchBuffer;
}
// If processing on the stack failed, fallback to a dynamic allocation
std::vector< char > vecChar{};
vecChar.resize( unSize + 1 );
va_start( args, pchFormat );
unSize = vsnprintf( vecChar.data(), vecChar.size(), pchFormat, args );
va_end( args );
// Double check, just in case
if ( unSize < 0 )
{
AssertMsg( false, "Format string parse failure" );
return "";
}
return vecChar.data();
}
#if defined( _WIN32 )
//-----------------------------------------------------------------------------
// Purpose: Convert LPSTR in the default CodePage to UTF8
//-----------------------------------------------------------------------------
std::string DefaultACPtoUTF8( const char *pszStr )
{
if ( GetACP() == CP_UTF8 )
{
return pszStr;
}
else
{
std::vector<wchar_t> vecBuf( strlen( pszStr ) + 1 ); // should be guaranteed to be enough
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pszStr, -1, vecBuf.data(), (int) vecBuf.size() );
return UTF16to8( vecBuf.data() );
}
}
#endif
// --------------------------------------------------------------------
// Purpose:
// --------------------------------------------------------------------
void strcpy_safe( char *pchBuffer, size_t unBufferSizeBytes, const char *pchSource )
{
strncpy( pchBuffer, pchSource, unBufferSizeBytes - 1 );
pchBuffer[unBufferSizeBytes - 1] = '\0';
}
// --------------------------------------------------------------------
// Purpose: converts a string to upper case
// --------------------------------------------------------------------
std::string StringToUpper( const std::string & sString )
{
std::string sOut;
sOut.reserve( sString.size() + 1 );
for( std::string::const_iterator i = sString.begin(); i != sString.end(); i++ )
{
sOut.push_back( (char)toupper( *i ) );
}
return sOut;
}
// --------------------------------------------------------------------
// Purpose: converts a string to lower case
// --------------------------------------------------------------------
std::string StringToLower( const std::string & sString )
{
std::string sOut;
sOut.reserve( sString.size() + 1 );
for( std::string::const_iterator i = sString.begin(); i != sString.end(); i++ )
{
sOut.push_back( (char)tolower( *i ) );
}
return sOut;
}
uint32_t ReturnStdString( const std::string & sValue, char *pchBuffer, uint32_t unBufferLen )
{
uint32_t unLen = (uint32_t)sValue.length() + 1;
if( !pchBuffer || !unBufferLen )
return unLen;
if( unBufferLen < unLen )
{
pchBuffer[0] = '\0';
}
else
{
memcpy( pchBuffer, sValue.c_str(), unLen );
}
return unLen;
}
/** Returns a std::string from a uint64_t */
std::string Uint64ToString( uint64_t ulValue )
{
char buf[ 22 ];
#if defined( _WIN32 )
sprintf_s( buf, "%llu", ulValue );
#else
snprintf( buf, sizeof( buf ), "%llu", (long long unsigned int ) ulValue );
#endif
return buf;
}
/** returns a uint64_t from a string */
uint64_t StringToUint64( const std::string & sValue )
{
return strtoull( sValue.c_str(), NULL, 0 );
}
//-----------------------------------------------------------------------------
// Purpose: Helper for converting a numeric value to a hex digit, value should be 0-15.
//-----------------------------------------------------------------------------
char cIntToHexDigit( int nValue )
{
//Assert( nValue >= 0 && nValue <= 15 );
return "0123456789ABCDEF"[ nValue & 15 ];
}
//-----------------------------------------------------------------------------
// Purpose: Helper for converting a hex char value to numeric, return -1 if the char
// is not a valid hex digit.
//-----------------------------------------------------------------------------
int iHexCharToInt( char cValue )
{
int32_t iValue = cValue;
if ( (uint32_t)( iValue - '0' ) < 10 )
return iValue - '0';
iValue |= 0x20;
if ( (uint32_t)( iValue - 'a' ) < 6 )
return iValue - 'a' + 10;
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: These define the set of characters to filter for components (which
// need all the escaping we can muster) vs. paths (which don't want
// / and : escaped so we don't break less compliant URL handling code.
//-----------------------------------------------------------------------------
static bool CharNeedsEscape_Component( const char c )
{
return (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9')
&& c != '-' && c != '_' && c != '.');
}
static bool CharNeedsEscape_FullPath( const char c )
{
return (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9')
&& c != '-' && c != '_' && c != '.' && c != '/' && c != ':' );
}
//-----------------------------------------------------------------------------
// Purpose: Internal implementation of encode, works in the strict RFC manner, or
// with spaces turned to + like HTML form encoding.
//-----------------------------------------------------------------------------
void V_URLEncodeInternal( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen,
bool bUsePlusForSpace, std::function< bool(const char)> fnNeedsEscape )
{
//AssertMsg( nDestLen > 3*nSourceLen, "Target buffer for V_URLEncode should be 3x source length, plus one for terminating null\n" );
int iDestPos = 0;
for ( int i=0; i < nSourceLen; ++i )
{
// worst case we need 3 additional chars
if( (iDestPos+3) > nDestLen )
{
pchDest[0] = '\0';
// AssertMsg( false, "Target buffer too short\n" );
return;
}
// We allow only a-z, A-Z, 0-9, period, underscore, and hyphen to pass through unescaped.
// These are the characters allowed by both the original RFC 1738 and the latest RFC 3986.
// Current specs also allow '~', but that is forbidden under original RFC 1738.
if ( fnNeedsEscape( pchSource[i] ) )
{
if ( bUsePlusForSpace && pchSource[i] == ' ' )
{
pchDest[iDestPos++] = '+';
}
else
{
pchDest[iDestPos++] = '%';
uint8_t iValue = pchSource[i];
if ( iValue == 0 )
{
pchDest[iDestPos++] = '0';
pchDest[iDestPos++] = '0';
}
else
{
char cHexDigit1 = cIntToHexDigit( iValue % 16 );
iValue /= 16;
char cHexDigit2 = cIntToHexDigit( iValue );
pchDest[iDestPos++] = cHexDigit2;
pchDest[iDestPos++] = cHexDigit1;
}
}
}
else
{
pchDest[iDestPos++] = pchSource[i];
}
}
if( (iDestPos+1) > nDestLen )
{
pchDest[0] = '\0';
//AssertMsg( false, "Target buffer too short to terminate\n" );
return;
}
// Null terminate
pchDest[iDestPos++] = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Internal implementation of decode, works in the strict RFC manner, or
// with spaces turned to + like HTML form encoding.
//
// Returns the amount of space used in the output buffer.
//-----------------------------------------------------------------------------
size_t V_URLDecodeInternal( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen, bool bUsePlusForSpace )
{
if ( nDecodeDestLen < nEncodedSourceLen )
{
//AssertMsg( false, "V_URLDecode needs a dest buffer at least as large as the source" );
return 0;
}
int iDestPos = 0;
for( int i=0; i < nEncodedSourceLen; ++i )
{
if ( bUsePlusForSpace && pchEncodedSource[i] == '+' )
{
pchDecodeDest[ iDestPos++ ] = ' ';
}
else if ( pchEncodedSource[i] == '%' )
{
// Percent signifies an encoded value, look ahead for the hex code, convert to numeric, and use that
// First make sure we have 2 more chars
if ( i < nEncodedSourceLen - 2 )
{
char cHexDigit1 = pchEncodedSource[i+1];
char cHexDigit2 = pchEncodedSource[i+2];
// Turn the chars into a hex value, if they are not valid, then we'll
// just place the % and the following two chars direct into the string,
// even though this really shouldn't happen, who knows what bad clients
// may do with encoding.
bool bValid = false;
int iValue = iHexCharToInt( cHexDigit1 );
if ( iValue != -1 )
{
iValue *= 16;
int iValue2 = iHexCharToInt( cHexDigit2 );
if ( iValue2 != -1 )
{
iValue += iValue2;
pchDecodeDest[ iDestPos++ ] = (char)iValue;
bValid = true;
}
}
if ( !bValid )
{
pchDecodeDest[ iDestPos++ ] = '%';
pchDecodeDest[ iDestPos++ ] = cHexDigit1;
pchDecodeDest[ iDestPos++ ] = cHexDigit2;
}
}
// Skip ahead
i += 2;
}
else
{
pchDecodeDest[ iDestPos++ ] = pchEncodedSource[i];
}
}
// We may not have extra room to NULL terminate, since this can be used on raw data, but if we do
// go ahead and do it as this can avoid bugs.
if ( iDestPos < nDecodeDestLen )
{
pchDecodeDest[iDestPos] = 0;
}
return (size_t)iDestPos;
}
//-----------------------------------------------------------------------------
// Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
// This version of the call isn't a strict RFC implementation, but uses + for space as is
// the standard in HTML form encoding, despite it not being part of the RFC.
//
// Dest buffer should be at least as large as source buffer to guarantee room for decode.
//-----------------------------------------------------------------------------
void V_URLEncode( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
{
return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, true, CharNeedsEscape_Component );
}
void V_URLEncodeNoPlusForSpace( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
{
return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, false, CharNeedsEscape_Component );
}
void V_URLEncodeFullPath( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
{
return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, false, CharNeedsEscape_FullPath );
}
//-----------------------------------------------------------------------------
// Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
// This version of the call isn't a strict RFC implementation, but uses + for space as is
// the standard in HTML form encoding, despite it not being part of the RFC.
//
// Dest buffer should be at least as large as source buffer to guarantee room for decode.
// Dest buffer being the same as the source buffer (decode in-place) is explicitly allowed.
//-----------------------------------------------------------------------------
size_t V_URLDecode( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen )
{
return V_URLDecodeInternal( pchDecodeDest, nDecodeDestLen, pchEncodedSource, nEncodedSourceLen, true );
}
size_t V_URLDecodeNoPlusForSpace( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen )
{
return V_URLDecodeInternal( pchDecodeDest, nDecodeDestLen, pchEncodedSource, nEncodedSourceLen, false );
}
//-----------------------------------------------------------------------------
void V_StripExtension( std::string &in )
{
// Find the last dot. If it's followed by a dot or a slash, then it's part of a
// directory specifier like ../../somedir/./blah.
std::string::size_type test = in.rfind( '.' );
if ( test != std::string::npos )
{
// This handles things like ".\blah" or "c:\my@email.com\abc\def\geh"
// Which would otherwise wind up with "" and "c:\my@email", respectively.
if ( in.rfind( '\\' ) < test && in.rfind( '/' ) < test )
{
in.resize( test );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Tokenizes a string into a vector of strings
//-----------------------------------------------------------------------------
std::vector<std::string> TokenizeString( const std::string & sString, char cToken )
{
std::vector<std::string> vecStrings;
std::istringstream stream( sString );
std::string s;
while ( std::getline( stream, s, cToken ) )
{
vecStrings.push_back( s );
}
if ( !sString.empty() && sString.back() == cToken )
{
vecStrings.push_back( "" );
}
return vecStrings;
}
//-----------------------------------------------------------------------------
// Purpose: Repairs a should-be-UTF-8 string to a for-sure-is-UTF-8 string, plus return boolean if we subbed in '?' somewhere
//-----------------------------------------------------------------------------
bool RepairUTF8( const char *pbegin, const char *pend, std::string & sOutputUtf8 )
{
typedef std::codecvt_utf8<char32_t> facet_type;
facet_type myfacet;
std::mbstate_t mystate = std::mbstate_t();
sOutputUtf8.clear();
sOutputUtf8.reserve( pend - pbegin );
bool bSqueakyClean = true;
const char *pmid = pbegin;
while ( pmid != pend )
{
bool bHasError = false;
bool bHasValidData = false;
char32_t out = 0xdeadbeef, *pout;
pbegin = pmid;
switch ( myfacet.in( mystate, pbegin, pend, pmid, &out, &out + 1, pout ) )
{
case facet_type::ok:
bHasValidData = true;
break;
case facet_type::noconv:
// unexpected! always converting type
bSqueakyClean = false;
break;
case facet_type::partial:
bHasError = pbegin == pmid;
if ( bHasError )
{
bSqueakyClean = false;
}
else
{
bHasValidData = true;
}
break;
case facet_type::error:
bHasError = true;
bSqueakyClean = false;
break;
}
if ( bHasValidData )
{
// could convert back, but no need
for ( const char *p = pbegin; p != pmid; ++p )
{
sOutputUtf8 += *p;
}
}
if ( bHasError )
{
sOutputUtf8 += '?';
}
if ( pmid == pbegin )
{
pmid++;
}
}
return bSqueakyClean;
}
//-----------------------------------------------------------------------------
// Purpose: Repairs a should-be-UTF-8 string to a for-sure-is-UTF-8 string, plus return boolean if we subbed in '?' somewhere
//-----------------------------------------------------------------------------
bool RepairUTF8( const std::string & sInputUtf8, std::string & sOutputUtf8 )
{
return RepairUTF8( sInputUtf8.data(), sInputUtf8.data() + sInputUtf8.size(), sOutputUtf8 );
}
//-----------------------------------------------------------------------------
// Purpose: Trims trailing CR, LF, Tab, and Space characters
//-----------------------------------------------------------------------------
std::string TrimTrailingWhitespace( const std::string& in )
{
// trim string at the point where a non-whitespace character occurs -- often at the end of the string
size_t endpos = in.find_last_not_of( " \t\n\r" );
if ( std::string::npos != endpos )
{
return in.substr( 0, endpos + 1 );
}
else
{
return in;
}
}
/** Returns an IP address as a string */
std::string IpToString( uint32_t unIpH )
{
uint8_t *ip = ( uint8_t * )&unIpH;
return Format( "%d.%d.%d.%d", ip[ 3 ], ip[ 2 ], ip[ 1 ], ip[ 0 ] );
}
/** Returns an IP address and port as a string */
std::string IpAndPortToString( uint32_t unIpH, uint16_t usPortH )
{
uint8_t *ip = ( uint8_t * )&unIpH;
return Format( "%d.%d.%d.%d:%u", ip[ 3 ], ip[ 2 ], ip[ 1 ], ip[ 0 ], usPortH );
}
+166
View File
@@ -0,0 +1,166 @@
//========= Copyright Valve Corporation ============//
#pragma once
#include <string>
#include <stdint.h>
#include <sys/types.h>
#include <vector>
/** returns true if the string has the prefix */
bool StringHasPrefix( const std::string & sString, const std::string & sPrefix );
bool StringHasPrefixCaseSensitive( const std::string & sString, const std::string & sPrefix );
/** returns if the string has the suffix */
bool StringHasSuffix( const std::string &sString, const std::string &sSuffix );
bool StringHasSuffixCaseSensitive( const std::string &sString, const std::string &sSuffix );
/** returns sModify with all instances of sFind replaced with sReplace */
std::string StringReplace( const std::string &sModify, const std::string &sFind, const std::string &sReplace );
/** converts a UTF-16 string to a UTF-8 string */
std::string UTF16to8( const wchar_t * in );
std::string UTF16to8( const std::wstring & in );
/** converts a UTF-8 string to a UTF-16 string */
std::wstring UTF8to16(const char * in);
std::wstring UTF8to16( const std::string & in );
#define Utf16FromUtf8 UTF8to16
#if defined( _WIN32 )
std::string DefaultACPtoUTF8( const char *pszStr );
#endif
/** Repairs a should-be-UTF-8 string to a for-sure-is-UTF-8 string, plus return boolean if we subbed in '?' somewhere */
bool RepairUTF8( const char *begin, const char *end, std::string & sOutputUtf8 );
bool RepairUTF8( const std::string & sInputUtf8, std::string & sOutputUtf8 );
/** Trims trailing CR, LF, Tab, and Space characters */
std::string TrimTrailingWhitespace( const std::string& in );
/** safely copy a string into a buffer */
void strcpy_safe( char *pchBuffer, size_t unBufferSizeBytes, const char *pchSource );
template< size_t bufferSize >
void strcpy_safe( char (& buffer) [ bufferSize ], const char *pchSource )
{
strcpy_safe( buffer, bufferSize, pchSource );
}
/** Turns printf-style format args into a std::string */
std::string Format( const char *pchFormat, ... );
/** converts a string to upper case */
std::string StringToUpper( const std::string & sString );
/** converts a string to lower case */
std::string StringToLower( const std::string & sString );
// we stricmp (from WIN) but it isn't POSIX - OSX/LINUX have strcasecmp so just inline bridge to it
#if defined( OSX ) || defined( LINUX )
int stricmp(const char *pStr1, const char *pStr2);
#ifndef _stricmp
#define _stricmp stricmp
#endif
int strnicmp( const char *pStr1, const char *pStr2, size_t unBufferLen );
#ifndef _strnicmp
#define _strnicmp strnicmp
#endif
#ifndef _vsnprintf_s
#define _vsnprintf_s vsnprintf
#endif
#define _TRUNCATE ((size_t)-1)
#endif
#if defined( OSX )
// behaviors ensure NULL-termination at least as well as _TRUNCATE does, but
// wcsncpy_s/strncpy_s can non-NULL-terminate, wcslcpy/strlcpy can not.
inline errno_t wcsncpy_s(wchar_t *strDest, size_t numberOfElements, const wchar_t *strSource, size_t count)
{
return wcslcpy(strDest, strSource, numberOfElements);
}
inline errno_t strncpy_s(char *strDest, size_t numberOfElements, const char *strSource, size_t count)
{
return strlcpy(strDest, strSource, numberOfElements);
}
#endif
#if defined( LINUX )
// this implementation does not return whether or not the destination was
// truncated, but that is straightforward to fix if anybody actually needs the
// return code.
#include "string.h"
inline void wcsncpy_s(wchar_t *strDest, size_t numberOfElements, const wchar_t *strSource, size_t count)
{
wcsncpy(strDest, strSource, numberOfElements);
strDest[numberOfElements-1] = '\0';
}
inline void strncpy_s(char *strDest, size_t numberOfElements, const char *strSource, size_t count)
{
strncpy(strDest, strSource, numberOfElements);
strDest[numberOfElements-1] = '\0';
}
#endif
#if defined( _WIN32 ) && _MSC_VER < 1800
inline uint64_t strtoull(const char *str, char **endptr, int base) { return _strtoui64( str, endptr, base ); }
#endif
/* Handles copying a std::string into a buffer as would be provided in an API */
uint32_t ReturnStdString( const std::string & sValue, char *pchBuffer, uint32_t unBufferLen );
/** Returns a std::string from a uint64_t */
std::string Uint64ToString( uint64_t ulValue );
/** returns a uint64_t from a string */
uint64_t StringToUint64( const std::string & sValue );
//-----------------------------------------------------------------------------
// Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
// This version of the call isn't a strict RFC implementation, but uses + for space as is
// the standard in HTML form encoding, despite it not being part of the RFC.
//
// Dest buffer should be at least as large as source buffer to guarantee room for decode.
//-----------------------------------------------------------------------------
void V_URLEncode( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen );
/** Same as V_URLEncode, but without plus for space. */
void V_URLEncodeNoPlusForSpace( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen );
/** Same as V_URLEncodeNoPlusForSpace, but without escaping / and : */
void V_URLEncodeFullPath( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen );
//-----------------------------------------------------------------------------
// Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
// This version of the call isn't a strict RFC implementation, but uses + for space as is
// the standard in HTML form encoding, despite it not being part of the RFC.
//
// Dest buffer should be at least as large as source buffer to guarantee room for decode.
// Dest buffer being the same as the source buffer (decode in-place) is explicitly allowed.
//-----------------------------------------------------------------------------
size_t V_URLDecode( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen );
/** Same as V_URLDecode, but without plus for space. */
size_t V_URLDecodeNoPlusForSpace( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen );
//-----------------------------------------------------------------------------
// Purpose: strip extension from a path
//-----------------------------------------------------------------------------
void V_StripExtension( std::string &in );
/** Returns an IP address as a string */
std::string IpToString( uint32_t unIpH );
/** Returns an IP address and port as a string */
std::string IpAndPortToString( uint32_t unIpH, uint16_t usPortH );
/** Tokenizes a string into a vector of strings */
std::vector<std::string> TokenizeString( const std::string & sString, char cToken );
+491
View File
@@ -0,0 +1,491 @@
//========= Copyright Valve Corporation ============//
#include <vrcore/vrpathregistry_public.h>
#include <json/json.h>
#include <vrcore/pathtools_public.h>
#include <vrcore/envvartools_public.h>
#include <vrcore/strtools_public.h>
#include <vrcore/dirtools_public.h>
#if defined( WIN32 )
#include <windows.h>
#include <shlobj.h>
#undef GetEnvironmentVariable
#elif defined OSX
#include <Foundation/Foundation.h>
#include <AppKit/AppKit.h>
#elif defined(LINUX)
#include <dlfcn.h>
#include <stdio.h>
#endif
#include <algorithm>
#include <sstream>
#ifndef VRLog
#if defined( __MINGW32__ )
#define VRLog(args...) fprintf(stderr, args)
#elif defined( WIN32 )
#define VRLog(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
#define VRLog(args...) fprintf(stderr, args)
#endif
#endif
/** Returns the root of the directory the system wants us to store user config data in */
static std::string GetAppSettingsPath()
{
#if defined( WIN32 )
WCHAR rwchPath[MAX_PATH];
if( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, rwchPath ) ) )
{
return "";
}
// Convert the path to UTF-8 and store in the output
std::string sUserPath = UTF16to8( rwchPath );
return sUserPath;
#elif defined( OSX )
std::string sSettingsDir;
@autoreleasepool {
// Search for the path
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES );
if ( [paths count] == 0 )
{
return "";
}
NSString *resolvedPath = [paths objectAtIndex:0];
resolvedPath = [resolvedPath stringByAppendingPathComponent: @"OpenVR"];
if ( ![[NSFileManager defaultManager] createDirectoryAtPath: resolvedPath withIntermediateDirectories:YES attributes:nil error:nil] )
{
return "";
}
sSettingsDir.assign( [resolvedPath UTF8String] );
}
return sSettingsDir;
#elif defined( LINUX )
// As defined by XDG Base Directory Specification
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
const char *pchHome = getenv("XDG_CONFIG_HOME");
if ( ( pchHome != NULL) && ( pchHome[0] != '\0' ) )
{
return pchHome;
}
//
// XDG_CONFIG_HOME is not defined, use ~/.config instead
//
pchHome = getenv( "HOME" );
if ( pchHome == NULL )
{
return "";
}
std::string sUserPath( pchHome );
sUserPath = Path_Join( sUserPath, ".config" );
return sUserPath;
#else
#warning "Unsupported platform"
#endif
}
// ---------------------------------------------------------------------------
// Purpose: Constructor
// ---------------------------------------------------------------------------
CVRPathRegistry_Public::CVRPathRegistry_Public()
{
}
// ---------------------------------------------------------------------------
// Purpose: Computes the registry filename
// ---------------------------------------------------------------------------
std::string CVRPathRegistry_Public::GetOpenVRConfigPath()
{
std::string sConfigPath = GetAppSettingsPath();
if( sConfigPath.empty() )
return "";
#if defined( _WIN32 ) || defined( LINUX )
sConfigPath = Path_Join( sConfigPath, "openvr" );
#elif defined ( OSX )
sConfigPath = Path_Join( sConfigPath, ".openvr" );
#else
#warning "Unsupported platform"
#endif
sConfigPath = Path_FixSlashes( sConfigPath );
return sConfigPath;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
std::string CVRPathRegistry_Public::GetVRPathRegistryFilename()
{
std::string sOverridePath = GetEnvironmentVariable( "VR_PATHREG_OVERRIDE" );
if ( !sOverridePath.empty() )
return sOverridePath;
std::string sPath = GetOpenVRConfigPath();
if ( sPath.empty() )
return "";
#if defined( _WIN32 )
sPath = Path_Join( sPath, "openvrpaths.vrpath" );
#elif defined ( POSIX )
sPath = Path_Join( sPath, "openvrpaths.vrpath" );
#else
#error "Unsupported platform"
#endif
sPath = Path_FixSlashes( sPath );
return sPath;
}
// ---------------------------------------------------------------------------
// Purpose: Converts JSON to a history array
// ---------------------------------------------------------------------------
static void ParseStringListFromJson( std::vector< std::string > *pvecHistory, const Json::Value & root, const char *pchArrayName )
{
if( !root.isMember( pchArrayName ) )
return;
const Json::Value & arrayNode = root[ pchArrayName ];
if( !arrayNode )
{
VRLog( "VR Path Registry node %s is not an array\n", pchArrayName );
return;
}
pvecHistory->clear();
pvecHistory->reserve( arrayNode.size() );
for( uint32_t unIndex = 0; unIndex < arrayNode.size(); unIndex++ )
{
std::string sPath( arrayNode[ unIndex ].asString() );
pvecHistory->push_back( sPath );
}
}
// ---------------------------------------------------------------------------
// Purpose: Converts a history array to JSON
// ---------------------------------------------------------------------------
static void StringListToJson( const std::vector< std::string > & vecHistory, Json::Value & root, const char *pchArrayName )
{
Json::Value & arrayNode = root[ pchArrayName ];
for( auto i = vecHistory.begin(); i != vecHistory.end(); i++ )
{
arrayNode.append( *i );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CVRPathRegistry_Public::ToJsonString( std::string &sJsonString )
{
std::string sRegPath = GetVRPathRegistryFilename();
if( sRegPath.empty() )
return false;
std::string sRegistryContents = Path_ReadTextFile( sRegPath );
if( sRegistryContents.empty() )
return false;
sJsonString = sRegistryContents;
return true;
}
// ---------------------------------------------------------------------------
// Purpose: Loads the config file from its well known location
// ---------------------------------------------------------------------------
bool CVRPathRegistry_Public::BLoadFromFile( std::string *psLoadError )
{
std::string sRegPath = GetVRPathRegistryFilename();
if( sRegPath.empty() )
{
if ( psLoadError )
{
*psLoadError = "Unable to determine VR Path Registry filename";
}
return false;
}
std::string sRegistryContents = Path_ReadTextFile( sRegPath );
if( sRegistryContents.empty() )
{
if ( psLoadError )
{
*psLoadError = "Unable to read VR Path Registry from " + sRegPath;
}
return false;
}
Json::Value root;
Json::CharReaderBuilder builder;
std::istringstream istream( sRegistryContents );
std::string sErrors;
try {
if ( !parseFromStream( builder, istream, &root, &sErrors ) )
{
if ( psLoadError )
{
*psLoadError = "Unable to parse " + sRegPath + ": " + sErrors;
}
return false;
}
ParseStringListFromJson( &m_vecRuntimePath, root, "runtime" );
ParseStringListFromJson( &m_vecConfigPath, root, "config" );
ParseStringListFromJson( &m_vecLogPath, root, "log" );
if ( root.isMember( "external_drivers" ) && root["external_drivers"].isArray() )
{
ParseStringListFromJson( &m_vecExternalDrivers, root, "external_drivers" );
}
}
catch ( ... )
{
if ( psLoadError )
{
*psLoadError = "Unable to parse " + sRegPath + ": exception thrown in JSON library";
}
return false;
}
return true;
}
// ---------------------------------------------------------------------------
// Purpose: Saves the config file to its well known location
// ---------------------------------------------------------------------------
bool CVRPathRegistry_Public::BSaveToFile() const
{
std::string sRegPath = GetVRPathRegistryFilename();
if( sRegPath.empty() )
return false;
Json::Value root;
root[ "version" ] = 1;
root[ "jsonid" ] = "vrpathreg";
StringListToJson( m_vecRuntimePath, root, "runtime" );
StringListToJson( m_vecConfigPath, root, "config" );
StringListToJson( m_vecLogPath, root, "log" );
StringListToJson( m_vecExternalDrivers, root, "external_drivers" );
Json::StreamWriterBuilder builder;
std::string sRegistryContents = Json::writeString( builder, root );
// make sure the directory we're writing into actually exists
std::string sRegDirectory = Path_StripFilename( sRegPath );
if( !BCreateDirectoryRecursive( sRegDirectory.c_str() ) )
{
VRLog( "Unable to create path registry directory %s\n", sRegDirectory.c_str() );
return false;
}
if( !Path_WriteStringToTextFile( sRegPath, sRegistryContents.c_str() ) )
{
VRLog( "Unable to write VR path registry to %s\n", sRegPath.c_str() );
return false;
}
return true;
}
// ---------------------------------------------------------------------------
// Purpose: Returns the current runtime path or NULL if no path is configured.
// ---------------------------------------------------------------------------
std::string CVRPathRegistry_Public::GetRuntimePath() const
{
if( m_vecRuntimePath.empty() )
return "";
else
return m_vecRuntimePath.front().c_str();
}
// ---------------------------------------------------------------------------
// Purpose: Returns the current config path or NULL if no path is configured.
// ---------------------------------------------------------------------------
std::string CVRPathRegistry_Public::GetConfigPath() const
{
if( m_vecConfigPath.empty() )
return "";
else
return m_vecConfigPath.front().c_str();
}
// ---------------------------------------------------------------------------
// Purpose: Returns the current log path or NULL if no path is configured.
// ---------------------------------------------------------------------------
std::string CVRPathRegistry_Public::GetLogPath() const
{
if( m_vecLogPath.empty() )
return "";
else
return m_vecLogPath.front().c_str();
}
// ---------------------------------------------------------------------------
// Purpose: Returns paths using the path registry and the provided override
// values. Pass NULL for any paths you don't care about.
// ---------------------------------------------------------------------------
bool CVRPathRegistry_Public::GetPaths( std::string *psRuntimePath, std::string *psConfigPath, std::string *psLogPath, const char *pchConfigPathOverride, const char *pchLogPathOverride, std::vector<std::string> *pvecExternalDrivers )
{
std::string sLoadError;
CVRPathRegistry_Public pathReg;
bool bLoadedRegistry = pathReg.BLoadFromFile( &sLoadError );
int nCountEnvironmentVariables = 0;
int nRequestedPaths = 0;
if( psRuntimePath )
{
nRequestedPaths++;
if ( GetEnvironmentVariable( k_pchRuntimeOverrideVar ).length() != 0 )
{
*psRuntimePath = GetEnvironmentVariable( k_pchRuntimeOverrideVar );
nCountEnvironmentVariables++;
}
else if( !pathReg.GetRuntimePath().empty() )
{
*psRuntimePath = pathReg.GetRuntimePath();
}
else
{
*psRuntimePath = "";
}
}
if( psConfigPath )
{
nRequestedPaths++;
if ( GetEnvironmentVariable( k_pchConfigOverrideVar ).length() != 0 )
{
*psConfigPath = GetEnvironmentVariable( k_pchConfigOverrideVar );
nCountEnvironmentVariables++;
}
else if( pchConfigPathOverride )
{
*psConfigPath = pchConfigPathOverride;
}
else if( !pathReg.GetConfigPath().empty() )
{
*psConfigPath = pathReg.GetConfigPath();
}
else
{
*psConfigPath = "";
}
}
if( psLogPath )
{
nRequestedPaths++;
if ( GetEnvironmentVariable( k_pchLogOverrideVar ).length() != 0 )
{
*psLogPath = GetEnvironmentVariable( k_pchLogOverrideVar );
nCountEnvironmentVariables++;
}
else if( pchLogPathOverride )
{
*psLogPath = pchLogPathOverride;
}
else if( !pathReg.GetLogPath().empty() )
{
*psLogPath = pathReg.GetLogPath();
}
else
{
*psLogPath = "";
}
}
if ( pvecExternalDrivers )
{
*pvecExternalDrivers = pathReg.m_vecExternalDrivers;
}
if ( nCountEnvironmentVariables == nRequestedPaths )
{
// all three environment variables were set, so we don't need the physical file
return true;
}
else if( !bLoadedRegistry )
{
VRLog( "%s\n", sLoadError.c_str() );
}
return bLoadedRegistry;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
uint32_t CVRPathRegistry_Public::GetSteamAppId()
{
#if !defined( REL_BRANCH_ONLY )
uint32_t nSteamAppId = k_unSteamVRMainAppId;
#else
uint32_t nSteamAppId = k_unSteamVRAppId;
#endif
return nSteamAppId;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CVRPathRegistry_Public::IsSteamVRMain()
{
#if defined( REL_BRANCH_ONLY )
return false;
#else
return true;
#endif
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
uint32_t CVRPathRegistry_Public::InitSteamAppId()
{
uint32_t nSteamAppId = CVRPathRegistry_Public::GetSteamAppId();
// Forcefully setting to what it should be before SteamAPI_Init() since SteamVR is more often
// started as child processes of the game app and otherwise Steam then considers us as the
// wrong app id.
SetEnvironmentVariable( "SteamAppId", std::to_string( nSteamAppId ).c_str() );
SetEnvironmentVariable( "SteamGameId", std::to_string( nSteamAppId ).c_str() );
return nSteamAppId;
}
//-----------------------------------------------------------------------------
// Purpose: Used by things that guess appkey to special case children of SteamVR processes
//-----------------------------------------------------------------------------
bool CVRPathRegistry_Public::IsChildOfVRServer()
{
std::string app_key = GetEnvironmentVariable( "STEAMVR_APPKEY" );
return app_key == "openvr.component.vrserver";
}
+52
View File
@@ -0,0 +1,52 @@
//========= Copyright Valve Corporation ============//
#pragma once
#include <string>
#include <vector>
#include <stdint.h>
static const char *k_pchRuntimeOverrideVar = "VR_OVERRIDE";
static const char *k_pchConfigOverrideVar = "VR_CONFIG_PATH";
static const char *k_pchLogOverrideVar = "VR_LOG_PATH";
static const uint32_t k_unSteamVRAppId = 250820;
static const uint32_t k_unSteamVRMainAppId = 330050;
class CVRPathRegistry_Public
{
public:
static std::string GetVRPathRegistryFilename();
static std::string GetOpenVRConfigPath();
static uint32_t GetSteamAppId();
static bool IsSteamVRMain();
static uint32_t InitSteamAppId();
static bool IsChildOfVRServer();
public:
CVRPathRegistry_Public();
/** Returns paths using the path registry and the provided override values. Pass NULL for any paths you don't care about.
* Returns false if the path registry could not be read. Valid paths might still be returned based on environment variables. */
static bool GetPaths( std::string *psRuntimePath, std::string *psConfigPath, std::string *psLogPath, const char *pchConfigPathOverride, const char *pchLogPathOverride, std::vector<std::string> *pvecExternalDrivers = NULL );
bool BLoadFromFile( std::string *psError = nullptr );
bool BSaveToFile() const;
bool ToJsonString( std::string &sJsonString );
// methods to get the current values
std::string GetRuntimePath() const;
std::string GetConfigPath() const;
std::string GetLogPath() const;
protected:
typedef std::vector< std::string > StringVector_t;
// index 0 is the current setting
StringVector_t m_vecRuntimePath;
StringVector_t m_vecLogPath;
StringVector_t m_vecConfigPath;
// full list of external drivers
StringVector_t m_vecExternalDrivers;
};
+21
View File
@@ -0,0 +1,21 @@
The MIT License (Expat)
Copyright (c) Zig contributors
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.
+311
View File
@@ -0,0 +1,311 @@
==============================================================================
The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
==============================================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.
==============================================================================
Software from third parties included in the LLVM Project:
==============================================================================
The LLVM Project contains third party software which is under different license
terms. All such code will be identified clearly using at least one of two
mechanisms:
1) It will be in a separate directory tree with its own `LICENSE.txt` or
`LICENSE` file at the top containing the specific license and restrictions
which apply to that software, or
2) It will contain specific license and restriction terms at the top of every
file.
==============================================================================
Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
==============================================================================
The libc++ library is dual licensed under both the University of Illinois
"BSD-Like" license and the MIT license. As a user of this code you may choose
to use it under either license. As a contributor, you agree to allow your code
to be used under both.
Full text of the relevant licenses is included below.
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
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:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
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
CONTRIBUTORS 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 WITH THE
SOFTWARE.
==============================================================================
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
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.
+311
View File
@@ -0,0 +1,311 @@
==============================================================================
The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
==============================================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.
==============================================================================
Software from third parties included in the LLVM Project:
==============================================================================
The LLVM Project contains third party software which is under different license
terms. All such code will be identified clearly using at least one of two
mechanisms:
1) It will be in a separate directory tree with its own `LICENSE.txt` or
`LICENSE` file at the top containing the specific license and restrictions
which apply to that software, or
2) It will contain specific license and restriction terms at the top of every
file.
==============================================================================
Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
==============================================================================
The libc++abi library is dual licensed under both the University of Illinois
"BSD-Like" license and the MIT license. As a user of this code you may choose
to use it under either license. As a contributor, you agree to allow your code
to be used under both.
Full text of the relevant licenses is included below.
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
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:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
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
CONTRIBUTORS 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 WITH THE
SOFTWARE.
==============================================================================
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
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.
+311
View File
@@ -0,0 +1,311 @@
==============================================================================
The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
==============================================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.
==============================================================================
Software from third parties included in the LLVM Project:
==============================================================================
The LLVM Project contains third party software which is under different license
terms. All such code will be identified clearly using at least one of two
mechanisms:
1) It will be in a separate directory tree with its own `LICENSE.txt` or
`LICENSE` file at the top containing the specific license and restrictions
which apply to that software, or
2) It will contain specific license and restriction terms at the top of every
file.
==============================================================================
Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
==============================================================================
The libunwind library is dual licensed under both the University of Illinois
"BSD-Like" license and the MIT license. As a user of this code you may choose
to use it under either license. As a contributor, you agree to allow your code
to be used under both.
Full text of the relevant licenses is included below.
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
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:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
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
CONTRIBUTORS 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 WITH THE
SOFTWARE.
==============================================================================
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
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.