frame_inject.py declares every Win32 signature, checks each result, frees its remote buffers and closes handles. Uninstall only accepts our folder and UEVR's own per-game settings folders, and keeps the receipt if anything can't be removed. The injector's reply is parsed defensively. Rechecked on a real Frame: install, three cold starts injected (33-35 s), clean uninstall. Part of #26. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
6.9 KiB
UEVR from Frame Control, 2026-09-29
Verified observations, with limits below. Same device and software as
2026-09-28: aarch64, SteamOS 0.4.1, BUILD_ID
20260925.6191901, Proton 11.0-2c ARM64, SteamVR 2.18.1. Times are the
Frame's AEST clock. Headset tests ran under the shared
/tmp/frame-test.lock, taken 11:13–11:28, 11:33–11:49, 12:02–12:40,
12:44–12:52 and 13:01–13:03. Battery 45 % →
93 %, on charge throughout.
Not observed: the VR image, head tracking and controller input. SteamVR
reported activity_level 3 (user interaction timeout; the headset was not
being worn) with average_target_fps 1. Every frame_vrshot.py stereo
capture was a uniform dark frame. Frame submits prove the app is presenting
to SteamVR, not that the image is correct.
Manual injection (11:13–11:28)
Game launched with steam steam://rungameid/1067310. UEVRInjector.exe was
started with the game process's Wine variables, and its WPF window drew
through DXVK's D3D9. It listed Drop-Win64-Shipping (pid: 320) (SkyArk (64-bit, PCD3D_SM5)), a Wine-side process id.
--attach=Drop-Win64-Shippingdidn't inject. The button still read Inject, and the savedOpenVRRadio=TrueinAppData/Local/praydog/…/user.configwas not restored on the next start.- XTest clicks landed on the game:
XQueryPointerreported child0x2000001(the game) at the injector's coordinates, andXLowerWindow/XRaiseWindow/XSetInputFocusdidn't change that. XSendEventbutton events to the injector window worked. After OpenVR and Inject,/proc/<game>/mapscontaineddrive_c/frame26/uevr/UEVRBackend.dllandopenvr_api.dll, next to Proton'svrclient_x64.dll. UEVR's log:
[11:17:32.948] Hooking D3D11
[11:17:33.020] Hooked DirectX 11
[11:17:34.263] Framework initialized
[11:17:34.475] [VR] Requested runtime: openvr_api.dll
[11:17:39.791] UGameViewportClient::Draw called for the first time.
[11:17:39.792] is stereo enabled called!
It also logged repeated Failed to initialize Framework on DirectX 12 (probing
before it chose D3D11) and failed Unreal scans, for example Failed to locate r.EnableStereoEmulation cvar and Failed to find FSceneView constructor.
SteamVR loaded UEVR's action manifest and bindings_oculus_touch.json for
steam.app.1067310. vrcmd --stats then showed "key" : "steam.app.1067310" with frame_submits 4439, then 5506 a few seconds later.
A second session's injector exited within about 20 seconds. The third,
started fresh, injected again (frame_submits 1412). After the game was
stopped, a plain launch had no UEVR modules and no SteamVR scene app. All test
files were removed.
Through Frame Control (11:33–11:49)
Local ui/server.py against the frame alias. First the API, then the
page's VR mod dialog, driven with a headless Chromium:
| Step | Result |
|---|---|
status for 2177750 (HL2 VR) |
eligible: false; install refused ("doesn't look like an Unreal Engine game") |
install for 1067310 |
3 s. Three downloads, hashes matched, receipt written |
start, game running |
First run refused: the UEVR window is 960 px wide, not 625. It was measured before WPF laid it out; start now waits for the width to settle. Rerun: injected, frame_submits 27 (36 s) |
uninstall while running |
Refused: "the game is running; quit it first" |
start, game not running |
Launched the game, injected, frame_submits 45 (55 s) |
| UI: Remove, Install | Worked; the prefix listing matched its pre-install state except an empty UnrealVRMod, which uninstall now also removes |
| UI: Play in VR | Failed once: the injector died with Process terminated. … Resource name: Arg_AccessViolationException, after Fontconfig error: No writable cache directories (it had no HOME). Fixed by keeping the normal environment under the game's (the relaunch-on-failure added next is gone with the GUI injector; see below) |
| UI: Play in VR ×4 | 4 of 4 injected. The three cold starts took 54, 58 and 58 s; frame submits 56, 71, 80 |
| UI: Remove | drive_c, AppData/Roaming and AppData/Local listings matched the pre-install state; download cache and receipt gone |
The GUI injector wasn't reliable (12:02–12:40)
After review fixes, cold starts through the API with UEVR's own injector:
- Batches of 4, 6 and 8 runs: 3/4, 4/6 and 6/8 injected. A retry that relaunched the injector didn't rescue a bad session. In both failures of the last batch, the first injector ignored the clicks and the next two never showed a window within 60 s.
- Captures of the ignoring injector (
xwd) showed its first-drawn frame, with OpenVR still unselected. The injector's log had no error.
Lock released at 12:40 before redesigning.
Frame Control's own injector (12:44–12:52)
start now runs frame_inject.py under python.org's embeddable Python
3.14.7 (SHA-256 d297e5ff…1f15, matching python.org's release-file API), in
the game's Wine session. The old install was removed and the new one
installed without the headset lock, since neither launches anything. The
prefix listing after removal matched the pre-install state; the new install
is 47 MB.
| Step | Result |
|---|---|
API start, cold |
Injected in 35 s, frame_submits 39. UEVR log: Hooked DirectX 11, Framework initialized, Requested runtime: openvr_api.dll; config.txt has Frontend_RequestedRuntime=openvr_api.dll |
API start ×8, cold |
8/8 injected, 33–34 s each; frame_submits 33–42 at the check |
| UI: dialog while running | "UEVR is running in the game now." Remove disabled (screenshot in docs/img/uevr-dialog.png) |
| UI: Remove → Install → Play in VR → Remove | Each worked; Play in VR injected (frame_submits 106 a few seconds later) |
| After the last Remove | drive_c, AppData/Roaming and AppData/Local listings matched the pre-install state; download cache and receipts gone; no game or python.exe processes. The empty /tmp/frame-control-mods.lock was deleted by hand (tmpfs) |
Battery 98–100 % on charge. Lock released at 12:52.
After the second review (13:01–13:03)
frame_inject.py now declares every Win32 signature, checks each result,
frees its remote buffers and closes its handles; uninstall accepts only our
folder and UEVR's own settings folders. Rechecked: install; three cold starts
injected in 35, 34 and 33 s (frame_submits 42, 38, 47), UEVR logging
Framework initialized and Requested runtime: openvr_api.dll; uninstall
left drive_c, AppData/Roaming and AppData/Local as before, with no cache,
receipts or processes. Frame Control's injector total: 12 of 12 cold starts
through the API, plus the UI run.
Left on the Frame
- Gravitas (free, 7.4 GB, installed 2026-09-28 for this work) is still installed; nothing else from this work is left.
- No
UEVRInjector.exeor game processes were left running. Steam and SteamVR were running at the end. - The test lock was released at 13:03.