diff --git a/docs/steamframe/DESIGN.md b/docs/steamframe/DESIGN.md index 26ed19d..a0ccde5 100644 --- a/docs/steamframe/DESIGN.md +++ b/docs/steamframe/DESIGN.md @@ -75,6 +75,36 @@ Private Qt symbols in the binary built that way (`objdump -T`, `Qt_6_PRIVATE_API - It calls `VR_Init` directly, without `initializeOpenVR`'s retry loop. That loop waits and retries on every error, including the one we expect here. +### 2a. App key already registered by Steam (approved 2026-10-07) + +**Finding (Frame test T1):** Steam writes `~/.local/share/Steam/config/steamapps.vrmanifest` +(`"source": "steam"`) with every VR app the user **owns**, installed or not (Frame: 154 +entries, 25 installed). It is regenerated by Steam. Whoever owns OVRAS on Steam therefore +always has `steam.overlay.1009850` registered, as `launch_type: url` +(`steam://launch/1009850/openvroverlay`), `is_hidden`. Such an entry has no working +directory. `reinstallApplicationManifest()` treats that as an error ("Could not find working +directory of already installed application: VRApplicationError_PropertyNotSet") and stops. + +**Open question:** does SteamVR accept a second manifest for the same key, and which entry +wins? `AddApplicationManifest` may return `VRApplicationError_AppKeyAlreadyExists`. If +Steam's entry wins, auto-start and the dashboard would launch the Steam (Proton) version. + +**Change (`setup.cpp`, platform-neutral, also useful upstream):** +- `reinstallApplicationManifest()`: if the key is installed but has no working directory + (`VRApplicationError_PropertyNotSet`), there is no manifest file to remove. Log it with + the entry's source and don't remove anything. Then call `AddApplicationManifest` directly + (`installApplicationManifest()` skips installed keys) and fail with the error's name if it + is refused. +- New helper `logApplicationEntry()`, called at the end of `--force-install-manifest`: logs + what SteamVR now reports for the key (source, launch type, working directory, binary path, + URL, auto-launch). This answers the open question on the Frame. +- Unchanged: the path for a regular earlier installation (with working directory), and + `installApplicationManifest()` on normal starts. + +**Follow-up depending on the result:** if SteamVR refuses our manifest or Steam's entry +wins, the native build needs its own app key (option 2, separate design: the key is also in +the manifest, 7 default action manifests and `SteamVRTabController.h`). + ## 3. Rendering through public API (R1, R3) **From the template** (`4a5b090`, verified on the Frame), adopted largely unchanged: diff --git a/docs/steamframe/PROGRESS.md b/docs/steamframe/PROGRESS.md index 57d7da3..e55b719 100644 --- a/docs/steamframe/PROGRESS.md +++ b/docs/steamframe/PROGRESS.md @@ -214,6 +214,26 @@ 1.6 MB (was 3.0 MB, the old library was 5.4 MB). - **Not tested yet:** on the Frame (T1). +### Finding: app key already registered by Steam (2026-10-07) + +- **Third try of T1** (by hand on the Frame, with the two fixes above): `VR_Init` works, as + `Utility` (vrserver.txt: "New Connect message … (VRApplication_Utility)"; SteamVR did + **not** refuse Utility this time, unlike with the template). Then: "Manifest Previously + Installed", "Could not find working directory of already installed application: + VRApplicationError_PropertyNotSet". +- **Cause:** Steam's generated `steamapps.vrmanifest` lists every owned VR app, including + OVRAS (`steam.overlay.1009850`, launch via `steam://` URL), although the Steam version is + uninstalled. Not a leftover of the uninstallation; it can't be removed permanently. +- **Next:** design DESIGN.md section 2a (approved 2026-10-07). +- **Implementation (`setup.cpp`, `setup.h`):** as designed: key without working directory → + nothing removed, `AddApplicationManifest` directly (error name on refusal); + `logApplicationEntry()` after "Manifest reinstalled." logs source, launch type, working + directory, binary, URL and auto-launch for the key. +- **Result:** cross build and package succeed, `Qt_6_PRIVATE_API` without `QVulkan*`: 0. +- **Not tested yet:** on the Frame. The log decides: `AppKeyAlreadyExists` or Steam's entry + (`steam://` URL) → own app key needed (option 2); our working directory and binary → + enough. + ### Test checklist on the Frame (step 5) Deploy: `build_scripts/steamframe/deploy-to-frame.sh @` (SteamVR running). diff --git a/src/utils/setup.cpp b/src/utils/setup.cpp index 7a54960..f1b7dd6 100644 --- a/src/utils/setup.cpp +++ b/src/utils/setup.cpp @@ -191,6 +191,29 @@ void reinstallApplicationManifest( const std::string manifestPath ) kStringSize, &app_error ); + // Steam lists every VR app the user owns in its generated + // steamapps.vrmanifest, installed or not, so whoever owns OVRAS on + // Steam has this key registered with a steam:// URL and no working + // directory. There is no manifest file to remove then: add ours next + // to it (installApplicationManifest() would skip the installed key). + if ( app_error == vr::VRApplicationError_PropertyNotSet ) + { + qInfo() << "Key registered without a manifest file (e.g. by " + "Steam), not removing it"; + const auto add_error = vr::VRApplications()->AddApplicationManifest( + manifestPath.c_str() ); + if ( add_error != vr::VRApplicationError_None ) + { + throw std::runtime_error( + "Could not add application manifest: " + + std::string( + vr::VRApplications()->GetApplicationsErrorNameFromEnum( + add_error ) ) ); + } + qInfo() << "Manifest Installed at: " << manifestPath; + return; + } + if ( app_error != vr::VRApplicationError_None ) { throw std::runtime_error( @@ -251,6 +274,43 @@ void forceRemoveApplicationManifest() } } +// Logs what SteamVR reports for OVRAS's app key, to show which entry it uses +// when several register the key (e.g. Steam's own, see +// reinstallApplicationManifest()). +void logApplicationEntry() +{ + const auto property = []( vr::EVRApplicationProperty prop ) + { + constexpr auto kStringSize = 1024; + char value[kStringSize] = { 0 }; + auto error = vr::VRApplicationError_None; + vr::VRApplications()->GetApplicationPropertyString( + application_strings::applicationKey, + prop, + value, + kStringSize, + &error ); + return error == vr::VRApplicationError_None + ? QString( value ) + : QString( + vr::VRApplications()->GetApplicationsErrorNameFromEnum( + error ) ); + }; + qInfo() << "SteamVR entry for" << application_strings::applicationKey + << "- source:" + << property( vr::VRApplicationProperty_Source_String ) + << "launch type:" + << property( vr::VRApplicationProperty_LaunchType_String ) + << "working dir:" + << property( vr::VRApplicationProperty_WorkingDirectory_String ) + << "binary:" + << property( vr::VRApplicationProperty_BinaryPath_String ) + << "URL:" << property( vr::VRApplicationProperty_URL_String ) + << "auto-launch:" + << vr::VRApplications()->GetApplicationAutoLaunch( + application_strings::applicationKey ); +} + // Initializes OpenVR and calls the relevant manifest functions. // The .vrmanifest is used by SteamVR however the exact functiontionality is not // documented in the official documentation. @@ -298,6 +358,7 @@ void forceRemoveApplicationManifest() reinstallApplicationManifest( *manifestPath ); enableApplicationAutostart(); qInfo() << "Manifest reinstalled."; + logApplicationEntry(); } else if ( removeManifest ) { diff --git a/src/utils/setup.h b/src/utils/setup.h index 37b9311..bfd6e7e 100644 --- a/src/utils/setup.h +++ b/src/utils/setup.h @@ -117,6 +117,7 @@ void installApplicationManifest( const std::string manifestPath ); void removeApplicationManifest( const std::string manifestPath ); void reinstallApplicationManifest( const std::string manifestPath ); void forceRemoveApplicationManifest(); +void logApplicationEntry(); [[noreturn]] void handleManifests( const bool installManifest, const bool removeManifest );