Register the manifest when Steam already lists the app key

Steam writes every VR app the user owns into its generated
steamapps.vrmanifest, installed or not. Whoever owns OVRAS on Steam
therefore always has steam.overlay.1009850 registered, as a steam:// URL
without a working directory, and --force-install-manifest failed with
"Could not find working directory of already installed application:
VRApplicationError_PropertyNotSet" (found on the Steam Frame).

Such an entry has no manifest file to remove, so OVRAS now leaves it
alone and adds its own manifest. Whether SteamVR accepts a second
manifest for the key, and which entry it then uses, is not documented:
logApplicationEntry() logs what SteamVR reports for the key after
installing, so a test on the Frame shows it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 23:25:57 +02:00
co-authored by Claude Opus 5.5
parent 317db6831d
commit ec82b5fead
4 changed files with 112 additions and 0 deletions
+30
View File
@@ -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:
+20
View File
@@ -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 <user>@<frame>` (SteamVR running).
+61
View File
@@ -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 )
{
+1
View File
@@ -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 );