mirror of
https://github.com/Toastconcern/FrameStudio.git
synced 2026-10-29 04:02:56 +01:00
- src/home_platform.h: what Frame Home and the panel need from the system (paths, finding and stopping each other, running a tool, restarting, priorities) for SteamOS, Windows and Linux PCs. On a PC the environments are Frame Studio's My Environments; Frame Home's own files in %LOCALAPPDATA%\Frame Home (Windows). - src/openvr_loader.cpp: a PC build finds SteamVR's own OpenVR library at run time (openvrpaths.vrpath), so it runs on any PC with SteamVR; Vulkan through volk. CMake: FRAME_HOME_PC (default on Windows and x86-64 Linux); the Steam Frame build is unchanged. - Windows: Frame Home is SteamVR's home app; SteamVR's classic environment shows a 360-degree picture of the current home from its spawn (frame-home --panorama --spawn, made by the panel when the home changes) instead of Aurora while Frame Home starts. Linux PCs: the Steam Frame's compositor layer, as on the headset. - frame/pc: install-frame-home-pc.ps1 (+ .bat): online through SteamVR's API or offline in its config files; the SteamVR settings it changes are saved and restored exactly on -Uninstall (tested in a sandbox both ways). frame-switcher --uninstall. Studio: Properties > Headset > This PC installs it from pc/ beside Studio. - Packaging and CI: pc/ in the Windows x64 and Linux x64 Studio downloads; FrameHome-PC-windows-x64.zip. - Thumbnails come from where Frame Home starts you (the first spawn, eyes 1.6 m up): Studio's saves and the panel's (frame-home --snapshot --spawn). - docs/pc-steamvr.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
163 lines
6.0 KiB
C++
163 lines
6.0 KiB
C++
// SteamVR's own OpenVR client library, loaded at run time from wherever SteamVR is installed on this PC: openvr.h's
|
|
// entry points, forwarded to it. A PC build of Frame Home and its panel then runs on any PC with SteamVR (Windows or
|
|
// Linux) without carrying a copy, the way the Steam Frame build links SteamVR's own (built with OPENVR_BUILD_STATIC,
|
|
// so openvr.h declares these as plain functions for this file to define).
|
|
//
|
|
// Where SteamVR is: OpenVR's own record, openvrpaths.vrpath ("runtime": [ "<SteamVR's folder>", ... ]), in
|
|
// %LOCALAPPDATA%\openvr (Windows) or ~/.config/openvr (Linux); then a copy beside this program, if there is one.
|
|
#include <openvr.h>
|
|
|
|
#include <cstdlib>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#ifdef _WIN32
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
#include <windows.h>
|
|
#include <shlobj.h>
|
|
#else
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
// The runtime folders openvrpaths.vrpath lists, in order.
|
|
std::vector<fs::path> runtimeFolders() {
|
|
fs::path file;
|
|
#ifdef _WIN32
|
|
if (const wchar_t* l = _wgetenv(L"LOCALAPPDATA"); l && *l) file = fs::path(l) / L"openvr" / L"openvrpaths.vrpath"; // (as set: a test can point it elsewhere)
|
|
else {
|
|
PWSTR p = nullptr;
|
|
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &p))) file = fs::path(p) / L"openvr" / L"openvrpaths.vrpath";
|
|
CoTaskMemFree(p);
|
|
}
|
|
#else
|
|
const char* xdg = std::getenv("XDG_CONFIG_HOME");
|
|
const char* home = std::getenv("HOME");
|
|
file = xdg && *xdg ? fs::path(xdg) / "openvr" / "openvrpaths.vrpath" : fs::path(home ? home : "") / ".config" / "openvr" / "openvrpaths.vrpath";
|
|
#endif
|
|
std::ifstream in(file, std::ios::binary);
|
|
const std::string j((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
|
std::vector<fs::path> out;
|
|
size_t at = j.find("\"runtime\"");
|
|
if (at == std::string::npos) return out;
|
|
at = j.find('[', at);
|
|
const size_t end = at == std::string::npos ? at : j.find(']', at);
|
|
while (at != std::string::npos && at < end) { // each "..." in the array, JSON escapes undone
|
|
const size_t q = j.find('"', at + 1);
|
|
if (q == std::string::npos || q > end) break;
|
|
std::string s;
|
|
size_t k = q + 1;
|
|
for (; k < j.size() && j[k] != '"'; ++k) {
|
|
if (j[k] == '\\' && k + 1 < j.size()) ++k;
|
|
s += j[k];
|
|
}
|
|
if (!s.empty()) out.push_back(fs::u8path(s));
|
|
at = k + 1;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
using Lib = HMODULE;
|
|
Lib open(const fs::path& p) { return LoadLibraryW(p.wstring().c_str()); }
|
|
void* symbol(Lib l, const char* name) { return reinterpret_cast<void*>(GetProcAddress(l, name)); }
|
|
const char* kLibInRuntime = "bin/win64/openvr_api.dll";
|
|
const char* kLibName = "openvr_api.dll";
|
|
#else
|
|
using Lib = void*;
|
|
Lib open(const fs::path& p) { return dlopen(p.c_str(), RTLD_NOW | RTLD_LOCAL); }
|
|
void* symbol(Lib l, const char* name) { return dlsym(l, name); }
|
|
const char* kLibInRuntime = "bin/linux64/libopenvr_api.so";
|
|
const char* kLibName = "libopenvr_api.so";
|
|
#endif
|
|
|
|
Lib library() {
|
|
static const Lib lib = [] {
|
|
for (const fs::path& r : runtimeFolders())
|
|
if (const Lib l = open(r / fs::u8path(kLibInRuntime))) return l;
|
|
return open(fs::u8path(kLibName)); // (one beside the program, or on the search path)
|
|
}();
|
|
return lib;
|
|
}
|
|
|
|
template <typename F> F entry(const char* name) {
|
|
const Lib l = library();
|
|
return l ? reinterpret_cast<F>(symbol(l, name)) : nullptr;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace vr {
|
|
|
|
uint32_t VR_CALLTYPE VR_InitInternal2(EVRInitError* peError, EVRApplicationType eApplicationType, const char* pStartupInfo) {
|
|
using F = uint32_t(VR_CALLTYPE*)(EVRInitError*, EVRApplicationType, const char*);
|
|
static const F f = entry<F>("VR_InitInternal2");
|
|
if (!f) { if (peError) *peError = VRInitError_Init_InstallationNotFound; return 0; }
|
|
return f(peError, eApplicationType, pStartupInfo);
|
|
}
|
|
|
|
void VR_CALLTYPE VR_ShutdownInternal() {
|
|
using F = void(VR_CALLTYPE*)();
|
|
static const F f = entry<F>("VR_ShutdownInternal");
|
|
if (f) f();
|
|
}
|
|
|
|
void* VR_CALLTYPE VR_GetGenericInterface(const char* pchInterfaceVersion, EVRInitError* peError) {
|
|
using F = void*(VR_CALLTYPE*)(const char*, EVRInitError*);
|
|
static const F f = entry<F>("VR_GetGenericInterface");
|
|
if (!f) { if (peError) *peError = VRInitError_Init_InstallationNotFound; return nullptr; }
|
|
return f(pchInterfaceVersion, peError);
|
|
}
|
|
|
|
bool VR_CALLTYPE VR_IsInterfaceVersionValid(const char* pchInterfaceVersion) {
|
|
using F = bool(VR_CALLTYPE*)(const char*);
|
|
static const F f = entry<F>("VR_IsInterfaceVersionValid");
|
|
return f && f(pchInterfaceVersion);
|
|
}
|
|
|
|
uint32_t VR_CALLTYPE VR_GetInitToken() {
|
|
using F = uint32_t(VR_CALLTYPE*)();
|
|
static const F f = entry<F>("VR_GetInitToken");
|
|
return f ? f() : 0;
|
|
}
|
|
|
|
bool VR_CALLTYPE VR_IsHmdPresent() {
|
|
using F = bool(VR_CALLTYPE*)();
|
|
static const F f = entry<F>("VR_IsHmdPresent");
|
|
return f && f();
|
|
}
|
|
|
|
bool VR_CALLTYPE VR_IsRuntimeInstalled() {
|
|
using F = bool(VR_CALLTYPE*)();
|
|
static const F f = entry<F>("VR_IsRuntimeInstalled");
|
|
return f && f();
|
|
}
|
|
|
|
bool VR_GetRuntimePath(char* pchPathBuffer, uint32_t unBufferSize, uint32_t* punRequiredBufferSize) {
|
|
using F = bool (*)(char*, uint32_t, uint32_t*);
|
|
static const F f = entry<F>("VR_GetRuntimePath");
|
|
return f && f(pchPathBuffer, unBufferSize, punRequiredBufferSize);
|
|
}
|
|
|
|
const char* VR_CALLTYPE VR_GetVRInitErrorAsSymbol(EVRInitError error) {
|
|
using F = const char*(VR_CALLTYPE*)(EVRInitError);
|
|
static const F f = entry<F>("VR_GetVRInitErrorAsSymbol");
|
|
return f ? f(error) : "VRInitError_Init_InstallationNotFound";
|
|
}
|
|
|
|
const char* VR_CALLTYPE VR_GetVRInitErrorAsEnglishDescription(EVRInitError error) {
|
|
using F = const char*(VR_CALLTYPE*)(EVRInitError);
|
|
static const F f = entry<F>("VR_GetVRInitErrorAsEnglishDescription");
|
|
return f ? f(error) : "SteamVR isn't installed (its OpenVR library wasn't found)";
|
|
}
|
|
|
|
} // namespace vr
|