mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 06:00:33 +02:00
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>
123 lines
6.9 KiB
Markdown
123 lines
6.9 KiB
Markdown
# UEVR from Frame Control, 2026-09-29
|
||
|
||
**Verified observations**, with limits below. Same device and software as
|
||
[2026-09-28](mods-2026-09-28.md): 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-Shipping` didn't inject. The button still read
|
||
**Inject**, and the saved `OpenVRRadio=True` in `AppData/Local/praydog/…/user.config`
|
||
was not restored on the next start.
|
||
- XTest clicks landed on the game: `XQueryPointer` reported child
|
||
`0x2000001` (the game) at the injector's coordinates, and
|
||
`XLowerWindow`/`XRaiseWindow`/`XSetInputFocus` didn't change that.
|
||
- `XSendEvent` button events to the injector window worked. After OpenVR and
|
||
Inject, `/proc/<game>/maps` contained
|
||
`drive_c/frame26/uevr/UEVRBackend.dll` and `openvr_api.dll`, next to
|
||
Proton's `vrclient_x64.dll`. UEVR's log:
|
||
|
||
```text
|
||
[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.exe` or game processes were left running. Steam and
|
||
SteamVR were running at the end.
|
||
- The test lock was released at 13:03.
|