mirror of
https://github.com/Toastconcern/FrameStudio.git
synced 2026-10-29 04:02:56 +01:00
Frame Home on a PC with SteamVR (Windows, Linux); thumbnails from the spawn
- 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>
This commit is contained in:
1 parent
b0609d3664
commit
3469b77ca3
21 files changed
+1296
-257
No files matched your search
@@ -0,0 +1,162 @@
|
||||
// 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
|
||||
Reference in new issue
Block a user