mirror of
https://github.com/KominoVR/frame-passthrough-shortcuts.git
synced 2026-10-06 07:00:26 +02:00
Initial release 0.1.0
This commit is contained in:
commit
c4fc8662d1
62 files changed
+69433
No files matched your search
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
/build*/
|
||||
/dist/
|
||||
/.zig-cache/
|
||||
*.log
|
||||
@@ -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 .)
|
||||
@@ -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.
|
||||
@@ -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 😎).
|
||||
|
||||
[](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.
|
||||
@@ -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.
|
||||
```
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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": []
|
||||
}
|
||||
@@ -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": []
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"poll_interval_ms": 20,
|
||||
"debounce_ms": 250
|
||||
}
|
||||
@@ -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})
|
||||
Binary file not shown.
@@ -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 |
@@ -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
|
||||
}
|
||||
@@ -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}`);
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
Vendored
+21
@@ -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.
|
||||
Vendored
+24765
File diff suppressed because it is too large.
Load diff
Vendored
+27
@@ -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.
|
||||
Vendored
+6166
File diff suppressed because it is too large.
Load diff
+8886
File diff suppressed because it is too large.
Load diff
+6292
File diff suppressed because it is too large.
Load diff
+3443
File diff suppressed because it is too large.
Load diff
+4703
File diff suppressed because it is too large.
Load diff
+118
@@ -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()
|
||||
Vendored
+18
@@ -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>
|
||||
Vendored
+39
@@ -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
@@ -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
@@ -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
|
||||
Vendored
+2077
File diff suppressed because it is too large.
Load diff
Vendored
+5266
File diff suppressed because it is too large.
Load diff
Vendored
+11
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
//========= Copyright Valve Corporation ============//
|
||||
#pragma once
|
||||
|
||||
const char *GetEnglishStringForHmdError( vr::EVRInitError eError );
|
||||
const char *GetIDForVRInitError( vr::EVRInitError eError );
|
||||
|
||||
+1093
File diff suppressed because it is too large.
Load diff
+160
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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 );
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
Reference in new issue
Block a user