Start Frametop's SteamVR clients only once SteamVR is up (#6)

The pointer and gaze services need steamvr.service to be running (Requisite=), and ft-pointer, ft-screens, and ft-gaze connect as a background app before switching to overlay, so they never start a vrserver of their own. One started from the dev container never finds the headset, which left a reboot stuck in a loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 51b4e79318)
This commit is contained in:
Nikita Koptelov authored and DeeJanuz committed 2026-09-30 10:02:46 -06:00
1 parent 369736f0ca
commit 4e5131d8ce
6 files changed
+19 -3

No files matched your search

+2
View File
@@ -70,6 +70,8 @@ SteamVR's dashboard and every overlay it hosts are driven by the vrcompositor `l
Driver poses are in SteamVR's raw tracking space, and client programs work in the standing universe, which on the Frame is about 1.6 m above raw. Mixing them up put the laser's origin 1.6 m above your head. The helper converts using the headset's pose in both spaces every frame.
Frametop's SteamVR clients (ft-pointer, ft-screens, ft-gaze) connect as a background app first and switch to an overlay app only once that works. `VR_Init` as an overlay app starts vrserver itself when none is running, and one started that way from the dev container never finds the headset. At a boot where the gamescope session timed out, systemd dropped `steamvr.service`'s start job, the pointer service (ordered only `After=` it) started anyway, and its vrserver made every SteamVR launch fail with `HmdNotFound`. SteamOS's health check then kept resetting the Steam client and tried to fall back to the previous OS slot. The units also say `Requisite=steamvr.service`, so they don't start at all when SteamVR's start fails.
The driver starts disconnected, because holding the right-hand role while SteamVR starts leaves the Steam UI stuck on its loading icon. It connects when the mouse is used and claims the right hand. SteamVR keeps a hand role reserved for a disconnected device that still asks for it, so the driver switches its role hint between right hand (connected) and opt-out (not connected).
### The cursor
+1
View File
@@ -5,6 +5,7 @@ Documentation=file://@REPO@/gaze/README.md
# Needs SteamVR's IPC (ft-gaze is an overlay client); it starts and stops with SteamVR.
After=steamvr.service frametop-pointer.service
PartOf=steamvr.service
Requisite=steamvr.service
[Service]
# Host Python; it runs ft-gaze in the dev container (distrobox enter), which quits when
+5 -1
View File
@@ -372,7 +372,11 @@ int main(int argc, char **argv) {
stdinClosed = true;
}).detach();
vr::EVRInitError err = vr::VRInitError_None;
vr::VR_Init(&err, vr::VRApplication_Overlay);
vr::VR_Init(&err, vr::VRApplication_Background);
if (err == vr::VRInitError_None) {
vr::VR_Shutdown();
vr::VR_Init(&err, vr::VRApplication_Overlay);
}
if (err != vr::VRInitError_None) {
std::fprintf(stderr, "ft-gaze: SteamVR: %s\n", vr::VR_GetVRInitErrorAsEnglishDescription(err));
return 1;
+1
View File
@@ -5,6 +5,7 @@ Documentation=file://@REPO@/README.md
# Needs SteamVR's IPC; it starts and stops with SteamVR.
After=steamvr.service frametop-input-relay.service
PartOf=steamvr.service
Requisite=steamvr.service
[Service]
# Runs in the dev container (built there against its libraries). The helper process
+5 -1
View File
@@ -546,7 +546,11 @@ int main() {
vr::EVRInitError err = vr::VRInitError_None;
while (true) {
vr::VR_Init(&err, vr::VRApplication_Overlay);
vr::VR_Init(&err, vr::VRApplication_Background);
if (err == vr::VRInitError_None) {
vr::VR_Shutdown();
vr::VR_Init(&err, vr::VRApplication_Overlay);
}
if (err == vr::VRInitError_None) break;
std::fprintf(stderr, "waiting for SteamVR: %s\n", vr::VR_GetVRInitErrorAsEnglishDescription(err));
std::this_thread::sleep_for(std::chrono::seconds(2));
+5 -1
View File
@@ -1148,7 +1148,11 @@ extern "C" {
bool ft_vr_init(void) {
vr::EVRInitError err = vr::VRInitError_None;
vr::VR_Init(&err, vr::VRApplication_Overlay);
vr::VR_Init(&err, vr::VRApplication_Background);
if (err == vr::VRInitError_None) {
vr::VR_Shutdown();
vr::VR_Init(&err, vr::VRApplication_Overlay);
}
if (err != vr::VRInitError_None) {
std::fprintf(stderr, "openvr: %s\n", vr::VR_GetVRInitErrorAsEnglishDescription(err));
return false;