mirror of
https://github.com/spoopyghosty0/frameport.git
synced 2026-10-12 01:01:16 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6acf9d048e | ||
|
|
cf18b30be9 | ||
|
|
eec0218b1d | ||
|
|
526ba0a684 | ||
|
|
23b6ff1b5e | ||
|
|
00c6863e54 | ||
|
|
4863035a05 | ||
|
|
a30162ea63 | ||
|
|
24eaa97fde | ||
|
|
767b152423 | ||
|
|
10e7669f70 | ||
|
|
76c4f2d225 | ||
|
|
3a851e911f | ||
|
|
2912d637b5 | ||
|
|
5c070ccf23 | ||
|
|
b979067215 | ||
|
|
4440b459e5 | ||
|
|
09b8046337 | ||
|
|
0d67335775 | ||
|
|
867b9977d5 | ||
|
|
80092098e0 | ||
|
|
feab03e049 | ||
|
|
9713da222a | ||
|
|
82e617741e | ||
|
|
9de0707070 | ||
|
|
1343930ae0 | ||
|
|
5424e429e4 | ||
|
|
9149eaecd7 | ||
|
|
64278e9e2c | ||
|
|
1531acb575 | ||
|
|
6796e006d9 | ||
|
|
45f05dd3c6 | ||
|
|
5a2cd45fb8 | ||
|
|
757671b389 | ||
|
|
8a2029c079 | ||
|
|
b2688dd368 | ||
|
|
13de114f39 | ||
|
|
9016982394 | ||
|
|
cad2cd366c | ||
|
|
a5820e68d7 | ||
|
|
aff939a6d8 | ||
|
|
5e725fbd08 | ||
|
|
1cb9e6f65b | ||
|
|
7a843962e8 | ||
|
|
51220a4942 | ||
|
|
981daa3c2d | ||
|
|
9781c2bb6b | ||
|
|
a7c349835c | ||
|
|
9a48eac3e6 | ||
|
|
9082c684ed | ||
|
|
84235fbc9f | ||
|
|
8accd6221a | ||
|
|
c37eebb59f | ||
|
|
5b0bd2589b | ||
|
|
231e5e50cd | ||
|
|
423b2e1c3e | ||
|
|
b6c5f7c18e | ||
|
|
74320e607f | ||
|
|
d87e3b3b5f | ||
|
|
c4153be8bc | ||
|
|
b5a16a2680 | ||
|
|
f6870da310 | ||
|
|
6c8cda5d7a | ||
|
|
7331d9320a | ||
|
|
dccdcaab96 | ||
|
|
99c6a88f90 | ||
|
|
da4f26197d | ||
|
|
02361b7c99 | ||
|
|
d6bbea05ca | ||
|
|
65d3c2ab67 | ||
|
|
327f213f56 | ||
|
|
05c24cc9e0 | ||
|
|
6e31ae52c4 | ||
|
|
7830444c87 | ||
|
|
9ace9d3bb3 | ||
|
|
a994e86ba5 | ||
|
|
e776116162 | ||
|
|
1e814af1d4 | ||
|
|
f3ed77deb0 | ||
|
|
d5245aa496 | ||
|
|
e4a62d6e90 | ||
|
|
dcc57f29c8 | ||
|
|
d87d085842 | ||
|
|
3930617518 | ||
|
|
deaf1f08a6 | ||
|
|
76ba1e4ffa | ||
|
|
62728dc562 | ||
|
|
6454c7f37d | ||
|
|
a4f73a88b7 | ||
|
|
e5658ed441 | ||
|
|
6bfaaf788f | ||
|
|
07da1c27a3 | ||
|
|
35462324bb | ||
|
|
2c1149815d | ||
|
|
c9b600d186 | ||
|
|
ca14fad3ce | ||
|
|
c4fa6557e1 | ||
|
|
1bb08bab36 | ||
|
|
cccafe86bc | ||
|
|
6fb4a52051 | ||
|
|
5f42c4bb01 | ||
|
|
37b8ac78a7 | ||
|
|
26b86f3f9d | ||
|
|
8d50d5a065 | ||
|
|
ce257b429a | ||
|
|
7e95c50f39 | ||
|
|
61ecc4d2ee | ||
|
|
391f43df95 | ||
|
|
60d8ab021b | ||
|
|
dfab8d68fd | ||
|
|
8167cb86c6 | ||
|
|
44bc689348 | ||
|
|
007db344d7 | ||
|
|
3fb2621030 | ||
|
|
39ca9a5f0d | ||
|
|
e64e8a95e0 | ||
|
|
69e888c7fe | ||
|
|
ce809c432d | ||
|
|
cdf900c6b7 | ||
|
|
d5b5b7751e | ||
|
|
351712172c | ||
|
|
f99aded40e | ||
|
|
f631e5fc1b | ||
|
|
26cee1551e | ||
|
|
82e2a40791 | ||
|
|
1c158930cd | ||
|
|
f812ea8716 | ||
|
|
3dc0a95379 | ||
|
|
f8244f582d | ||
|
|
69fbdfdc4a | ||
|
|
7d9ef9c6f8 | ||
|
|
e042d1f1ee | ||
|
|
2bb88b211b | ||
|
|
27694243f2 | ||
|
|
e6c00ec4b8 | ||
|
|
ef3272a31b | ||
|
|
b0f3ef2698 | ||
|
|
1711e7f7d9 | ||
|
|
ff6cdf3b6a | ||
|
|
28517a1999 | ||
|
|
2480d203ec | ||
|
|
af9c207ddf | ||
|
|
88045bcf4e | ||
|
|
5147710fad | ||
|
|
9c45cff09a | ||
|
|
c4983b6e18 | ||
|
|
b4848c0424 | ||
|
|
3a46b78361 | ||
|
|
75bbc6eb5f | ||
|
|
da638aea99 | ||
|
|
7c37b479db | ||
|
|
fa473c5ada | ||
|
|
1e6ce04e4f | ||
|
|
5cd5b119e7 | ||
|
|
186138cc3c | ||
|
|
98c7ca980a | ||
|
|
13736d2524 | ||
|
|
b2470b962a | ||
|
|
2a705bc6d1 | ||
|
|
a069c79c73 | ||
|
|
5d44831757 | ||
|
|
456880501b | ||
|
|
63f7ae12e1 | ||
|
|
fa69ffefd2 | ||
|
|
f78bc82cc4 | ||
|
|
dfacf6a206 | ||
|
|
b2f14c1e2e | ||
|
|
9ee9355348 | ||
|
|
0a2dbc3140 | ||
|
|
03d7a311bf | ||
|
|
f7108c0ca4 | ||
|
|
cd5119278b | ||
|
|
6ab3608a44 | ||
|
|
c0539af368 | ||
|
|
c6363140f4 | ||
|
|
187105b729 | ||
|
|
b983c0f11f | ||
|
|
c7cee33889 | ||
|
|
d9d73b8aec | ||
|
|
48455b9b72 | ||
|
|
21d72e51ef | ||
|
|
222633e462 | ||
|
|
f64c93b338 | ||
|
|
429899d6fc | ||
|
|
f1486609ab | ||
|
|
e35170d4f9 | ||
|
|
80a5c0b6d6 | ||
|
|
63f0125954 | ||
|
|
af798c0bf3 | ||
|
|
ed714c9d65 | ||
|
|
832b401630 | ||
|
|
5e7c07c98e | ||
|
|
00d2b3bf6b | ||
|
|
02c697c2f0 | ||
|
|
511051952c | ||
|
|
114b7cb4d6 | ||
|
|
a4d3ce5e00 | ||
|
|
b3095b110a | ||
|
|
dce2362cbb | ||
|
|
fb284b6fc4 | ||
|
|
c0df52b981 | ||
|
|
6782ace43d | ||
|
|
71447c932f | ||
|
|
a1b0a3f350 | ||
|
|
df864e9fed | ||
|
|
45bcc76436 | ||
|
|
6595a31048 | ||
|
|
1a6c3321a1 | ||
|
|
b21ef9ece6 | ||
|
|
9b3c3b330a | ||
|
|
46d8c5c379 | ||
|
|
f4bcb658e1 | ||
|
|
76542b53e5 | ||
|
|
088dac8fb6 | ||
|
|
4a511ca6e3 | ||
|
|
35da9c569a | ||
|
|
dec7b2a9ec | ||
|
|
ba1ed72a55 | ||
|
|
e4f70144e9 | ||
|
|
020334b37b | ||
|
|
24a98ab263 | ||
|
|
ba2ddef525 | ||
|
|
58563b99a5 | ||
|
|
9013a7d13c | ||
|
|
f36a17b46a | ||
|
|
7335ff2ce5 | ||
|
|
21c039f9b3 | ||
|
|
b20591540b | ||
|
|
8814b1ce56 | ||
|
|
f3dee5425e | ||
|
|
676b025c5b | ||
|
|
fac72d17c8 | ||
|
|
a2fa6021f5 | ||
|
|
c492e662cb | ||
|
|
ce803340b0 | ||
|
|
3e8a480ea6 | ||
|
|
4be34d20ca | ||
|
|
ea2cd893b4 | ||
|
|
a90a8c27dc | ||
|
|
9d0d72c40a | ||
|
|
56d2ac117f | ||
|
|
cb8202d891 | ||
|
|
029b0674d1 | ||
|
|
c852473a43 | ||
|
|
a4e8e20fcd | ||
|
|
fc1bee2673 | ||
|
|
acf1ef1da2 | ||
|
|
5fe120e897 | ||
|
|
41624c8dbd | ||
|
|
e8ec39db86 | ||
|
|
936d4b188b | ||
|
|
e9ab1489cc | ||
|
|
1043eb1d1b | ||
|
|
0f444da595 | ||
|
|
fda644d036 | ||
|
|
fa0fbbd29a | ||
|
|
ad8847509b | ||
|
|
627c05bf98 | ||
|
|
b82b714165 | ||
|
|
3446999109 | ||
|
|
0bfdfbd0e8 | ||
|
|
19addc1570 | ||
|
|
1f35c77a69 | ||
|
|
5973b6c85c | ||
|
|
d74dc1aa61 | ||
|
|
7b14207f81 | ||
|
|
e9eedb2241 | ||
|
|
d3a35c307a | ||
|
|
d6f87f79a6 | ||
|
|
24a1ef9f1d | ||
|
|
ae4904c7d9 | ||
|
|
b142f7c0ac | ||
|
|
934f356927 | ||
|
|
a61764960d | ||
|
|
55a376cdfd | ||
|
|
da7655ee25 | ||
|
|
0ae754fa5d | ||
|
|
ecfd2408cf | ||
|
|
44dfd43692 | ||
|
|
29876ec046 | ||
|
|
3667f504ec | ||
|
|
375b976dcd | ||
|
|
2a74e5ff64 | ||
|
|
0c623c2efc | ||
|
|
d49b3ae884 | ||
|
|
f811261507 | ||
|
|
ec701e6e56 | ||
|
|
360dc105e1 | ||
|
|
892c7c13bd | ||
|
|
3b6128a1b0 | ||
|
|
4c42a528bd | ||
|
|
0e7c5264a0 | ||
|
|
69a4fa6fa7 | ||
|
|
e6b9808beb | ||
|
|
b34330dc85 | ||
|
|
ae69fe3831 | ||
|
|
6d2dc95cc8 | ||
|
|
474be9d34b | ||
|
|
7b4dff7d84 | ||
|
|
885070a00c | ||
|
|
67b58238a0 | ||
|
|
a1c87618f6 | ||
|
|
b4cec19283 | ||
|
|
109c7e449c | ||
|
|
05ad7e4d25 | ||
|
|
1e96abebbb | ||
|
|
69da709ea2 | ||
|
|
d5f9c248e0 | ||
|
|
cc39547e33 | ||
|
|
096d24e42b | ||
|
|
2995413434 | ||
|
|
4b34dbd218 | ||
|
|
a775e63e6d | ||
|
|
3cd4affcf6 | ||
|
|
51e553039a | ||
|
|
27ff49a137 | ||
|
|
e70acf9269 | ||
|
|
1f6fce0fcc | ||
|
|
188cb8fe68 | ||
|
|
0744a126cb | ||
|
|
1c8a0d63e7 | ||
|
|
124ec594a6 | ||
|
|
90b528f9f2 | ||
|
|
2da6082166 | ||
|
|
3e61b07254 | ||
|
|
48c3716ef5 | ||
|
|
dca2d8b762 | ||
|
|
2c4bbdbfec | ||
|
|
8a08733be8 | ||
|
|
d7193bea19 | ||
|
|
c3522a05bb | ||
|
|
8a9a428f1e | ||
|
|
919d059add | ||
|
|
674c26f2d9 | ||
|
|
a6daa5cccb | ||
|
|
6f89123017 | ||
|
|
518c94d79f | ||
|
|
edf9f494e8 | ||
|
|
0db053c31f | ||
|
|
7439f082db | ||
|
|
f12f4aec4c | ||
|
|
8d95592f53 | ||
|
|
44d23f778d | ||
|
|
7a40e47e67 | ||
|
|
9401b7fc87 | ||
|
|
e510ea88f0 | ||
|
|
4c2b6c9dee | ||
|
|
cb689c576a | ||
|
|
bd2e1a8410 | ||
|
|
608fac9d68 | ||
|
|
919c0e291e | ||
|
|
23f4192cfd | ||
|
|
3391a981b8 | ||
|
|
271ce56d08 | ||
|
|
0e47ce8f1a | ||
|
|
ad924669d3 | ||
|
|
311277ae40 | ||
|
|
bad59bfb5b | ||
|
|
9b57c4753f | ||
|
|
1301e60be1 | ||
|
|
069a74f97e | ||
|
|
683ae5bcf6 | ||
|
|
af904c62e6 | ||
|
|
f2d93af973 | ||
|
|
59fb47a827 | ||
|
|
9e169e069b | ||
|
|
eed6e4aaed | ||
|
|
9829ee1666 | ||
|
|
13069db28a | ||
|
|
7058105056 | ||
|
|
df5b949f1a | ||
|
|
3929ff0a8b | ||
|
|
4910440f56 | ||
|
|
bda9045be6 | ||
|
|
a7890d46de | ||
|
|
50ab3eb341 | ||
|
|
4313b52792 | ||
|
|
4f9ecf4801 | ||
|
|
9a5efa28f5 | ||
|
|
cf900cd9d4 | ||
|
|
ef21b33ca2 | ||
|
|
799f1bd9f1 | ||
|
|
44a056095d | ||
|
|
6037b13e88 | ||
|
|
c3a34ace99 | ||
|
|
05843633a7 | ||
|
|
5198f704e9 | ||
|
|
eeee7d2b84 | ||
|
|
772c26d91a | ||
|
|
db0a78ccc3 | ||
|
|
01d764e05c | ||
|
|
0c5c1638ed | ||
|
|
a49cfb0b57 | ||
|
|
0f975d2233 | ||
|
|
ccff9ab486 | ||
|
|
f2cf7db507 | ||
|
|
2f740bbb41 | ||
|
|
1462d3d471 | ||
|
|
22d10c1ff0 | ||
|
|
8b9657b2e6 | ||
|
|
bc6f6b7fe2 | ||
|
|
8f3c8b33ba | ||
|
|
8187c79d17 | ||
|
|
2ecf7f8624 | ||
|
|
6200662bc7 | ||
|
|
d2c72264be | ||
|
|
f37f7be9c7 | ||
|
|
d05dd27971 | ||
|
|
fd478f4c70 | ||
|
|
d834ffac42 | ||
|
|
3e11175001 | ||
|
|
85a11061d0 | ||
|
|
e26cb8b0db | ||
|
|
208d7bb4dd | ||
|
|
cf1a2c9181 | ||
|
|
ba9ccc3606 | ||
|
|
83a614d645 | ||
|
|
5734c1fdee | ||
|
|
af791a45b4 | ||
|
|
11c8802b07 | ||
|
|
70d519a5a9 |
No files matched your search
@@ -1,31 +1,30 @@
|
||||
name: Problem report
|
||||
description: A game or FramePort doesn't work (FramePort → "Report a problem…" fills this in and saves a diagnostics zip).
|
||||
title: "[Problem] "
|
||||
labels: ["bug"]
|
||||
name: Game report
|
||||
description: 'A game doesn''t run right on the Frame, or FramePort itself misbehaves (FramePort fills this in: "Report a problem…").'
|
||||
title: "[Game report] "
|
||||
labels: ["game-compatibility"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please attach the **diagnostics zip** FramePort saved (drag it into the "Diagnostics" box below). It contains
|
||||
logs, the recipe and device details with personal data removed — no game files. Without the app, it can be
|
||||
read with `frameport diag inspect <zip>`.
|
||||
Please attach the **diagnostics zip** FramePort saved: drag it into the "Diagnostics" box below. It holds logs
|
||||
and device details with personal data removed, and no game files.
|
||||
- type: input
|
||||
id: game
|
||||
attributes:
|
||||
label: Game
|
||||
description: Title — package id (version, engine / XR API); empty for app problems
|
||||
description: Title and package name; leave empty for problems with FramePort itself
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: What happens?
|
||||
description: What you did, what you expected, what you saw (in the headset, if it started)
|
||||
description: What you did, what you expected and what happened
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: findings
|
||||
attributes:
|
||||
label: Launch test / triage
|
||||
description: Filled in by FramePort from the last launch test
|
||||
label: Launch test
|
||||
description: Filled in by FramePort
|
||||
- type: textarea
|
||||
id: recipe
|
||||
attributes:
|
||||
@@ -39,6 +38,6 @@ body:
|
||||
id: logs
|
||||
attributes:
|
||||
label: Diagnostics
|
||||
description: Drag the FramePort-diag-*.zip here (max 25 MB)
|
||||
description: "Drag the FramePort-diag-*.zip here (max 25 MB). If the box won't take it, submit the report and attach the zip in a comment."
|
||||
validations:
|
||||
required: true
|
||||
@@ -1 +1,8 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Questions and ideas
|
||||
url: https://github.com/spoopyghosty0/frameport/discussions
|
||||
about: Setup help, how-to questions and feature ideas go to Discussions.
|
||||
- name: Docs and FAQ
|
||||
url: https://frameport.app/docs/
|
||||
about: Install steps, game folder layouts and common questions.
|
||||
@@ -1,32 +1,32 @@
|
||||
name: Working configuration
|
||||
description: Submit a recipe that works on the Steam Frame (FramePort → game menu → "Share working config…" fills this in).
|
||||
title: "[Working config] "
|
||||
name: Working recipe
|
||||
description: 'Share a recipe that works on the Steam Frame (FramePort fills this in: game menu → "Share working recipe…").'
|
||||
title: "[Working recipe] "
|
||||
labels: ["working-config"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks! A maintainer reviews the recipe; once it's labelled `catalog-accepted`, a pull request adding it to
|
||||
`catalog/games/` is opened automatically. Please don't paste game files or links to them.
|
||||
Thanks! Once a maintainer accepts the recipe, it becomes built-in for everyone. Please don't share game files
|
||||
or links to them.
|
||||
- type: input
|
||||
id: game
|
||||
attributes:
|
||||
label: Game
|
||||
description: Title — package id (platform, version, engine / XR API)
|
||||
description: Title and package name (e.g. Lucky's Tale — com.playful.LuckysTale)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: result
|
||||
attributes:
|
||||
label: Result
|
||||
description: works or issues (+ the last headless launch test)
|
||||
description: works or issues
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: recipe
|
||||
attributes:
|
||||
label: Recipe
|
||||
description: The catalog entry (catalog/games/<package>.yaml). Don't edit the package line.
|
||||
description: Filled in by FramePort. Don't edit the package line.
|
||||
render: yaml
|
||||
validations:
|
||||
required: true
|
||||
@@ -34,16 +34,16 @@ body:
|
||||
id: environment
|
||||
attributes:
|
||||
label: Environment
|
||||
description: FramePort, tool and SteamOS versions
|
||||
description: FramePort and SteamOS versions
|
||||
- type: textarea
|
||||
id: notes
|
||||
attributes:
|
||||
label: Notes
|
||||
description: What you checked in the headset, known issues, anything unusual
|
||||
description: What you checked in the headset and any known issues
|
||||
- type: checkboxes
|
||||
id: confirm
|
||||
attributes:
|
||||
label: Confirmation
|
||||
options:
|
||||
- label: I played the game in the headset with this recipe (headless launch tests can't check the picture).
|
||||
- label: I played the game in the headset with this recipe.
|
||||
required: true
|
||||
@@ -5,6 +5,15 @@ on:
|
||||
tags: ["v*"]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dev:
|
||||
description: "Publish the bundles as the dev build (testers: Settings → Updates → Install the latest dev build)"
|
||||
type: boolean
|
||||
default: true
|
||||
notes:
|
||||
description: "What testers should try (shown before they install it)"
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
jobs:
|
||||
test:
|
||||
@@ -50,6 +59,9 @@ jobs:
|
||||
if: runner.os == 'Linux'
|
||||
run: sudo apt-get update && sudo apt-get install -y ninja-build libgtk-3-dev
|
||||
- run: uv sync --extra dev
|
||||
- name: Dev build version (next patch + .dev<run number>; not committed)
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.dev
|
||||
run: uv run --no-project python scripts/dev_version.py ${{ github.run_number }}
|
||||
- id: flet
|
||||
run: uv run python scripts/package.py
|
||||
continue-on-error: ${{ runner.os == 'macOS' || runner.arch == 'ARM64' }}
|
||||
@@ -84,8 +96,27 @@ jobs:
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
app=$(find dist -maxdepth 3 -name "*.app" -type d | head -1)
|
||||
codesign --force --deep --sign - "$app"
|
||||
# Re-signing replaces the signature, entitlements included: pass them again, or the file picker refuses to
|
||||
# open ("ENTITLEMENT_NOT_FOUND", GitHub #63). flet build writes them to Release.entitlements; the
|
||||
# PyInstaller fallback has none, so it gets the same set.
|
||||
ent=build/flutter/macos/Runner/Release.entitlements
|
||||
if [ ! -f "$ent" ]; then
|
||||
ent="$RUNNER_TEMP/FramePort.entitlements"
|
||||
cat > "$ent" <<'EOF'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0"><dict>
|
||||
<key>com.apple.security.cs.allow-jit</key><true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/>
|
||||
<key>com.apple.security.network.client</key><true/>
|
||||
<key>com.apple.security.network.server</key><true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key><true/>
|
||||
</dict></plist>
|
||||
EOF
|
||||
fi
|
||||
codesign --force --deep --sign - --entitlements "$ent" "$app"
|
||||
codesign --verify --deep --strict --verbose=2 "$app"
|
||||
codesign -d --entitlements - "$app" 2>/dev/null | tee /dev/stderr | grep -q "files.user-selected.read-write"
|
||||
|
||||
# ---- package (archives keep permissions/symlinks) + provenance ----
|
||||
- name: Package (Windows)
|
||||
@@ -119,6 +150,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
actions: write # starts the pages workflow
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: astral-sh/setup-uv@v10.2.0
|
||||
@@ -128,6 +160,12 @@ jobs:
|
||||
merge-multiple: true
|
||||
- name: Wheel (CLI install via uv tool / pipx / pip, and its self-update)
|
||||
run: uv build --wheel -o out && ls out
|
||||
- name: Videos (docs/media, rendered by the showcase workflow; frameport-<name>.mp4 → FramePort-<name>.mp4)
|
||||
run: |
|
||||
for f in docs/media/frameport-*.mp4; do
|
||||
[ -f "$f" ] || continue
|
||||
n=$(basename "$f" .mp4); cp "$f" "out/FramePort-${n#frameport-}.mp4"
|
||||
done
|
||||
- name: Checksums
|
||||
run: cd out && sha256sum * > SHA256SUMS.txt && cat SHA256SUMS.txt
|
||||
- name: Publish release
|
||||
@@ -148,3 +186,54 @@ jobs:
|
||||
echo; cat packaging/release-footer.md; } > "$notes" # short: the update dialog shows these notes
|
||||
gh release delete "$tag" --yes 2>/dev/null || true
|
||||
gh release create "$tag" out/* packaging/FramePort-selfsigned.cer --title "FramePort ${tag}" --notes-file "$notes"
|
||||
- name: Refresh the website's changelog (a release made with github.token starts no other workflow)
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh workflow run pages.yml --ref main
|
||||
|
||||
dev-release:
|
||||
# "Run workflow" with dev ticked: replaces the rolling `dev` pre-release (never shown by the automatic update check;
|
||||
# testers install it from Settings → Updates). Same assets + SHA256SUMS as a release.
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.dev
|
||||
needs: bundle
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
actions: write # starts the pages workflow
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: astral-sh/setup-uv@v10.2.0
|
||||
- id: version
|
||||
run: echo "version=$(uv run --no-project python scripts/dev_version.py ${{ github.run_number }})" >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: out
|
||||
merge-multiple: true
|
||||
- name: Wheel
|
||||
run: uv build --wheel -o out && ls out
|
||||
- name: Checksums
|
||||
run: cd out && sha256sum * > SHA256SUMS.txt && cat SHA256SUMS.txt
|
||||
- name: Publish the dev build
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
NOTES: ${{ inputs.notes }}
|
||||
run: |
|
||||
notes=$(mktemp)
|
||||
last=$(git describe --tags --abbrev=0 --match 'v*' 2>/dev/null || true)
|
||||
{ echo "Dev build **${VERSION}** from commit ${GITHUB_SHA::7} ($(date -u +%Y-%m-%d)). For testing fixes before"
|
||||
echo "the next release; you get that release as a normal update."; echo
|
||||
if [ -n "$NOTES" ]; then echo "## Please test"; echo; echo "$NOTES"; echo; fi
|
||||
if [ -n "$last" ]; then echo "## Changes since ${last}"; echo
|
||||
git log --no-merges --format='- %s' "${last}..HEAD" | cut -c1-160 | head -40; fi; } > "$notes"
|
||||
gh release delete dev --yes --cleanup-tag 2>/dev/null || true
|
||||
gh release create dev out/* packaging/FramePort-selfsigned.cer --prerelease --target "$GITHUB_SHA" \
|
||||
--title "FramePort dev build ${VERSION}" --notes-file "$notes"
|
||||
- name: Refresh the website's changelog (a release made with github.token starts no other workflow)
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh workflow run pages.yml --ref main
|
||||
@@ -1,5 +1,5 @@
|
||||
name: catalog-from-issue
|
||||
# A maintainer adds the label `catalog-accepted` to a "Working configuration" issue → this opens a PR adding the
|
||||
# A maintainer adds the label `catalog-accepted` to a "Working recipe" issue → this opens a PR adding the
|
||||
# recipe to catalog/games/. Never runs for unlabelled issues; the issue text is only read by the validating script
|
||||
# (never interpolated into shell commands). Needs Settings → Actions → "Allow GitHub Actions to create pull requests".
|
||||
on:
|
||||
@@ -40,7 +40,8 @@ jobs:
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git checkout -b "$branch"
|
||||
git add "catalog/games/$PKG.yaml"
|
||||
uv run python scripts/patch_docs.py # docs/PATCHES.md lists the catalog games per patch
|
||||
git add "catalog/games/$PKG.yaml" docs/PATCHES.md site/src/data/patches.json
|
||||
git commit -m "catalog: $PKG (from #$ISSUE)"
|
||||
git push --force origin "$branch"
|
||||
if ! gh pr view "$branch" >/dev/null 2>&1; then
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# The project page (site/, Astro) on GitHub Pages: https://frameport.app/
|
||||
# Rebuilt when the site, the docs it renders, the catalog or the showcase renders change on main, when a release is
|
||||
# published, and once a day (contributors, releases and the games board stay fresh). The games board also refreshes
|
||||
# itself from the catalog on main in the visitor's browser, so a catalog change shows before the next build.
|
||||
# One-time setup: Settings → Pages → Source: GitHub Actions; custom domain frameport.app (kept in the settings, not a
|
||||
# CNAME file: GitHub ignores that file for Actions deployments).
|
||||
name: pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'site/**'
|
||||
- 'docs/*.md'
|
||||
- 'catalog/games/**'
|
||||
- 'bootstrap/setup.sh'
|
||||
- 'docs/images/**'
|
||||
- 'docs/media/**'
|
||||
- 'docs/badges/**'
|
||||
- 'src/frameport/ui/icons/**'
|
||||
- '.github/workflows/pages.yml'
|
||||
release: # the changelog page shows every release (CI's own releases start this through workflow_dispatch)
|
||||
types: [published]
|
||||
schedule:
|
||||
- cron: '17 5 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: site
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
cache-dependency-path: site/package-lock.json
|
||||
- uses: actions/configure-pages@v6
|
||||
- run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends ffmpeg # the video tiles' short previews
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
- run: npm run build
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # the contributors list (higher API limit)
|
||||
# analytics (Settings → Secrets and variables → Actions → Variables); unset = none on the site
|
||||
PUBLIC_CF_ANALYTICS_TOKEN: ${{ vars.CF_ANALYTICS_TOKEN }}
|
||||
PUBLIC_GA_ID: ${{ vars.GA_MEASUREMENT_ID }}
|
||||
- uses: actions/upload-pages-artifact@v5
|
||||
with:
|
||||
path: site/dist
|
||||
|
||||
deploy:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- id: deployment
|
||||
uses: actions/deploy-pages@v5
|
||||
@@ -0,0 +1,107 @@
|
||||
name: showcase
|
||||
# Docs screenshots and the videos (the demo tour, the install tutorial, …), rendered from the demo library
|
||||
# (docs/showcase/, scripts/showcase/; see docs/SHOWCASE.md). Opens or updates one pull request (branch
|
||||
# showcase/update) when a picture really changed; the renders are also kept as a workflow artifact either way.
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
videos:
|
||||
description: "Videos to record too: names from docs/showcase/videos (e.g. tour install), all, or empty"
|
||||
type: string
|
||||
default: ""
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "src/frameport/ui/**"
|
||||
- "src/frameport/locales/**"
|
||||
- "docs/showcase/**"
|
||||
- "scripts/showcase/**"
|
||||
|
||||
concurrency:
|
||||
group: showcase-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
render:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
PYTHONUTF8: "1"
|
||||
PYTHONIOENCODING: utf-8
|
||||
TZ: UTC
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0 # the push's changed files decide whether the video is recorded again
|
||||
- uses: astral-sh/setup-uv@v10.2.0
|
||||
- name: ffmpeg and fonts
|
||||
run: sudo apt-get update && sudo apt-get install -y ffmpeg fonts-roboto fonts-noto-color-emoji
|
||||
- run: uv sync --extra dev --python 3.12 # the Python the renders are made with locally
|
||||
- name: Chromium for Playwright
|
||||
run: uv run playwright install --with-deps chromium
|
||||
- name: Store art cache (fetched once from the stores the app uses)
|
||||
uses: actions/cache@v6.1.0
|
||||
with:
|
||||
path: ~/.cache/frameport-showcase
|
||||
key: showcase-art-${{ hashFiles('docs/showcase/demo-library.yaml') }}
|
||||
restore-keys: showcase-art-
|
||||
|
||||
- name: Docs screenshots
|
||||
run: uv run python scripts/showcase/render_docs.py --keep showcase-out/screenshots
|
||||
|
||||
- name: Videos
|
||||
# a push records the videos whose storyboard (docs/showcase/videos/<name>.yaml) changed, or all of them when
|
||||
# the recorder changed (UI tweaks alone don't re-record ~10 MB files); Run workflow records what it's asked
|
||||
env:
|
||||
INPUT_VIDEOS: ${{ inputs.videos }}
|
||||
BEFORE: ${{ github.event.before }}
|
||||
run: |
|
||||
if [ "$GITHUB_EVENT_NAME" = workflow_dispatch ]; then
|
||||
read -ra names <<< "$INPUT_VIDEOS"
|
||||
if [ ${#names[@]} -eq 0 ]; then echo "no videos asked for"; exit 0; fi
|
||||
[ "${names[0]}" = all ] && names=()
|
||||
uv run python scripts/showcase/record_video.py "${names[@]}" --work showcase-out/videos
|
||||
else
|
||||
uv run python scripts/showcase/record_video.py --changed-since "$BEFORE" --work showcase-out/videos
|
||||
fi
|
||||
|
||||
- name: Keep the renders
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: showcase
|
||||
path: |
|
||||
showcase-out/screenshots
|
||||
showcase-out/videos/*/video.json
|
||||
docs/media
|
||||
docs/images/tour-teaser.webp
|
||||
if-no-files-found: ignore
|
||||
retention-days: 14
|
||||
|
||||
- name: What changed
|
||||
id: diff
|
||||
run: |
|
||||
git status --short -- docs/images docs/media
|
||||
if [ -n "$(git status --porcelain -- docs/images docs/media)" ]; then
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"; fi
|
||||
- name: Pull request
|
||||
if: steps.diff.outputs.changed == 'true' && github.ref == 'refs/heads/main'
|
||||
uses: peter-evans/create-pull-request@v8.1.1
|
||||
with:
|
||||
branch: showcase/update
|
||||
delete-branch: true
|
||||
add-paths: |
|
||||
docs/images/*
|
||||
docs/media/*
|
||||
commit-message: "Docs: update screenshots and videos"
|
||||
title: "Update docs screenshots and videos"
|
||||
body: |
|
||||
Rendered by the `showcase` workflow from the demo library (docs/showcase/demo-library.yaml) with a
|
||||
pretend Frame: only pictures that visibly changed are included. Look them over before merging (the
|
||||
run's `showcase` artifact has every render).
|
||||
|
||||
Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
+11
@@ -19,3 +19,14 @@ CLAUDE.local.md
|
||||
# outputs of `frameport report` / `parity` / ruff
|
||||
REPORT.md
|
||||
.ruff_cache/
|
||||
.claude/worktrees/
|
||||
|
||||
# project page (site/)
|
||||
site/node_modules/
|
||||
site/dist/
|
||||
site/.astro/
|
||||
site/public/media/
|
||||
site/src/assets/media/
|
||||
site/media-hq/
|
||||
site/public/s
|
||||
site/public/setup.sh
|
||||
@@ -10,8 +10,20 @@ Read `docs/PLAYBOOK.md` (symptom → fix) before debugging a game, and `docs/FRA
|
||||
## Layout
|
||||
- `src/frameport/` — Python package. `pipeline.py` is the API the CLI (`cli.py`) and GUI (`ui/`, Flet 1.0) share.
|
||||
- `ui/` — `app.py` shell (sidebar with Frame connection + activity cards, routing, actions, 30 s connection poll),
|
||||
`theme.py` (dark design tokens; change colours/spacing only there), `components.py` (pill, card, callout,
|
||||
status_row, art_fill, confirm, `update()` = safe update: in Flet 1.0 reading `.page` of an unmounted control
|
||||
`theme.py` (design tokens; change colours/spacing only there. Dark-only colour themes (owner: no light mode):
|
||||
built-ins `portal` (default), `portal_oled` (true black) and `original` (the old violet; alias `classic`). Portal =
|
||||
the logo's dichotomy: orange ACCENT = main action + selection, blue SECONDARY = secondary (outlined) buttons,
|
||||
progress, switch tracks, PC; `dual` themes add the blue→orange sidebar edge, two-colour wordmark (`C.wordmark`) and
|
||||
selected-tab fade (`C.portal_gradient`). Installable theme files `<data>/themes/*.json` (docs/THEMES.md: name,
|
||||
base, dual, any subset of `T.TOKENS`; refused if light/unreadable, `T.ThemeError`), Settings → Appearance: cards
|
||||
switch live via `app.restyle()` (rebuilds the shell, drops cached views; setting `ui.theme`), "Install theme
|
||||
file…", "Copy this theme as a file", bin icon removes. Read tokens as `T.NAME` when building — never as a default argument or module-level constant (they
|
||||
keep the import-time theme; module maps refill through `T.on_change`, `tests/test_theme.py` checks defaults)),
|
||||
`glyphs.py` + `icons/` (the logo `logo.svg`/`logo-solid.svg` = a monitor that comes out of a tilted portal as the
|
||||
Frame, and 24 px line glyphs `fp:frame|port|patch|container|test|pc|sync|live|shot|keys|recipe|link`, copied into
|
||||
the assets dir and shown as tinted `ft.Image`s: pass `G.FRAME` … wherever an icon goes, `C.as_icon` / the button
|
||||
helpers handle both kinds; `src/assets/icon.png` = the bundles' app icon), `components.py` (pill, card, callout,
|
||||
checklist, art_fill, confirm, `update()` = safe update: in Flet 1.0 reading `.page` of an unmounted control
|
||||
raises), `jobs.py` (background FIFO job queue, one at a time, cancel via Reporter; no Flet), `views/`
|
||||
(library: search/filters/tags/sort as pure tested helpers; game: hero + one-click install, patches under
|
||||
"Customize"; frame: device + readiness + installed, or connect wizard; files: the Frame's file manager (persistent
|
||||
@@ -21,8 +33,69 @@ Read `docs/PLAYBOOK.md` (symptom → fix) before debugging a game, and `docs/FRA
|
||||
the "Send files" dialog, game menu → "Add videos & files…" = `go("files", pkg)`; multi-select bar (download/
|
||||
delete); drag-and-drop from the OS via the `flet-dropzone` extension (Apache-2.0, Flutter `desktop_drop`), which
|
||||
only a `flet build` bundle contains: `files.dropzone_available()` keeps it out of source runs and the PyInstaller
|
||||
fallback, which would show an unknown control); settings; welcome; activity panel).
|
||||
fallback, which would show an unknown control); screenshots (Steam screenshots on the Frame, `install/screenshots.py`
|
||||
+ agent v46 `list_screenshots`/`delete_screenshots`: the Frame files every headset shot under SteamVR 250820, so
|
||||
launch.sh logs `start/end <unix>` to `<anchor>/plays.log` (upgrade_launchers adds it; Proton launchers exec → start
|
||||
only) and shots are matched by time; thumbnails cached in `<data>/screenshots-cache/<frame>/`, shown by asset URL;
|
||||
delete leaves screenshots.vdf alone (Steam rewrites it at exit); game menu → "Screenshots" = `go("screenshots",
|
||||
pkg)`; Copy image (card hover button, right-click, viewer) = `core/clipboard.copy_image` (Windows/WSL: PowerShell
|
||||
bitmap + file via %TEMP%, macOS osascript PNG, Linux wl-copy/xclip; Flet's set_image only in web mode)); live view (`views/live.py` + `install/livestream.py`: the Frame's built-in SteamVR "headset view" webcam
|
||||
(`steamvr-v4l2cam.service` → v4l2loopback "SteamVR" /dev/video99, see docs/FRAME_RUNTIME.md) + the default
|
||||
output's pulse monitor (sound) → `fp_venc` (`native/venc`, own clean-room V4L2 driver of the Frame's iris
|
||||
hardware encoder: RGB24→NV12 box downscale with NEON, H.264 CBR, one frame per slot of an even fraction of the
|
||||
panel rate read from DRM, e.g. 96 Hz → 32 fps; stdin `k` = keyframe, EOF = stop; `--probe` / `--selftest`;
|
||||
artifact `linux-arm64-bin/`, synced to `~/.local/share/frameport/bin` by sha256 at stream start, no agent change)
|
||||
piped into ffmpeg (`-c:v copy` + AAC), else the old ffmpeg/x264 30 fps path (qualities scale down only; "full" =
|
||||
SteamVR's size; stderr `live: encoder=… fps=…` → status line) →
|
||||
fragmented MP4 on an SSH exec channel's stdout (stdin EOF stops it) → relay on 127.0.0.1 (a new viewer asks
|
||||
the hardware encoder for a keyframe and waits for it, else gets init + fragments since the last keyframe of the
|
||||
*video* track) → player page
|
||||
`install/live_player.py` (MSE; starts muted as browsers require, "Sound on" button; 0.3 s cushion, catches up at
|
||||
1.1x, seeks only when >2 s behind: seeking to the very edge starved it) opened
|
||||
in the user's default browser: Flet can't show video outside `flet build` bundles; the stream outlives the tab and
|
||||
stops on disconnect/window close); monitor (`views/monitor.py` + `frame/monitor.py` + agent v62 `_monitor`: one
|
||||
JSON sample per tick over an SSH exec channel while the tab is shown (stopped in go/disconnect/on_close, the
|
||||
agent ends at EOF); sources and costs in docs/FRAME_RUNTIME.md "Monitoring sources"; game card (fps from
|
||||
FrameBridge pacing; End game = Steam's Exit game, then cmd_stop), tiles with `C.Sparkline` (Flet canvas, no charts
|
||||
extension), details, a pooled process table (Game / Steam & SteamVR / All; right-click: end / force kill / end
|
||||
game; MON_CRITICAL needs force, MON_NEVER is refused)); settings; welcome; activity panel). Files, Screenshots and the Library share right-click menus
|
||||
(one `ft.ContextMenu` per view, filled on right-click; on one of several selected items they act on the whole
|
||||
selection, `C.menu_targets`) and click-and-drag multi-select (`C.DragSelect`: pan start/end on the area + item
|
||||
hover events, which Flutter also sends with the button held; Flet can't report item positions, so no rubber band).
|
||||
Selection bars sit below the list: above it, their appearing shifted the items mid-drag. Async handlers (they
|
||||
await a FilePicker) must be coroutine functions or go through `page.run_task`: Flet doesn't await a lambda's
|
||||
coroutine (the Files row Download button silently did nothing). `ui_smoke.py --fake-frame --gestures` drives real
|
||||
mouse drags/right-clicks.
|
||||
Live Frame data comes from one app-owned `app.monitor_hub` (`frame/monitor_hub.py`): subscribers name modules +
|
||||
interval, the hub runs one agent `_monitor` stream (union of modules, fastest interval; per-module collection
|
||||
needs agent v73), reconnects after a lost stream and stops it when nobody subscribes; `_poll` retries an "error".
|
||||
Subscribers: "monitor" (the Monitor tab, everything, only while shown) and "card" = the sidebar's live Frame card
|
||||
(`ui/frame_card.py` helpers: battery ring, "now playing" row with fps + 2-min sparkline → click opens Monitor;
|
||||
games + battery every 5 s, paused while a job's stage is "Upload…", only while connected and setting
|
||||
`ui.live_frame_card` (Settings → Appearance, default on) is on; else the battery comes from the 30 s poll).
|
||||
ui_smoke injects `FakeMonitorSession` as the hub's `session_factory`.
|
||||
User tags live in library entries (`tags`), filters in library setting `ui.library`.
|
||||
Easter eggs (`ui/easter.py`, owner's wish; never mentioned in the UI or docs): seven quick clicks on the sidebar
|
||||
logo (the *wordmark's* portal glows + rings, a cover is tossed out of it and falls across the window on a random
|
||||
arc: `toss_path`, `page.overlay` at the computed window position `_wordmark_portal`; the logo's portal in
|
||||
themes without one), the Monitor's fps number has a mood tooltip (above the number, clear of the pointer) and
|
||||
2.5 minutes within 0.5 fps of target (`PERFECT_SECONDS`) make the game card glow and pulse blue/orange for 5 s,
|
||||
the number grow in the portal gradient and the sparkline take it; the "Perfect pacing" pill + line stay until the
|
||||
rate slips (`PerfectPacing.done`, `_show_perfect`, `Sparkline.portal`), holiday badges on the logo that dance on
|
||||
hover and shower the window on click (`HOLIDAY_MOTION`; `SHOWERS`/`shower_plan`: New Year confetti, winter snow,
|
||||
Valentine hearts, Pi digits, clovers, hopping eggs, bouncing pumpkins; `holiday`: Valentine's
|
||||
heart, Pi Day, St Patrick's clover, Easter Sat-Mon egg (computus), April 1 = the logo upside down until hovered,
|
||||
Halloween pumpkin, Dec 31-Jan 1 party popper, else Dec 20-Jan 2 snowflake; on those days the logo's box grows to
|
||||
hold the badge: Flutter only hit-tests inside a control's bounds; `FRAMEPORT_TODAY` pretends a date),
|
||||
Library search "frameport" (logo spins, "That's me!"), Type on Frame "hello" (keys still go to the Frame; a
|
||||
headset peeks up from the window's corner and waves), the battery ring (only the ring, with a glow behind
|
||||
it; not the percentage) empties at once and spins while its bar refills, then pulses, on reaching 100 % plugged (`charged_now`, `ring_pulse`, `ring_glow`), a long upload (>2.5 min, `HOP_AFTER`) makes the transit cover hop, the Live view
|
||||
tab shows a blinking ON AIR sign while streaming
|
||||
(`app.sync_on_air`), install milestones (confetti +
|
||||
message at the 1st/10th/100th *different* game: settings `fun.installed` = game ids, `fun.milestones` = shown;
|
||||
each milestone shows once ever, recorded before it's shown; updates/reinstalls never count; the first use seeds
|
||||
from games already on a Frame and marks passed milestones shown). Setting `ui.easter_eggs` (no UI) turns them
|
||||
off; the showcase demo library has them off (steps hook `easter_eggs` for egg demos). Reduce motion: no animation.
|
||||
**Performance rules** (the app froze before): never put image bytes in controls — artwork is served by URL from the
|
||||
GUI assets dir (= user data dir; `ft.run(assets_dir=…)`), as thumbnails (`artwork/thumbs.py`, Pillow); the
|
||||
library view is persistent, streams cards in batches from a background thread and filters by toggling visibility;
|
||||
@@ -30,16 +103,45 @@ Read `docs/PLAYBOOK.md` (symptom → fix) before debugging a game, and `docs/FRA
|
||||
**Never recreate clickable controls on progress ticks** (sidebar, activity tiles): update their properties —
|
||||
replacing them 5×/s swallowed clicks (couldn't leave the Library during an upload).
|
||||
Labelled switches: `C.switch(label, …)` (Material's default label colour is dark on our dark theme).
|
||||
Cover cards (Library grid + shelf): `library.card_hover`/`hover_motion` (lift, art zoom inside the clipped frame,
|
||||
radial `scrim`, accent-tinted shadow) and `C.CoverButton` (frosted round button, accent under the pointer; Play =
|
||||
blue then orange ring ripple + "Starting on Frame…" pill for STARTING_S; its `.status` layer must sit *below*
|
||||
the button in the Stack, else it takes the clicks); the game page's Play turns into "Starting on …" with a spinner
|
||||
(`GameView._starting`). Reduce motion: no lift/zoom/rings. Filled buttons use a glyph's `-solid` variant
|
||||
(`glyphs.solid`, e.g. `frame-solid.svg`: the outline glyph faded into the orange).
|
||||
Empty states (`C.empty_state(..., features=[(icon, heading, sentence)])`): with features the portal is bigger, a
|
||||
soft portal light (`C.portal_glow`) fills the screen and `C.feature_row` says what the screen will show (the
|
||||
not-connected Files/Screenshots/Live/Type/Monitor tabs and the empty Library); the idle Live view is a framed
|
||||
"screen" with its own Start button + `live_features()`, Type on Frame shows `keyboard_tips()` under the box.
|
||||
Setup checklists (Frame → Ready to play, Settings → Tools / This PC) = `C.checklist([C.Check(ok, title, detail,
|
||||
help, fix=(label, icon, handler), extra=control)])`: ok True/False/"warn"/None (= spinner); the fix button shows
|
||||
inline only while an item isn't ready — wire existing actions only. Motion: `app.body` holds an
|
||||
`ft.AnimatedSwitcher` (`app._show_view`): a new route gets a new wrapper (150 ms fade), a same-route redraw swaps
|
||||
the wrapper's content (no animation; persistent views just remount); the wordmark's portal (`wordmark().data`)
|
||||
pulses once when frame_state turns "connected"; both off with setting `ui.reduce_motion` (Settings → Appearance
|
||||
"Reduce motion", `app.reduce_motion`). Settings has a fixed index column (`settings.SECTIONS` order, Appearance
|
||||
first, Remove FramePort last): entries scroll the section column via `scroll_to(scroll_key=…)` (sections wrapped
|
||||
in Containers with `ft.ScrollKey`), the header stays; `app.settings_view.show_section(key)` (ui_smoke
|
||||
`settings-index`).
|
||||
Help hints: wording for non-obvious terms lives in `ui/help.py` (`HELP`); show it with `C.help_icon(key)` or the
|
||||
`help=` argument of `section`/`status_row`/`kv`, tooltips via `C.tip()` (wraps). Game actions for the Library
|
||||
right-click menu (one `ft.ContextMenu` around the grid, filled on right-click) and the game page's "…" menu come
|
||||
from `app.game_actions()`. Picking art (`sources.apply_choice`) downloads into a staging dir and keeps the old
|
||||
`help=` argument of `section`/`kv` (`C.Check` help key), tooltips via `C.tip()` (wraps). Game actions for the Library
|
||||
right-click menu (one `ft.ContextMenu` around the grid, filled on right-click; short: `menus.quick_menu`, key actions
|
||||
+ "More actions…" = the game page with its full "…" menu opened from code, `app.open_game_menu`) and the game
|
||||
page's "…" menu (`menus.menu_sections` quick=False, sectioned) come from `app.game_actions()`. Picking art (`sources.apply_choice`) downloads into a staging dir and keeps the old
|
||||
art if nothing came back; "Update Steam art on Frame" re-sends the art set to the game's anchor (agent ≥ 12).
|
||||
Patch descriptions/reasons describe the general case, naming games only as "e.g. …".
|
||||
- Installs: queued/cancelled/failed ones are remembered (library setting `ui.installs`) → Library "Resume" bar;
|
||||
uploads are interruptible (Cancel checked per MiB) and resumable (big files via SFTP `.part` append, small files
|
||||
streamed in tar batches; the agent counts files already in `incoming/`). Multi-select in the Library queues
|
||||
installs after asking every needed question up front. Failures end in one pop-up (Resume / Uninstall / log).
|
||||
- Other drives (GitHub #90, agent v63): anchors stay in ~/Applications/quest-frame, a game's files may live in
|
||||
`<mount>/FramePort/<pkg>` (`deployment.json` base). Agent `drives` (/proc/mounts, /run/media + Steam library
|
||||
drives; vfat/exfat/ntfs/read-only refused), prepare*/finalize_linux `dest` (unmounted = error, never a fallback;
|
||||
installed games keep their base), `move` (detached systemd-run + `move_status`; cp -a under podman unshare,
|
||||
count+bytes check, symlink retarget, launch.sh: Quest app_dir line / Linux+PC VR rewritten from the record's
|
||||
`launcher` field, else text swap), list_installed `drive`/`drive_missing` (install_state keeps such games
|
||||
"installed"). PC: `install/drives.py`, library setting `install.drive` (Frame page → Storage), game menu
|
||||
"Move to…" (job kind tool-frame), CLI `frame drives`/`frame move`/`install --dest`. Untested on the device.
|
||||
- **PC VR repacks are pre-patched to run directly** (proven: Rick and Morty, Vader Immortal run when the exe is
|
||||
launched directly; Revive breaks them). So Rift games default to `as_is` = install the copy unchanged and launch
|
||||
the exe directly (`pcvr.xr_timefix` for the Frame OpenXR-1.1→1.0 fix, `pcvr.no_crash_reporter` for Unreal).
|
||||
@@ -52,7 +154,11 @@ Read `docs/PLAYBOOK.md` (symptom → fix) before debugging a game, and `docs/FRA
|
||||
doesn't do). `VD.bat` is Virtual Desktop's launcher: ignore it except as an exe-location hint. Library migration
|
||||
`rift_run_direct` resets existing recipes.
|
||||
Auto launch-test is skipped on PC installs (it would start the game on the user's desktop). Launch tests collect the Unreal
|
||||
game log + crash summaries from the Proton prefix; triage `unreal-crash`. Lies Beneath via Proton without Revive
|
||||
game log + crash summaries from the Proton prefix; triage `unreal-crash`. Agent v67 adds Unity's logs (LocalLow/<Company>/<Product>/Player(-prev).log via `<Name>_Data/app.info`,
|
||||
`output_log.txt`, Temp/…/Crashes/*/error.log; `unity_logs`) to launch tests and diagnostics; triage `unity-vr-init` /
|
||||
`unity-crash`. A catalog recipe verified with another build (catalog `xr` ≠ the build's) keeps the build's own
|
||||
`pcvr.launch_args` (`engine._other_build`; GitHub #105: SUPERHOT VR's Oculus+OpenVR build needs `-vrmode OpenVR`,
|
||||
the catalog's is the OpenXR build); Electron launchers next to a game rank −30 (`rift.is_electron`). Lies Beneath via Proton without Revive
|
||||
crashed (UE 4.23 "Unhandled exception").
|
||||
- Rift scanning: `sources/rift_dump.scan` = the scanned folder's subfolders are games (one per folder; a folder is a
|
||||
game if all candidate exes sit under one child), recursing into collections; `analysis/rift.py` walks once,
|
||||
@@ -76,6 +182,11 @@ Rick and Morty runs on the Frame via its catalog recipe (OpenVR, no Revive),
|
||||
auto-start SteamVR on Play (`winhost.start_steamvr`); the Frame launcher always uses `/openxr`. UI (`game.py`
|
||||
where()) states per-game where it runs; installing an Oculus game on the Frame shows a warning. Migration
|
||||
`rift_frame_native` re-analyzes + re-derives existing entries.
|
||||
- Own artwork (0.8.0): game menu → "Use your own artwork…" (also from the Find artwork dialog) =
|
||||
`views/art_dialog.show_custom_art_dialog`: one slot per kind (portrait/landscape/hero/logo/icon), FilePicker →
|
||||
`sources.apply_custom` (Pillow check, ≥64 px, ≤40 MB, scaled to ≤3840 px, PNG if alpha/logo/icon else JPEG,
|
||||
replaces only that kind + its thumbnails, drops the generated cover/banner, writes `.picked` = "custom") /
|
||||
`remove_custom`; Steam shapes are still composed from what exists (`steam.py` PREFER).
|
||||
- Art for Rift games: `artwork/sources.py` — Quest version package (OculusDB packageName, exact name or +
|
||||
"Unplugged"-type suffix, never sequels) → Meta art; OculusDB square cover; Steam (exact names only); exe icon.
|
||||
- Store details (`artwork/details.py`, entry `details`): OculusDB (description, genres, publisher, website; by Quest
|
||||
@@ -86,7 +197,28 @@ Rick and Morty runs on the Frame via its catalog recipe (OpenVR, no Revive),
|
||||
set: the name on a colour from the title hash + the APK icon, `steam_set_for`; 2D Android apps get no store lookups) and tags: how it runs, the original platform
|
||||
(Meta Quest / Oculus Rift), genres, user tags — merged with tags set in Steam (non-Steam shortcuts can't hold a
|
||||
description).
|
||||
- Linux apps (GitHub #31, library kind `linux`, `linux.<slug>`; x86_64 builds run through FEX (app 3127680, no SLR:
|
||||
RootFS /usr/share/guestos/fex-mesa from the OS image; needs `STEAM_COMPAT_DATA_PATH`, see docs/FRAME_RUNTIME.md):
|
||||
agent v61 `linux_x86_tools`/`pick_tool`, `install_proton`/`proton_status` `kind: linux_x86`, `finalize_linux
|
||||
x86_64`, `installer.ensure_proton(kind=)`; an arm64 program wins over an x86_64 one): GUI = Add
|
||||
games → "Add a Linux app…" /
|
||||
"…folder…" (`app.add_linux` job → `pipeline.add_linux_app`); game page `linux_summary` (program + Change…, AppImage,
|
||||
OpenXR, source) instead of recipe/patches, `C.missing_libraries` callout (Frame deployment, else last install);
|
||||
no Analyze/Rebuild/recipe/share/Game settings actions, Frame only; platform "Linux" badge + library filter; Steam
|
||||
tags "Linux app on Frame"/"Linux"; `_follow_catalog` skips them; local files of a lone AppImage = the file only.
|
||||
Desktop Mode entries (GitHub #84, agent v63): finalize_linux writes `frameport-<slug>.desktop` to
|
||||
~/.local/share/applications (+ ~/Desktop if it exists; `X-FramePort-Package` marks ours), launch.sh with
|
||||
`FRAMEPORT_DESKTOP=1` skips the Steam-parent watchdog and Steam's display; ensure_host_fixes refreshes entries
|
||||
(older installs, stale ones removed), uninstall/purge remove them. Per app: library entry field `desktop_entry`
|
||||
(default on; patches don't apply to Linux apps) → game page switch → agent `desktop_entry`. Untested on device.
|
||||
- Scanning: SideQuest backups (`<package>/<time>/apk|obb|data`) and AXRB downloads (`AXRB/<app id>/<binary id>/base.apk`
|
||||
+ OBBs/assets; its `patched/`/`*-axrb.apk` PC builds are skipped) are recognised (`sources/quest_dump.py`, FAQ).
|
||||
- Quest/Rift twins stay separate entries, shown and named in Steam "Title (Quest)"/"(Rift)" (`core/titles.py`).
|
||||
- Rename… (game menu "Name and artwork", Library right-click; CLI `frameport rename <pkg> "<name>"|--reset`):
|
||||
`pipeline.rename_game` sets `title` + `title_locked` (+ `auto_title` = the automatic name for reset; rescans, Rift
|
||||
store matches and links then only update `auto_title`; a locked name gets no twin suffix); `sync_title` → Frame
|
||||
agent v77 `rename` (deployment.json title, same shortcut appid: upsert matches by Exe) + art + one Steam restart, PC
|
||||
`PcReviveTarget.rename`; an unreachable Frame → `steam_name_stale` (game page "Update name on Frame", next install).
|
||||
- `Recipe.as_is` = install unchanged (pre-patched libraries): `pipeline.prepare_as_is`; auto for APKs that already
|
||||
contain FrameBridge (`frame_patched`). For Rift it changes nothing (the dump is never modified; the Frame copy
|
||||
still gets launch fixes like the crash-reporter rename — `-nocrashreports` alone doesn't stop UE 4.23) — it does
|
||||
@@ -101,6 +233,10 @@ Rick and Morty runs on the Frame via its catalog recipe (OpenVR, no Revive),
|
||||
- `patches/` — **the unit of modularity**. `base.py` (Patch interface, registry), `overport.py` (overport CLI patch ids,
|
||||
discovered dynamically via `overport patches`), `frame/*.py` (one module per Frame fix), `settings.py` (FrameBridge
|
||||
adapter keys + device files as patches). Add a patch = add a module that calls `register(...)`.
|
||||
`upstream.py`: upstream fixes that replace a workaround per build (a probe finds the fix in OVRPort's output →
|
||||
the build leaves the workaround out, `build.superseded`; recipes unchanged). Registered: `ovrport.haptic_envelope`
|
||||
(→ `adapter.haptic_fix`, `frame/haptic_envelope.py`) and `ovrport.microphone_stream` (→ `frame.ovr_microphone`),
|
||||
both fixed in OVRPort runtime 3.4.3-aa54c3f (ovrport/app#73; haptics owner-verified with Lucky's Tale 2026-10-07).
|
||||
- `analysis/` — APK/ELF inspection (`detect.py`), `elf.py` (pyelftools reads; own DT_NEEDED writer), `stubgen.py`
|
||||
(generates the ovr_* stub .so without a compiler).
|
||||
- `apk/` — `axml.py` (binary manifest editor), `workspace.py` (staged zip edits), `sign.py` (apksigner; it aligns too).
|
||||
@@ -120,8 +256,12 @@ Rick and Morty runs on the Frame via its catalog recipe (OpenVR, no Revive),
|
||||
- `diag/` — user feedback without tokens (docs/DIAGNOSTICS.md): `redact.py` (every file/issue text: IPs, hosts,
|
||||
home dirs, Steam ids, dump folders → placeholders), `bundle.py` (redacted diagnostics zip; agent v21
|
||||
`collect_diag`; `frameport diag collect|inspect|report`), `issue.py` (prefilled GitHub issue-form links, ≤7.5k
|
||||
chars). "Share working config" → `working-config.yml` issue → maintainer label `catalog-accepted` →
|
||||
`catalog-from-issue.yml` workflow (`scripts/catalog_from_issue.py` validates) opens a catalog PR. App log:
|
||||
chars; `*_links` = `IssueLinks(form, plain, body)`: body = the Markdown the form writes (`### <label>`, recipe in
|
||||
a ```yaml block; labels checked against the templates), plain = `?title=&body=` link. GitHub #167: some (Flatpak)
|
||||
browsers opened the form with empty fields → `app.open_issue` copies the body to the clipboard and offers "Form
|
||||
empty? Open a plain issue"; keep the templates valid YAML, quote `: ` in descriptions). "Share working config" →
|
||||
`working-config.yml` issue → maintainer label `catalog-accepted` (a plain issue needs `working-config` added by
|
||||
hand) → `catalog-from-issue.yml` workflow (`scripts/catalog_from_issue.py` validates) opens a catalog PR. App log:
|
||||
`core/applog.py` (`<data>/logs/app.log`, finished GUI jobs in `<data>/logs/jobs/`).
|
||||
- Self-update (docs/ARCHITECTURE.md "Self-update", docs/INSTALL.md "Updating"): `_version.py` = the only version
|
||||
(`frameport.__version__`; pyproject reads it via hatch `dynamic`; app log, diagnostics, User-Agent, Settings use
|
||||
@@ -135,9 +275,15 @@ Rick and Morty runs on the Frame via its catalog recipe (OpenVR, no Revive),
|
||||
signed, `ready.json`); `apply()` writes + starts detached `apply.ps1`/`apply.sh` (wait for pid → Windows: copy over,
|
||||
replaced files kept in `updates/<ver>/previous` because the zip has no top folder; macOS/Linux: mv to `.old` + swap,
|
||||
rollback, `xattr -dr` quarantine → relaunch; `<data>/logs/update.log`). `ui/updater.py`: background check (10 s,
|
||||
then 6 h), sidebar card (hidden control, toggled), Library bar (`library_bar`), notes dialog (`ft.Markdown`), job
|
||||
then 6 h), sidebar card (hidden control, toggled), Library bar (`library_bar`), changelog dialog, job
|
||||
`app-update` that restarts only when no other job is active, `apply_pending_at_start()` in `main()` for
|
||||
"Install updates automatically"; Settings → Updates. CLI: `--version`, `frameport update [--check (exit 10)]
|
||||
"Install updates automatically"; Settings → Updates. Changelog: `updates.fetch_changelog` (releases list,
|
||||
`app-releases.json`, same 6 h cache, offline → cached / []) → `ChangelogEntry` (`whats_new()` keeps only the tag
|
||||
message's "What's new" part: CI's intro, update hint and release-footer.md stripped); `between`/`changelog_for`
|
||||
(installed < v ≤ offered, dev entry only when on/choosing dev, skipped versions included), `recent_history`;
|
||||
the update/dev dialogs show one collapsible section per version (`_changes_column`, newest open); after an update
|
||||
`pending_news` (setting `update.last_seen_version`; first start only records it; offline → retried next start)
|
||||
→ Library "was updated" bar → "See what's new…"; Settings → Updates "What's new…" = recent history. CLI: `--version`, `frameport update [--check (exit 10)]
|
||||
[--yes]`, once-a-day stderr hint read from the cache only (`refresh_cache()` in a daemon thread writes no
|
||||
settings, to avoid read-modify-write races). `scripts/update_smoke.py <archive>` runs the real extract + swap script
|
||||
(no relaunch) — CI runs it on all three OS with the archive it just built.
|
||||
@@ -146,8 +292,18 @@ Rick and Morty runs on the Frame via its catalog recipe (OpenVR, no Revive),
|
||||
by hand: the "Update now" button path (same apply(), called from the running app). macOS: CI smoke only.
|
||||
- `agent/frameport_agent.py` — runs **on the Frame** (python3 stdlib only), JSON over SSH. Owns the install layout,
|
||||
launch.sh template, Steam shortcuts (binary VDF), launch tests. Bump `AGENT_VERSION` when changing it.
|
||||
- `bootstrap/bootstrap.sh` — one-time Frame setup served by the pairing server (sshd, app key, avahi service, Lepton).
|
||||
- `catalog/games/<package>.yaml` — 38 recipes (34 verified 2026-09-28; Deadpool VR, 4XVR, NEX Player and AC Nexus's
|
||||
- `bootstrap/bootstrap.sh` — one-time Frame setup served by the pairing server (app key, podman fix, Developer Mode, Lepton).
|
||||
`bootstrap/setup.sh` — the setup URL (`curl -fsSL https://frameport.app/s | bash`, copied to the site by
|
||||
`site/scripts/sync-media.mjs`): finds the PC (stdlib mDNS on 5353 for `_frameport-pair._tcp`, which the pairing
|
||||
server announces while open; else the USB address and a /24 scan of `/ping`), `/hello` → the user clicks Allow (4
|
||||
digits on both sides, `ask_digits`) → `/wait` hands over the code → runs bootstrap.sh exactly like the typed line.
|
||||
Checked on the dev Frame 2026-10-10 (mDNS and scan paths) with a stub bootstrap; tests/test_setup_url.py.
|
||||
Auto-pairing (`frame/autopair.py`, `app._start_auto_pair`, setting `frame.auto_pair`, default on; off with
|
||||
FRAMEPORT_HOME/FRAMEPORT_NO_AUTO_PAIR): while no Frame is connected, Frames in Developer Mode (devkit mDNS, else a
|
||||
30 s scan of the /24s for :32000/login-name, deduped by host key) are connected if FramePort's key works, else
|
||||
offered the key via devkit.register every 4 s (refused at once until Pair new host is open; 45 s back-off after an
|
||||
unanswered request). This WSL dev PC never hears the Frame's mDNS (Hyper-V firewall): the scan finds it.
|
||||
- `catalog/games/<package>.yaml` (installed apps also fetch these from GitHub `main`, see "Catalog updates") — 38 recipes (34 verified 2026-09-28; Deadpool VR, 4XVR, NEX Player and AC Nexus's
|
||||
90 Hz default confirmed later by the owner); `catalog/triage.yaml` — log signatures → fixes.
|
||||
- `native/` — sources of the prebuilt binaries in `artifacts/` (adapter, VrApi bridge patches, GL shim, stubs).
|
||||
`native/build.py` rebuilds them with NDK r27c (downloaded on demand into `native/.cache`, git-ignored; uses
|
||||
@@ -167,16 +323,33 @@ Games tests: `pytest -m games` (FRAMEPORT_GAMES=<downloads dir>); on-device chec
|
||||
native layer test: `FRAMEPORT_NATIVE_TESTS=1 pytest -m native` (compiles with the NDK, ~2 min on NTFS).
|
||||
Repo is on an NTFS drive (`core.fileMode=false`); line endings are LF (`.gitattributes`).
|
||||
- `FRAMEPORT_HOME=<dir>` isolates all app data (tests use it); `FRAMEPORT_JAVA/_OVERPORT_JAR/_APKSIGNER_JAR` override
|
||||
managed tools. Real app data (WSL): `~/.local/share/frameport` (tools, overport workspace + game keystores, library,
|
||||
artwork, frames.json, the app's SSH key which the dev Frame authorizes).
|
||||
managed tools. **The owner's real app data is the Windows copy's: `%LOCALAPPDATA%\FramePort`** (= `/mnt/c/Users/
|
||||
<user>/AppData/Local/FramePort`; tools, overport workspace + game keystores, library, artwork, frames.json, the app's
|
||||
SSH key which the dev Frame authorizes; moved from WSL's `~/.local/share/frameport` on 2026-10-11, which is now only
|
||||
a stale backup). The desktop shortcut "FramePort (dev)" runs the source checkout natively on Windows
|
||||
(`%LOCALAPPDATA%\FramePort-dev\launch-frameport-dev.vbs`: venv `FramePort-dev\venv` via `UV_PROJECT_ENVIRONMENT`,
|
||||
`uv sync --frozen`, log `FramePort\logs\dev-gui.log`). **WSL runs on that data need `FRAMEPORT_WINDOWS_DATA=1`**
|
||||
(set in `.claude/settings.local.json`; `paths.shares_windows_data`): the data folder becomes the Windows one, saved
|
||||
paths from the other side are translated on load (`paths.localize`: /mnt/d/X <-> D:\X in library.json, tools.json,
|
||||
PC deployment records), Java/Cpp2IL keep one copy per OS (tools.json `java-<os>-<arch>`), and the link handler isn't
|
||||
registered (it would point the Windows copy's links at WSL). Don't run a WSL GUI and the Windows GUI at once
|
||||
(the gui.lock flock/msvcrt locks don't see each other; only the gui.alive heartbeat does).
|
||||
- Device checks: `frameport test <pkg>` / `frameport parity-device --results <parity.json> --baseline <launch.txt>
|
||||
[--test-only]`; the pre-FramePort baseline is `PATCHED/_known-good-2026-09-28/_frame-state/baseline-launch.txt`.
|
||||
- Docs screenshots (`docs/images/`): `python scripts/scrub_library.py ~/.local/share/frameport <dir> --status works,issues` (copies
|
||||
library + artwork only; titles replace folder names, local paths → `D:/Games/...`, sort by size) then
|
||||
`FRAMEPORT_HOME=<dir> python scripts/ui_smoke.py --out <shots> --docs --fake-frame --game <pkg>` (set
|
||||
FRAMEPORT_JAVA/_OVERPORT_JAR/_APKSIGNER_JAR so no tool download toast appears; `--viewport 1280x2600` + crop for the
|
||||
patch list). Check every PNG for paths, IPs, user names and repack/scene names before committing.
|
||||
README rules (owner): states the project is a proof of concept, provides no piracy tools, credits the wrapped
|
||||
- Docs screenshots + videos (docs/SHOWCASE.md): never by hand. `uv run python scripts/showcase/render_docs.py`
|
||||
renders `docs/showcase/shots.yaml` into `docs/images/` (every shot twice, kept when both agree; only visibly changed
|
||||
files are rewritten; `--check` for CI) and `scripts/showcase/record_video.py [names] [--draft]` records the
|
||||
storyboards `docs/showcase/videos/<name>.yaml` (tour = FramePort in use + README teaser; install = the install
|
||||
tutorial: instruction cards for the download / the command on the Frame, the rest filmed from a first start) into
|
||||
`docs/media/frameport-<name>.mp4`. Both build the demo library first (`demo_home.py`, profile `demo` or `fresh`:
|
||||
catalog games from `demo-library.yaml` + `demo-analyses.json`, art/details cached in ~/.cache/frameport-showcase, a
|
||||
pretend Frame from `showcase-frame.json`) in a temp FRAMEPORT_HOME: no personal data, refuses the real data dir
|
||||
and placeholder art; discovery is off and the setup command shows placeholders for the address and code. Steps find elements
|
||||
by name through Flutter's semantics tree (`scripts/showcase/web.py`: text, tooltip; the innermost match wins) —
|
||||
give new icon-only controls a tooltip. The `showcase` workflow renders on UI pushes to main (+ the videos whose
|
||||
storyboard changed, or the ones named on dispatch) and opens PR `showcase/update`; releases attach every video as
|
||||
`FramePort-<name>.mp4`. `scripts/scrub_library.py` (the owner's own library) is only for private screenshots now.
|
||||
README rules (owner): says not every game runs (no longer "proof of concept" since 1.0), provides no piracy tools, credits the wrapped
|
||||
projects (most functionality is theirs); neutral technical wording; no Quest2Frame mentions anywhere.
|
||||
- GUI smoke test: `uv pip install flet-web playwright && playwright install chromium`, then
|
||||
`FRAMEPORT_HOME=<test dir> python scripts/ui_smoke.py --out <dir> [--game <pkg>] [--frame steamos@<host>] [--update]` (`--update` = fake release: banner, dialog, Settings → Updates) and look
|
||||
@@ -188,6 +361,11 @@ Repo is on an NTFS drive (`core.fileMode=false`); line endings are LF (`.gitattr
|
||||
`T.px(n)`, Material defaults through the theme's text_theme; never hardcode a bare pixel number in ui/.
|
||||
`scripts/ui_smoke.py --scale 1.5 --viewport 2560x1440` renders it. Flet draws a grey box for invalid layouts (e.g.
|
||||
an `expand` child in a `wrap=True` Row).
|
||||
- One GUI per data folder (`core/instance.py`, 2026-10-11): `<data>/gui.lock` (flock/msvcrt, pid inside); a second
|
||||
start asks the open window to come to the front (`gui.focus`, read by `_watch_links`) and exits; a lock held with no
|
||||
window (no `gui.alive`) and older than 30 s is a leftover FramePort process and is ended. `main()` ends the process
|
||||
(`os._exit`) once `ft.run` returns: background threads (re-analysis, session triage) used to keep a windowless
|
||||
process running. Session triage shows one pop-up for all games of a burst (`_announce_sessions`).
|
||||
- Stopping the GUI: `pkill -f` patterns match your own shell — use `pgrep -f "[b]in/frameport-gui|[f]let-desktop-light"`.
|
||||
|
||||
## Round-2 polish (2026-10-02)
|
||||
@@ -241,7 +419,10 @@ The Frame runs firewalld (22 and 32000 open). PC side: the setup server needs in
|
||||
firewall blocks it silently (`DefaultInboundAction Block`): `pairing.ensure_reachable` adds a temporary rule via one
|
||||
UAC prompt, removed when the server stops (flag file in %TEMP%, max 35 min); hints per OS after 45 s without a request
|
||||
(`pairing.firewall_hint`). Flet 1.0 patches aren't thread-safe → `app.serialize_flet_updates()` (a dialog shown while a
|
||||
scan redraw ran never closed: "dropped a patch for unknown control").
|
||||
scan redraw ran never closed: "dropped a patch for unknown control"); it also makes property writes (Flet's
|
||||
`Prop.__set__`) take the same `app.FLET_LOCK`: a property set on a background thread while the loop packed a patch
|
||||
changed the control's `_values` mid-iteration ("dictionary changed size during iteration", a sidebar click's page
|
||||
change died while the live Frame card took a sample). Anything that serves the app (ui_smoke, showcase) calls it too.
|
||||
**Lepton:** needs an activity with category **LAUNCHER** (Quest apps often only have INFO → "APP_ACTIVITY is empty").
|
||||
**2D apps:** Lepton runs every app headless (`lepton.headless=true`, only OpenXR output reaches the headset) unless the app folder (`<base>/lepton-app/`) has a `lepton-show-flatscreen` file (liblepton/app_metadata.sh) → Waydroid window on gamescope; agent v28 `set_flatscreen` at finalize for `vr_kind == "none"`. Android 11's navbar covered the
|
||||
app's controls → patch `device.hide_navbar` (default on for vr_kind none, migration `flat_hide_navbar`) exports
|
||||
@@ -383,7 +564,7 @@ GPU. Learned in the headset: quads can't be a background (the Frame draws quad l
|
||||
whatever the order: hid 4XVR's balcony/controllers), and the Frame doesn't reproject a projection layer from its own
|
||||
pose (images drawn only after head turns, or ahead from xrWaitFrame and sometimes late, wobbled);
|
||||
`equirect_flip/face/res/fps/stereo`), `stable_local`, `focus_hold` (only after 3 s FOCUSED, dips <600 ms),
|
||||
`aim_pitch/aim_yaw/aim_forward`, `refresh_rate`, `layer_debug` (diagnostics). 4XVR re-creates LOCAL spaces every 2–4 s
|
||||
`aim_pitch/aim_yaw/aim_forward`, `refresh_rate`, `layer_debug` (diagnostics); focus_hold is default-on since 2026-10-05. 4XVR re-creates LOCAL spaces every 2–4 s
|
||||
(menu recentring suspect); its theatres are baked 7680×3840 equirect2 images (`assets/100.png` …). Test 360° videos
|
||||
(NASA, public domain) are in the Frame's ~/Videos; copies in `~/Downloads/frameport-360`. 4XVR's "Internal Storage"
|
||||
lists its own `/sdcard/4XPlayer`, not Movies: agent v25 `link_media` hard-links sent files into an app's own top-level
|
||||
@@ -462,7 +643,10 @@ shortcuts.vdf and writes once more if Steam put its old copy back (`shortcuts_lo
|
||||
auto-repair (`_add_then_play`) crashed (its job got a Job, not a reporter). Uploads resume on a transient OSError.
|
||||
Lepton's Android 11 has no clipboard service (134 services; checked with `podman exec … service check clipboard`):
|
||||
SDL/LÖVE apps crashed at start → `frame.sdl_clipboard` (`apk/dex.py`: in-place dex edit, nops one invoke, fixes
|
||||
the header checksum/signature; verified with LÖVE for Android 11.5, GitHub #24 Dramatic Shape). 2D apps (vr_kind none)
|
||||
the header checksum/signature; verified with LÖVE for Android 11.5, GitHub #24 Dramatic Shape). Godot 4.2-4.4 (Kotlin `as ClipboardManager`,
|
||||
GitHub #134 EndoparasiticVR) → `frame.godot_clipboard` (nops the `Intrinsics.checkNotNull` before that check-cast; Godot's
|
||||
clipboard methods return at once; analysis `godot_clipboard`, ANALYSIS_VERSION 10; checked on real godot-lib/templates
|
||||
4.2.2-4.4.1, not on the device; 4.1 and 4.5+ don't throw). 2D apps (vr_kind none)
|
||||
keep every suggested patch that isn't about VR (`needs_vr = False`), not a fixed list. FramePort on the Frame:
|
||||
`frame/local.py` (127.0.0.1, own key authorized; "This Frame (experimental)"; app data in
|
||||
~/.local/share/frameport-app because ~/.local/share/frameport is the agent's) and Steam library changes wait while
|
||||
@@ -475,7 +659,7 @@ recipe keeps it on). I Am Cat works with issues (judder; scale 0.8 no help). Rob
|
||||
libroblox, also with the Vulkan shim). Installs skip the Steam restart when the shortcut is unchanged and wait while a
|
||||
game runs (agent v37). New default fixes in FramePort's code reach games already in a library: after every app update
|
||||
`library._follow_catalog` re-derives each non-user recipe once (setting `recipes.app_version`; tests switch it off via
|
||||
`library.REFRESH_ON_UPDATE`); analysis fields added later still need a re-analysis. Troubleshooting techniques: docs/PLAYBOOK.md "Debugging techniques". Unity `boot.config` "vulkan" substring mislabels GLES games as Vulkan
|
||||
`library.REFRESH_ON_UPDATE`). Analysis fields added later: **bump `analysis/detect.ANALYSIS_VERSION`** (stored as `analysis.extra.analysis_version`); older entries whose APK is still there are analysed again at the GUI's start (background thread, not a job: ~8 s per 900 MB APK) and before a build (`pipeline.refresh_analyses`, GitHub #104); only `analysis`/`suggested` change, user recipes stay; an unreadable APK gets `analysis_failed` = the version (not retried until the next bump). Troubleshooting techniques: docs/PLAYBOOK.md "Debugging techniques". Unity `boot.config` "vulkan" substring mislabels GLES games as Vulkan
|
||||
(I Am Cat ran GLES); OVRPlugin's "Unavailable OpenXR extension: XR_FB_scene" is routine (no longer triaged).
|
||||
**Round 4 (2026-10-04):** XR_KHR_android_surface_swapchain is listed by the Frame's runtime but returns
|
||||
FUNCTION_UNSUPPORTED → adapter `surface_emul` (default on, `native/adapter/surface_swapchain.c`): an ordinary runtime
|
||||
@@ -492,15 +676,230 @@ lacks (ActivityNotFoundException) → imported by hand: libmain.so + game.json
|
||||
DATA/ → external files/WiiCompiledOpenXRVR/DATA, MOD/ → .../RetroRewind6 (decompiled with jadx). Lepton has no
|
||||
picker at all: a FramePort fix would be an injected picker activity. Generated cover/banner art used to suppress the
|
||||
Steam placeholder set (no Steam art at all) → fixed in artwork/steam.py.
|
||||
**Steam library failures (GitHub #21/#30):** on some Frames Steam never loads FramePort's shortcuts.vdf entries (same SteamOS build and arm64 beta client as the dev Frame; IDs correct; file written before Steam starts): Play → console_log `GameAction [AppID <id>] … RequestingLicense → UpdatingAppInfo → LaunchApp failed with AppError_9` (= "Game configuration unavailable", Steam treats the id as a store app). Cause unknown; agent v42/43 diagnostics: `launch` returns `steam` (started / error + code / silent from console_log), `collect_diag` adds `steam_library` (real Steam dir, beta, Steam start vs vdf write time, entries per account, devkit games) + `steam-console.txt` (incl. Steam's `logs/shortcuts.previous.txt` if present; steamclient.so has "LoadShortcuts: rejecting attempt to load shortcuts: invalid account ID"). **Fallback (agent v43):** when Play gets AppError_9 the agent registers the game through Steam's devkit interface (what Valve's Devkit Management Tool does, MIT: `devkit-1 steam://devkit-1/<~/.steam/steam.token>/create-shortcut?response=<file>&gameid=<id>&directory=~/devkit-game` written to ~/.steam/steam.pipe; answer file / `.error`; `~/devkit-game/<id>/launch.sh` = link to the game's launch.sh, `<id>-argv.json` ["launch.sh"] (relative to the folder), `<id>-settings.json` {steam_play 0, compat_tool ""}). Steam adds it live (no restart), picks its own appid (read back from shortcuts.vdf by DevkitGameID), shows it as a normal non-Steam game named "Devkit Game: <id>" (OpenVR 0, but VR works: owner-verified 2026-10-04), art copied to grid/<appid>*. Ids: letter first, then letters/digits/_ only (spaces, '-', leading digit → "missing/invalid arguments"). Later Plays use the devkit entry; uninstall (`devkit_unregister`, `delete-shortcut` live) and purge remove it. Verified on the dev Frame: register/launch/unregister; the AppError_9 trigger itself only in unit tests. GitHub #42 (two Steam accounts, neither MostRecent): Steam rewrote the active account's shortcuts.vdf without FramePort's entries right after starting, and added the devkit entry live without saving it, so `devkit_appid` (vdf only) failed ("Steam added the devkit entry but didn't save it"); agent v55 `devkit_appid_from_log` reads Steam's `sanitize shortcut app id "~/devkit-game/<id>/launch.sh": replacing 0 with N` console line. GitHub #41 (one account): same Steam behaviour, and every install restarts Steam, which forgets the never-saved devkit entry. Agent v56: the log fallback reads only the current session's console_log.txt; Play re-registers a devkit entry that gets AppError_9; art updates also write the devkit entry's grid art (`copy_grid_art`). Open: why some Frames' Steam never loads or saves shortcuts.vdf (single account too).
|
||||
**Exit game (GitHub #36, 2026-10-04):** `Apps.TerminateApp` (what Exit game calls; reachable for tests through Steam's CEF devtools on 127.0.0.1:8080, SharedJSContext, `Runtime.evaluate` — a stdlib websocket client is enough) closed both a normal shortcut and a devkit entry on the dev Frame within 1-3 s. SIGTERM to Steam's `reaper` alone left launch.sh + Lepton (setsid) + the container running (an earlier headset launch ran on for 87 min); SIGTERM to launch.sh cleans up in ~4 s. Agent v44: launch.sh's 2 s loop ends the game when its parent (the reaper) is gone; `upgrade_launchers` (from ensure_host_fixes) adds that to existing launchers in place. Which in-headset exit path fails is still unknown. Screenshots on the Frame: all under SteamVR's appid 250820 (`760/remote/250820/screenshots`, screenshots.vdf has creation time, no game).
|
||||
**SteamVR dashboard at game start (2026-10-04):** the dashboard (Resume game / controller / VR options) was open whenever a FramePort game started (also over Lepton's 2D launcher). Steam's UI exposes `SteamClient.OpenVR.VROverlay.{IsDashboardVisible,HideDashboard,ShowDashboard}` (CEF devtools, `steam_js` in the agent). Agent v44: launch.sh starts `_dashboard_worker` (waits for FrameBridge's first `pacing:` line in launch.log, then hides a visible dashboard for 25 s, at most 3 times; log `<base>/dashboard.log`; opt out with env FRAMEPORT_KEEP_DASHBOARD=1). Verified on the dev Frame (dashboard shown → 4XVR launched via Steam → hidden ~5 s after the first frames); not yet seen in the headset. **Headset (ITR2, 2026-10-05): too late** — Steam shows its frame menu (`valve.steam.gamepadui.frame.menu`, vrwebhelper_systemui.txt `[PooledPopups] Showing`) ~0.3 s after the game's first submitted frame (FrameBridge `new layer:`), but the first `pacing:` summary comes ~8 s later; the owner had pressed Resume (`[HideDashboard] return_to_game`) before the worker looked. Agent v52: watches from the first `new layer:` line (incremental log reads, 0.5 s polls, 30 s window). Agent v59: watches 120 s and hides up to 10 times (the menu came back after 30 s in the owner's sessions). Agent v53: stops for good once the player opens the dashboard with the controller (`toggle_dashboard_action` in vrwebhelper_systemui.txt since the worker started): v52 closed it 60 ms after each press, and the game, paused for it, stayed paused.
|
||||
**Space warp / ITR2 (2026-10-04):** Into The Radius 2 (UE5, `libUnreal.so` + OVRPlugin, Vulkan) uses Application SpaceWarp (extra swapchains `376x376 format=97` motion vectors + `format=129` depth); #35 reports flickering textures. OVRPort's `patch_disable_space_warp` has no effect on UE5, and hiding XR_FB_space_warp in FrameBridge's enumerate doesn't reach the game either: **OVRPort's dispatcher offers/enables XR_FB_space_warp itself**. FrameBridge `hide_space_warp` (per game, off) hides it and strips `XrCompositionLayerSpaceWarpInfoFB` (1000171000) from the projection views (log `hide_space_warp: removed space warp info`); the game still renders its MV swapchains. Test build installed on the dev Frame; flicker not yet checked in a headset. Heuristic: UE5 + OVRPlugin shows it as an option (off). `equirect_emul` is suggested for Unreal games whose graphics API isn't detected ("GLES or unknown"); on Vulkan it switches itself off (Myst/Riven/ITR2): harmless, but Unreal Vulkan detection is a known gap.
|
||||
**PR #34 (merged 2026-10-04, Lucas-Mathieu):** `native/adapter/audio_metadata.h` patches Meta XR Audio Wwise (only build ID e1619e7f…, Batman: Arkham Shadow 1.4.1) so queued audio metadata isn't freed while the current audio frame references it (smoke-bomb crash). Verified headless: installs ("metadata reclamation follows …"), 72 fps; we added a once-per-second scan limit. Outside PRs: review source, rebuild `artifacts/` ourselves, merge locally (contributor's commit kept), push main.
|
||||
**Heuristics eval (2026-10-04):** `scripts/eval_heuristics.py "<VR CyberDeck Portable>"` → 19/44 recipes exact, 322/352 fields (most diffs: `frame.unity_text_input` suggested where older catalog recipes lack it; the rest are runtime-only findings: use_alt, vk_shader_fix, focus_hold, sync_guard, multipass). `PATCHED/` copies aren't scored (already converted).
|
||||
**Power (0.9, agent v47):** sidebar bar below the Frame card: Sleep / Restart / Shut down, each confirmed (`app.frame_power`, a running game asks again). logind answers CanSuspend/CanReboot/CanPowerOff = "challenge" for an SSH session but "yes" inside a user unit, so agent `power` runs `systemctl suspend|reboot|poweroff` from a transient `systemd-run --user --on-active=3` timer (the command answers first). Restart verified on the device (back in ~84 s, new boot id).
|
||||
**Proton default (agent v51, owner's choice 2026-10-05):** the newest **stable** ARM64 Proton is the default for PC VR games (`pick_proton`: asked-for name/alias, else newest installed stable, else any stable). Agent v49-50 defaulted to Experimental; the owner's headset A/B with Rick and Morty (Experimental 69 % reprojected frames, stable felt much smoother) reverted it. The game page's Customize shows `pcvr.proton_tool` as a dropdown (Stable (default) / Experimental = alias `proton-experimental`); `ensure_proton` installs a chosen one (it asks once more for the runtime Steam only names after Proton is installed; Experimental installed unattended on the device in 53 s). A PC VR launch test that fails on stable suggests Experimental (`pipeline.PROTON_TOOL`, `proton_alternative_worth_trying`, button "Try Proton Experimental and reinstall"). Remaining Rick and Morty stutter on head turns = the game rendering below the refresh rate (reprojection), not Proton.
|
||||
**USB cable link (verified 2026-10-05, cabled to the dev PC):** `usb0` is a USB **NCM** gadget (configfs g1: `ncm.usb0` + `ffs.adb`, Valve 28de:2460) set up by `usb-ncm-gadget@usb0.service`, which only runs with `ConditionPathExists=|/etc/systemd/system/adbd.service.d/steamos-devkit-enabled` = **Developer Mode only**. `usb-ncm-dnsmasq@usb0` serves DHCP with fixed `dhcp-host` entries: Frame 10.86.200.233, PC **10.86.200.234** (`frame/usb.py` FRAME_USB_IP/PC_USB_IP). Windows 11 binds its built-in "UsbNcm Host Device" (no driver); WSL mirrored sees eth 10.86.200.234. USB is high-speed (USB 2.0): upload 36.9 MB/s vs 11.3 MB/s home Wi-Fi. Frame→PC over the cable worked without a firewall change. `scripts/usb_autotest.py` (Windows via powershell.exe from WSL), `frameport frame usb-check`. Wizard "Set up with a USB cable" (`PairingServer(host=PC_USB_IP)`; the real bootstrap ran end to end over USB). Discovery dedupes by SSH host key and prefers USB > Frame hotspot > network (`discovery.dedupe`); a lost address is re-found by key (`Frame.relocate`, tested with a dead saved address).
|
||||
**Catalog updates without a release (2026-10-05):** apps fetch `catalog/games/*.yaml` from GitHub `main` (`recommend/catalog.refresh_remote`: one API call `git/trees/main?recursive=1` + raw.githubusercontent downloads of changed blobs, cached as `catalog-gh-<sha>.yaml`, every 6 h; GUI at start + Settings → Data "Check now"; CLI background thread + `frameport catalog-update`). `catalog.load()` only reads the cache. A remote entry is skipped (`unusable_reason`) when it names a patch/adapter key this app doesn't register, has a field `CatalogEntry` doesn't know, or `min_app` > this version: add `min_app` (or rely on new patch ids) when a config needs an unreleased FramePort. Remote beats bundled unless the bundled entry has a later updated/verified date; the user's own entries still win as before. Off: setting `catalog.auto_update`, env FRAMEPORT_NO_CATALOG_UPDATE; FRAMEPORT_CATALOG_URL still overrides the source. So: **pushing a catalog change to main reaches users within 6 h** (their recipes follow via catalog_rev → Update on Frame).
|
||||
**Start activity / QuestCraft / ITR2 pop-in (2026-10-05):** WiiCompiled's VR part (`QuestActivity`, SDL, process `:game`) stays behind Lepton's flat window when the launcher is shown (`device.text_input_window`). `frame.start_activity` (opt-in; analysis `extra.vr_activity` = the original APK's activity with Meta's VR category when it isn't the launcher) gives that activity's filter LAUNCHER and every other LAUNCHER becomes INFO, per element (`axml.set_start_activity`); Lepton's own `apk-info-extractor --print-activity-name` (on the Frame under Lepton/liblepton/apk_extractor) then answers QuestActivity. **OVRPort gives every MAIN activity LAUNCHER + VR categories**, so the VR activity must come from the original APK's analysis, not the converted manifest. Lepton has no activity override (`APP_ACTIVITY` is reset when app_metadata.sh is sourced). QuestCraft reaches Minecraft 1.21.5 (Fabric, Vivecraft, Sodium; LTW "Large Thin Wrapper" GL 3.0 on the Frame's Zink) and stops at `OpenGL error 1282` in `WindowFramebuffer` createTexture (Minecraft's crash report in `files/instances/1.21.5/crash-reports/`, Pojlib's `files/latestlog.txt`); the vrclient SIGSEGV / "pthread_mutex_lock called on a destroyed mutex" afterwards is only the shutdown (Unity's render thread still in vrclient while the instance is destroyed). ITR2 "models popping in and out" in game: Unreal reads user config from **internal** storage (`lepton-data/internal/<pkg>/files/UnrealGame/<Project>/<Project>/Saved/Config/Android/Engine.ini`, not external); `r.AllowOcclusionQueries=0` there broke its menus (removed again). Vulkan shim `vk_query_slots` (two slots per occlusion query) never saw ITR2 create an occlusion query pool, and the run with it crashed (Unreal RenderThread SIGSEGV, caught by sentry-native: no tombstone; the backtrace is in `cache/sentry/*/.sentry-native/*.run/*.envelope` with module offsets) - left off. Next suspect: Unreal 5 mobile HZB occlusion reading depth stored with Valve's FDM injection layer; test = launcher `VK_INSTANCE_LAYERS=''` (Lepton then loads neither VALVE_rpo nor VALVE_fdm_injection, Fossilize still). QuestCraft: LTW overrides glGetError and returns 0 with `LIBGL_NOERROR` set; Lepton passes `LEPTON_ENV_<NAME>` to the app as `<NAME>`, so catalog `lepton_env: {LEPTON_ENV_LIBGL_NOERROR: '1'}` gets Minecraft past the GL error 1282; then black picture (see the catalog entry). FrameBridge logs `new layer` once per layer type per process and projection views only for the first frames, so a second session in the same process (Vivecraft after Unity) needs `layer_debug=1`.
|
||||
**Vulkan validation / ITR2 solved (2026-10-05):** Khronos' validation layer can't come from Lepton (its layer dir is in the read-only guest image; the Linux copies on the Frame are glibc) and a global `debug.vulkan.layers` property (via the LEPTON_GFXRECON_FP_PROPS trick) kills the container at boot. Working recipe: bundle `libVkLayer_khronos_validation.so` (Khronos android-binaries release, arm64-v8a, stored uncompressed) into a test APK re-signed with the game's key (`apk.sign.sign`), install with `frameport install <pkg> --apk`, and set the Vulkan shim's `vk_validation=1` (it adds the layer at vkCreateInstance; findings under the logcat tag VALIDATION in launch.log, each message ID capped at 10). ITR2's flicker + windows behind models = Unreal's Qualcomm shader-resolve subpasses with a depth resolve of a single-sampled depth attachment (VUID-04908/-03179); `vk_spec_fixes=1` drops that resolve (its TRANSFER_DST half never applies to ITR2: the main depth is transient). Valve's VALVE_rpo layer removed ITR2's fog (catalog `lepton_env: VK_INSTANCE_LAYERS: VK_LAYER_VALVE_fdm_injection`). `r.ViewDistanceScale=2` in the internal Saved/Config/Android/Engine.ini only exists on the owner's Frame (the catalog can't write internal storage yet). Launch tests: Lepton prints a transient "is not a running context" on the first start after an APK change (agent v54 `not_started` waits 30 s for "Boot complete!").
|
||||
**GitHub #38/#39 (2026-10-05):** PowerWash Simulator stuck at "Waiting.." on a reporter's Frame = Unity sized the right-eye image rect past its swapchain (rect 268+1656 on a 1920-wide swapchain; the runtime's recommended size differs per Frame) and SteamVR rejected every frame (`xrEndFrame failed -25`, XR_ERROR_SWAPCHAIN_RECT_INVALID), after which the game stopped calling xrEndFrame. FrameBridge `rect_clamp` (default on; sizes noted in xrCreateSwapchain; clamped layers count as swapped) keeps projection/quad rects inside their swapchain; triage `swapchain-rect-invalid`. Not reproducible on the dev Frame with `scale` (Unity's eye sizing isn't just the recommended width); no regression there (72 fps). The adapter's revision was NOT bumped (that would mark every installed game outdated): affected users rebuild. The Room VR (#38) = Unity's render thread SIGSEGV in libgallium_dri.so ~30 ms after the game recreates its eye swapchain with samples=4 (FrameBridge retries samples=1); the game's own code turns MSAA on (not OVRManager); candidate fix: hide GL_EXT_multisampled_render_to_texture (GL shim) - needs the APK to test. The launch test passed both (render thread dead / no frames): triage gap.
|
||||
**Headset round 2026-10-05 (evening):** I Am Cat's judder = repeated xrLocateViews of one display time returning
|
||||
slightly different poses → FrameBridge `pose_consistency` (owner-verified, works). Lucky's Tale freeze on touching a
|
||||
save slot = OVRPort's OpenXR dispatcher converting XR_FB_haptic_amplitude_envelope with ns read as s (GitHub #9) →
|
||||
xrshim `haptic_fix` (owner-verified); its remaining short pauses were the wear sensor ("HMD off/on" 0.5-1 s in
|
||||
`logs/eyetracking.txt`, not vrserver.txt) → **`focus_hold` is on by default with 5 s** (owner's choice; C defaults
|
||||
too). The Room VR works (`unity_no_overlay_copy`). BattleSisters (Unity 2019.4 built-in VR, no libOculusXRPlugin.so)
|
||||
flooded "outside of frame bounds" ~2400/s and hung the GPU (kernel hangcheck, zink DEVICE LOST) → the ovrpshim frame
|
||||
wait now also applies to Unity 2019 without the Oculus XR Plugin (`UnityOculusCheck.legacy_loop`). Vader Immortal
|
||||
(UE4, stuck on the loading image after the intro, GitHub #49): VRP repacks carry a Frida gadget (`libfrda.so`,
|
||||
config `hijack_responses`), but OVRPort's `patch_clean_up_frida` removes its loadLibrary call, so it never runs on
|
||||
the Frame; `frame.ovr_trace` (opt-in, `native/ovrtrace`: 1138 exported ovr_* stubs → real loader functions, logcat
|
||||
tag `fp_ovrtrace`: calls, PopMessage answers, unanswered requests every 10 s) is the next diagnostic.
|
||||
**Unity built-in VR input (2026-10-05):** probe builds (`FRAMEPORT_INPUT_PROBE=1 frameport build …`; it logs
|
||||
trigger/grip crossings too) showed OVRPlugin returns the full Touch state for every controller mask (also Go masks);
|
||||
presses reach the games' OVRInput. Accounting+ (Il2CppDumper v6.7.46 reads its Unity 2017 metadata v24, run on Windows)
|
||||
passes stance selection (NewtonVR grip/trigger) but its motion warning waits for `Input.GetMouseButtonDown(0/1)`,
|
||||
which Lepton never delivers (no focused Android window) → ovrpshim registers its own
|
||||
`UnityEngine.Input::GetMouseButtonDown(System.Int32)` icall (il2cpp_add_internal_call, after Unity's: resolve first)
|
||||
that adds a click in the frame a Touch trigger or A/B/X/Y is newly pressed (`unity_oculus_check` revision 3).
|
||||
BattleSisters (Unity 2019 InputSystem/XR InputDevices via `VrHandInput`): libunity's Oculus module (OVRPlugin
|
||||
function table: a global pointer at 0x16b57c0 in this build, slots filled by name, e.g. +0x160 GetControllerState,
|
||||
+0x168 State2, +0xe8 GetNodePresent) only reports controllers when `strncmp(deviceModel, "Oculus", 6) == 0` (next to
|
||||
the Go check `deviceModel == "Oculus Pacific"`); Lepton's model is "Valve Lepton" → Go/unknown → only Go masks polled,
|
||||
buttons dead. `unity_oculus_check` revision 4 (`oculus_model_checks`, Unity 2019 only) turns the 17 compares' length
|
||||
into 0 (`orr w2, wzr, #6` → `mov w2, #0` after the adrp/add of the "Oculus" literal; the string stays: it is also
|
||||
Unity's VR device name). Device result: libunity now polls `ovrp_GetControllerState(0x3)` (Touch); owner-confirmed
|
||||
2026-10-06: buttons work. Its first controller vibration then hit OVRPort's haptic-envelope bug (11 GB, OOM kill) →
|
||||
`haptic_fix` is now suggested for every game with libOVRPlugin.so (settings detect), BattleSisters works. Ruled out before: ProductName, GetNodePresent, the device-model string itself, exports.
|
||||
Accounting+ works (owner, 2026-10-06).
|
||||
**Vader Immortal (UE4, GitHub #49, 2026-10-06, headless):** stuck after the intro on an in-game image (the splash
|
||||
quad ends ~6 s in; then the game's own projection frames, 72 fps, balanced xrBeginFrame/xrEndFrame). Not the Platform
|
||||
SDK (`frame.ovr_trace`: only user + entitlement, both answered) and not the repack's Frida gadget (OVRPort's
|
||||
`patch_clean_up_frida` removes its loadLibrary). It **leaks ~430 GPU mappings (/dev/dri/renderD128) and ~20 MB a
|
||||
second** (6.5 GB + swap after 5 min, then 26 fps): page-fault stacks (perf -e page-faults, offsets resolved with the
|
||||
process maps + vrclient.so's own symbols; its text segment is at file offset + 0x4000) end in SteamVR's runtime:
|
||||
`xrBeginFrame → CSxrCompositorOpenVR::BeginFrame → SubmitExplicitTimingData → CVRCompositorSharedTextures::
|
||||
BeginGPUTimingCommandBuffer` and `xrEndFrame → CVRCompositorClient::SubmitWithArrayIndexAndTime`. Ruled out: the
|
||||
layer color scale/bias + image layout structs (FrameBridge `strip_color_bias` 1/2, diagnostic), Valve's Vulkan
|
||||
layers (VK_INSTANCE_LAYERS=""), array swapchains in general (Lucky's Tale/I Am Cat flat). Kernel tracepoints aren't
|
||||
allowed for steamos. FrameBridge: `layer_debug` logs xrDestroySwapchain and per-5 s xrBeginFrame/xrEndFrame counts;
|
||||
`frame_balance` (ends an open frame before the next begin) exists but Vader never leaves one open. Next (built,
|
||||
not yet run: the Frame slept): `layer_debug` also counts xrAcquire/Wait/ReleaseSwapchainImage per 5 s (hooked only
|
||||
with layer_debug, after the other conditional hooks) to see whether Vader skips a wait or release: it doesn't (361/361/361 per 5 s).
|
||||
FrameBridge `snapshot=N` (`snapshot_gl.c`, GLES): every N s the left-eye image the game submits is read back on its
|
||||
own context and saved as `files/fb_snap_0-7.ppm` (quarter size) — headless launches show a black headset view, this
|
||||
shows what the game draws. Vader: Lucasfilm logo (an OBB mp4: video works), then its loading card (portrait + segmented
|
||||
bar) that never advances; the async loader thread sleeps and OBB reads stop (~168 MB of a 2.7 GB pak).
|
||||
**SteamVR overlay apps (agent v75, 2026-10-10):** host OpenVR overlays (VRApplication_Overlay) are composited over Lepton (Quest) games on the Frame (probe checker seen over 4XVR in the headset view, `native/vroverlay_probe`); Windows ones register through Proton's vrclient too (Temporal Reality's Windows build created its overlays; fpsVR never reaches OpenVR under Proton). Detection `analysis/vroverlay.py` (bundled `.vrmanifest` with `is_dashboard_overlay`, else OpenVR + IVROverlay + self-registration markers, engine Other; Rift fingerprint `RIFT_ANALYSIS`), patch `pcvr.vr_overlay` (param autostart), Linux entry fields `vr_overlay`/`vr_overlay_autostart` (game page switch); agent `register_vr_overlay`/`unregister_vr_overlay`/`launch_vr_overlay` (IVRApplications via ctypes, live; manifest `<anchor>/frameport-overlay.vrmanifest`, the app's own key, binary = launch.sh). **SteamVR on the Frame reads only `binary_path_linux_arm`** (binary_path_linux alone is skipped). Overlay apps: no launch test, no Linux Steam-parent watchdog (`FRAMEPORT_OVERLAY`), not a "running game". A Steam overlay-app shortcut and a game shortcut run together. SteamVR's SetApplicationAutoLaunch didn't persist on the dev Frame (the watch never started by itself) → **agent v76: FramePort's own autostart**: the deployment's `overlay.autostart` is the source of truth; `ensure_host_fixes` (`ensure_overlay_service`) keeps the user service `frameport-vr-overlays.service` (`_vr_overlay_watch`, 5 s polls of the known vrserver's /proc stat) only while an overlay app has autostart on (register/unregister, uninstall, purge and the kill switch `~/.local/share/frameport/vr-overlays.disabled` / `FRAMEPORT_NO_OVERLAY_AUTOSTART` update it); a new vrserver (pid + start time, kept in `~/.cache/frameport-vr-overlays.json` across agent updates) → child `_vr_overlay_round`: wait for IVRApplications, 15 s grace, `launch_vr_overlay` for apps not running (folder process or GetApplicationProcessId); log `~/.local/share/frameport/vr-overlays.log`. Dev Frame: adopted the running vrserver, skipped the already running watch; a start after a real SteamVR start/boot is not yet seen. Details: docs/FRAME_RUNTIME.md "Overlay apps".
|
||||
**Lepton's logcat mirror dies (agent v67, 2026-10-09):** occasionally launch.log ends with `logcat: Unexpected EOF!` right after the game starts (about 1 launch in 50, Lepton 2.8.14 and 3.0.5): no dashboard auto-hide, no launch-test result. launch.sh's `_logcat_keeper` then reads `podman exec lepton-steamlaunch-<appid> logcat` itself into launch.log (up to 5 restarts while the game runs).
|
||||
**Double launch (agent v57, 2026-10-06):** a second Play while Lepton still boots (~10 s with nothing to see) made
|
||||
the second Lepton stop the first one's container ("Waiting for steamlaunch-<appid> (PID …) to exit", exit 137
|
||||
"(starting)", "Clearing baked app data due to early exit") and both died (Vader, BattleSisters). launch.sh now takes
|
||||
`flock` on `<base>/.launch.lock` (fd 9, inherited by Lepton; the watchdog and dashboard helpers close it with
|
||||
`9>&-`); a second launch exits 0 and logs to `<base>/launch-dup.log`. `upgrade_launchers` adds it to existing
|
||||
launchers. Verified on the device (second launch during boot: ignored, the first kept running). Vader on its loading
|
||||
card ignores input too (owner pressed/held every button: presses reach the runtime, the card never changes).
|
||||
**Issue triage round (2026-10-06, owner's headset):** vibration in OVRPlugin games = 2 s vibrations updated per frame and
|
||||
stopped with amplitude 0; FrameBridge turns amplitude 0 into xrStopHapticFeedback (else each buzz ran 2 s), haptic_fix
|
||||
uses the envelope RMS (was its peak), per-game `haptic_scale` → Creed/The Boys/Jurassic World fine. Lambda1VR (TBXR):
|
||||
loader named by Build.MANUFACTURER (`frame.tbxr_vendor`), no graphics extension enabled (FrameBridge adds
|
||||
XR_KHR_opengl_es_enable + asks for the requirements on -50), always multisampled render-to-texture (GL shim gives
|
||||
single-sampled stand-ins when GL_EXT_multisampled_render_to_texture is hidden) + its xash/ data → works. Jurassic World:
|
||||
`frame.vrapi_stub`. Metro Awakening: contributor's vkshim fixes + hide_space_warp + no VALVE_rpo (jumping polygons like
|
||||
ITR2). Pinball FX VR: hide_space_warp (stutters). Eleven: a Meta online request fails after platform init → needs Meta
|
||||
services. Star Wars Tales: top half black + freeze after loading; ruled out: asset-file paks (never requested), Valve
|
||||
foveation (off: no change), GL errors (MESA_DEBUG=1: none), EGL_BAD_ACCESS once in OVRPlugin init (harmless); the game's
|
||||
eye image reads back black. BlazeRush: VrApi bridge exports + ovr*_ToString stubs + avatar stub get it to the menu room
|
||||
at 72 fps, full input reaches it (diagnostics `input 5s`: Touch type only, sticks 1.0, buttons, poses 0x8f), but the room
|
||||
draws no controllers and ignores input; avatar loader forced to its "Failed" path (no logged-in user) changed nothing.
|
||||
`frame.ovr_trace` can't trace Unity games (P/Invoke dlsym; nothing imports ovr_*). Headless: the VrApi bridge's 30 s
|
||||
head-pose deadline ends VR mode without a worn headset (not a game bug).
|
||||
**Lepton storage (2026-09-30):** each app's /sdcard (= /storage/emulated/0 → `<base>/lepton-data/external`) has `Movies`/`Download`/`Documents` symlinked to the Frame's `~/Videos`/`~/Downloads`/`~/Documents` (liblepton/mounting.sh, only if they exist at start); agent v24 `storage_targets` reads that mapping. Android's MediaProvider canonicalises paths to /home/steamos/... and rejects every file ("doesn't appear under [/system/media...]"), `sm list-volumes` is empty: the media index never works, apps must browse folders. Lepton installs with `adb install -g` (runtime permissions granted, MANAGE_EXTERNAL_STORAGE too). Files: `install/files.py`, `frameport frame send|storage`, GUI Files tab (formerly Frame → Send files).
|
||||
**SteamVR per-app settings (2026-09-30):** editing steamvr.vrsettings while SteamVR runs is lost; the web API (127.0.0.1:27062 /app/setsettings) needs `x-steamvr-secret`. `native/vrsettings` = `fp_vrsettings.exe` (freestanding, OpenVR `FnTable:IVRSettings_003` as a Utility app, loads SteamVR's bin/win64/openvr_api.dll) sets them live and SteamVR persists them: section `steam.app.<shortcut appid>`, keys `preferredRefreshRate` (float) and `motionSmoothingOverride` (0 global, 1 on, 2 off, 3 always). Steam Link (vrlink) lists the Frame's rates 72/80/90/96/108/120/144 in vrserver.txt and follows the per-app preference ("host preferred N Hz"; whether the key is honoured is unverified in-headset yet). Judder metric: vrcompositor.txt session summary dropped + "Timed out. N total" (Stormland: 0 dropped but 313 timeouts in 2 min); fpsVR (`%LOCALAPPDATA%\fpsVR\*.json`, 0.1 ms histograms) gives p99 CPU/GPU ms. `pcvr.steamvr_tuning` (default on, PC only) applies on Play: highest rate whose budget ≥ p99×1.05, at least one step down, smoothing on.
|
||||
|
||||
**Language packs (merged from PR #20, 2026-10-04):** overport's `libovrplatformloader.so` is a dispatcher that `dlopen`s Meta's own loader (`libovrplatformloader_meta.so` / `_meta_q1.so`, also `libpxrplatformloader.so`) and keeps its own message queue; `ovr_LanguagePack_GetCurrent/SetCurrent` are 8-byte `return 0` stubs in it, `ovr_AssetFile_GetList` forwards to Meta's loader. `frame.langpacks` (opt-in, `native/langpack`) serves `<tag>.lang` files from the game's data; `elf.hide_exports` marks the loader's exports STB_LOCAL (bionic and glibc only match GLOBAL/WEAK; verified for glibc, bionic's `is_symbol_global_and_defined` is from memory). `libfp_langpack.so` is built here (`python native/build.py --only langpack`) and committed. Owner-verified 2026-10-04: Deadpool VR with only `en.lang` and the patch on plays English dialogue and runs normally (headless launch tests show 2-6 fps while it loads: not a regression sign). A dispatcher answer that arrives after we answered the timed-out GetList is dropped (one answer per request); games already in a library get `lang_packs` filled after an app update (`library._refresh_data_fields`). The library logs to logcat under the tag `fp_langpack` (dirs looked in, packs found, every language-pack call), so it shows up in the game's `launch.log`; an `ovr_AssetFile_GetList` the dispatcher leaves unanswered for 1.5 s is answered with our packs alone. Deadpool VR (Unreal) accepts a pack only when its `Metadata` equals the game's own version string (`ULanguagePacksSubsystem` compares it with `%s.%s.%s.%s.%s` built from the build info, e.g. `1.0.40.356975.Quest` = versionName; found by disassembling `libUE4.so`): the patch writes the APK's versionName into the library (`@FPMETA@` slot, `with_metadata`), `FRAMEPORT_LANGPACK_META` overrides it. Deadpool VR (2026-10-04, headset): German became selectable with that Metadata, but dialogue stayed silent (even English once reported as a pack) while the path was spelled `/sdcard/Android/obb/<pkg>/x.lang`; with the `/storage/emulated/0/Android/obb/<pkg>/x.lang` spelling (Unreal's own, now listed first in `scan_dirs`) German dialogue plays. `FRAMEPORT_LANGPACK_SKIP=<tags>` or a file `fp_langpack_skip` in the obb folder leaves packs out of the list (experiments).
|
||||
|
||||
**Cube swapchains (GitHub #107, 2026-10-09):** the Frame's runtime refuses `faceCount=6` swapchains (-2,
|
||||
XR_ERROR_RUNTIME_FAILURE); OVRPlugin ignores that ("CreateSwapchain for eye 0: 0x0, 0 stages") and crashes in
|
||||
ovrp_EndFrame4 (memset). FrameBridge `cube_standin` (default on, `native/adapter/cube_standin.c`) serves a refused cube
|
||||
swapchain as one GL cube-map texture in the app's context (GLES only) and drops its layers. Verified headless with
|
||||
Budget Cuts Ultimate (2048² sRGB, 12 mips; runs on at ~70 fps); what the cube layer showed is simply missing.
|
||||
**Hardware video decoding (PR #96, PR #128 by Lucas-Mathieu, adapted 2026-10-10, agent v71):** `native/hevc` = one
|
||||
OMX plugin `OMX.frameport.{avc,hevc,vp9}.decoder` (FFmpeg v4l2m2m on Iris /dev/video-dec0, FFmpeg software decoders as
|
||||
fallback inside the component, Vulkan copy for big native surfaces). The connection installs it once per Frame
|
||||
(`ensure_video_codec` → agent `video_codec_status`/`install_video_codec`: `~/.local/share/frameport/video-codec/versions/
|
||||
<manifest sha>` + `current` symlink). A Frame keeps the same or a newer `revision` (no flip-flop between PCs) → **bump
|
||||
`revision` in native/hevc/build.py with every artifact change**; a failed install isn't retried during the connection.
|
||||
Per game: only launchers of games whose recipe has `frame.hw_video_decode` (install stage, no APK change; suggested
|
||||
from analysis `media_codec`, ANALYSIS_VERSION 8; deployment.json `hw_video_decode`) get the codec line → the Podman
|
||||
wrapper (native/hevc/podman.py) mounts the plugin into that container (merged media_codecs.xml in
|
||||
$XDG_RUNTIME_DIR/frameport-video). Off for every game: Settings → Installing (library `video.hw_decode` → agent
|
||||
`video_codec_switch` = video-codec/disabled); one game: `FRAMEPORT_NO_HW_VIDEO=1 %command%`. `upgrade_launchers`
|
||||
converts agent ≤70 launchers (Batman's `<base>/frameport-codec` → keeps the line, folder then removed). Batman: catalog
|
||||
+ migration `batman_video_patches` (`frame.hw_video_decode` + hidden adapter setting `surface_native`). Iris: 8K
|
||||
refused (ENOMEM) while any other decoder session is open; SteamVR's vrlinkrunthread holds one ~12 s at game start →
|
||||
refused opens retry until 20 s after the plugin loaded (else 2 s), then software; VP9 7680x3840 never returned a
|
||||
picture → VP9 ≤4096x2304; hidden VP9 frames come back as empty capture buffers (bytesused 0, no LAST):
|
||||
FFmpeg's wrapper ended the EOS drain at the first one (two-pass VP9 lost its last ~25 frames) → build.py requeues
|
||||
them (codec revision 8; drain ends at LAST, or 1 s after a skipped empty buffer; dev Frame 2026-10-10: 600/600 =
|
||||
OMX.google.vp9 Y hashes). Rebuilds are deterministic (ext4 + NTFS path
|
||||
with spaces, two NDK copies → same; rev 8 141de01f…; PR #128's own sources → its 27a2d749…). Not yet run on the device.
|
||||
**Vulkan shader dump (GitHub #140, 2026-10-10):** adapter `vk_shader_dump=1` (Vulkan shim, `native/vkshim/shader_dump.h`;
|
||||
applies where vk_sanitize can: Unreal/Other arm64) writes each distinct SPIR-V module once to
|
||||
`files/fp_vk_shaders/<size>_<sha256>.spv` (tmp + rename) and one `index.txt` line per vkCreateShaderModule (`<seq> <ms>
|
||||
<unix ms> <name> new|known|again|failed`, O_APPEND) to find the module behind a GPU hang. Agent v72 `collect_diag`
|
||||
returns `shaders` (newest modules of fp_vk_shaders + fp_spirv, ≤4 MB each, base64) → bundle
|
||||
`games/<pkg>/target/shaders/`. Triage `gpu-hang` suggests both dumps; `triage.graphics_api` (FrameBridge's
|
||||
xrCreateSwapchain formats: <0x1000 Vulkan, else GL) keeps only the session's API's (`API_ONLY`). Host-tested only.
|
||||
Agent v74: a reporter's hang came ~10 s after the last new module, so the culprit wasn't among the newest written →
|
||||
with an index naming modules, `shader_dumps` sends every module of the index's **newest session** (after the last
|
||||
`# start`), deduplicated, last used first, each ≤4 MB, ≤30 MB in all (`session` = how many it names) + that session's
|
||||
index lines; no index (fp_spirv) = the old newest-by-mtime 4 MB. One JSON line (~40 MB base64) over the SSH channel
|
||||
is read whole by `Frame.agent` (fine at that size); `_Writer.fit` drops the least recently used modules last, after
|
||||
cutting logs, to stay under the 24 MB zip limit.
|
||||
**Unreal OBB check (GitHub #159, 2026-10-10):** Epic's DownloaderActivity checks OBB name+size (OBBData), then with `bVerifyOBBOnStartUp=true` CRCs the whole OBB; its screens are invisible in Lepton (stuck at `Displayed …/.DownloaderActivity`, triage `unreal-obb-check-stuck`) → `frame.unreal_skip_obb_check` (analysis `unreal_verify_obb`, ANALYSIS_VERSION 11) flips the manifest boolean (`axml.set_meta_data_bool`); skips only the CRC pass. Host-tested only.
|
||||
**Meta user id (GitHub #182, 2026-10-11):** OVRPort's platform loader (runtimes 3.4.3-23204ea and -aa54c3f) answers
|
||||
`ovr_GetLoggedInUserID` (and two other callers) without a Meta account with a made-up id from a helper (`stp; mov x29,
|
||||
sp; bl random; …; mov/movk x9 = 0x53444835ec580000 (6e18); add x0, x8, x9; ldp; ret`), kept only in memory → a new id
|
||||
every start; Walkabout Mini Golf files saves under `Profiles/Oculus/<id>/` and lost all progress. `frame.ovr_stable_user`
|
||||
(default_on, detect = the game has libovrplatformloader.so) rewrites the helper to `mov x0, <6e18 + 32 bits of
|
||||
sha256(package, keystore)>; ret`: stable per game and per user (keystores are personal), so online games never see two
|
||||
FramePort users as one. Found in every PATCHED build checked. Host-tested only; upstream candidate.
|
||||
**Vivox API 31 (GitHub #101, 2026-10-09):** newer Vivox builds (Green Hell VR) call Android 12 AudioManager
|
||||
communication-device methods from `com.vivox.sdk.AudioChangeListener` with no SDK check → NoSuchMethodError on Lepton's
|
||||
Android 11. `frame.vivox_audio_route` (analysis `vivox_api31`, ANALYSIS_VERSION 4) makes every such method return at
|
||||
once (`Dex.return_early`: return-void / `const/4 v0,0; return v0`; nopping the invoke would leave a move-result the
|
||||
verifier rejects). Older Vivox (Eleven Table Tennis, BattleSisters) lacks that code and isn't matched. Verified
|
||||
headless: Vivox initialises, 150 s at ~65-70 fps.
|
||||
**Steam Input gamepad for 2D apps (GitHub #162, agent v72, 2026-10-10):** Lepton's Android only has the Wayland seat's `wayland_touch/keyboard/pointer`. Steam makes its virtual pad (uinput, `/devices/virtual/input`, 28de:11ff, "Microsoft X-Box 360 pad N", ACL for steamos) only while the Frame's controllers are on (controller.txt "Steam Controller reserving XInput slot 0"; gone when they sleep), also with no game running; steamos-manager's 28de:0000 keys device isn't a pad (no BTN_SOUTH). Lepton's container root (system_server too) = steamos (`keep-id:uid=0`), so a bind-mounted `/dev/input/eventN` opens. Opt-in `device.steam_gamepad` (vr_kind none; suggested for `android.hardware.gamepad`/`LEANBACK_LAUNCHER`, analysis `gamepad`, ANALYSIS_VERSION 9): deployment.json `steam_gamepad` → launcher `GAMEPAD_LINE` (after the codec line: exports FRAMEPORT_GAMEPAD + LEPTON_ENV_SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD, puts `agent/bin` first on PATH) → `PODMAN_WRAPPER` (written by ensure_host_fixes; non-`run` calls go straight on; `run` imports the agent's `podman_run_args`; hands on to the next Podman after its own PATH entry with itself removed from PATH, so it chains to the codec wrapper either way round) adds the pads + `Vendor_28de_Product_<pid>.kl` (Xbox 360 layout). Verified headless with a uinput stand-in pad (Stremio's container: EventHub `classes=0x80000141`, our key layout, KeyEvent BUTTON_A dispatched); with Steam's real pad and a game: not yet. Pads made after the start aren't seen until the next one.
|
||||
**Multiview programs on flat framebuffers (GitHub #77, Doom3Quest, 2026-10-09, prototype):** Mesa enforces OVR_multiview's
|
||||
"program num_views == draw framebuffer views" rule (`draw_validate.c`, the draw is dropped silently); Qualcomm doesn't.
|
||||
Doom3Quest compiles every VS with `layout(num_views=2) in;` and draws its HUD/PDA into 2D-texture FBOs → black. Opt-in
|
||||
`frame.gl_multiview_fbo` (analysis `gl_multiview_libs`, ANALYSIS_VERSION 6; own-engine GLES only): `native/glmv` =
|
||||
`libfpglmv.so` (12 chars = "libGLESv3.so": libdoom3.so's one `.rodata` dlopen string is rewritten in place, its qgl*
|
||||
table comes from dlsym on that handle; also first DT_NEEDED for its direct gl*/egl* imports). Such draws use a lazily
|
||||
built single-view twin (view 0, uniforms copied per draw); eglMakeCurrent resets the per-thread cache. Host-tested only
|
||||
(rewriter on all 19 Doom3Quest shaders + glmv.c against a stand-in GL, `tests/test_gl_multiview_fbo.py`); the host's
|
||||
Mesa 23.2 llvmpipe has no OVR_multiview. Headset-verified by Xandrix1987 (2026-10-09: HUD/PDA shown, no fps drop) -> in the Doom3Quest catalog recipe.
|
||||
**BlazeRush (GitHub #57, Klownicle's verified guide, 2026-10-10; FramePort's version headless-tested only):**
|
||||
`frame.blazerush` (1.0.349 only: exec-segment SHA + every word checked, offsets from PT_LOAD) NOPs the avatar-readiness
|
||||
wait (0x4b2dbc), RETs the avatar renderer (0x4b2adc), renames `cache/shader_cache` → `cache/br_shader_v1`. GL shim
|
||||
(now also exports glShaderSource/glLinkProgram/glProgramBinary/glDeleteProgram/glDraw*: pass-through unless a setting
|
||||
is on) `gl_int_attribs` (integer inputs fed via glVertexAttribPointer re-set with glVertexAttribIPointer per draw,
|
||||
reflection cached per EGL context + program, dropped on link/binary/delete) and `gl_highp_markers` (vertex shaders with
|
||||
every '|' marker: mediump→highp except on in/out/uniform lines); host test `tests/test_blazerush.py` with a stand-in
|
||||
GL. FOV: the game reads VrApi props 7/8 right after vrapi_Initialize (no session; the Frame has no
|
||||
XR_EPIC_view_configuration_fov) → the bridge saves the live FOV (also located before tracking, headless: 118.87×120.04
|
||||
on the dev Frame) in `files/framebridge-vrapi-fov.txt` and answers with it at the next start (log `FOV properties: …
|
||||
(saved by an earlier session)`). `device.config_sync` (catalog field `config_sync`, agent v78): user_config.xml
|
||||
r_3dwidth/height, r_width/height from the runtime eye size (FrameBridge logs `runtime recommended eye size`) × scale,
|
||||
written at finalize, set_settings and by launch.sh before Lepton starts (`_config_sync`). Headless runs at scale 1.7
|
||||
end in `zink: DEVICE LOST` right after the bridge's 30 s no-tracking deadline tears the session down (gpu fault at the
|
||||
teardown; not at scale 1.0): a headless-only artifact, the headset never hits that deadline. Launch tests count an exit/crash after
|
||||
such a line as a pass (triage.yaml `headless_end`, finding `headless-end`; GitHub #57 "Failed to launch" on install).
|
||||
**Session triage (agent v70, 2026-10-09):** launch tests never reach FOCUSED, so real play sessions are triaged
|
||||
too. list_installed gives each game `last_play` {start, end, test} from `<anchor>/plays.log` (launch tests write a
|
||||
`test <unix>` line first → test sessions are only marked); `session_log` returns the newest session's launch.log
|
||||
(`<base>/session.log`, ≤4 MB: head + tail + FrameBridge/crash lines between) + that session's logcat-crash.log +
|
||||
`journalctl -k` GPU lines ("kernel: …"). The GUI's connection refresh (`app._check_sessions` → `pipeline.sessions_due`,
|
||||
background thread; sessions >7 days old are skipped) runs `pipeline.triage_session` = `validate/session.analyze`
|
||||
(triage.yaml signatures incl. `space-warp-used` (info, `question:`, never auto-applied), `gpu-hang` (`report: true`)
|
||||
+ computed `slow-frames` (pacing windows vs the nearest refresh rate) and `focus-dips` (FrameBridge always logs
|
||||
`focus: lost` / `focus: back after N ms`, session_fixes.c)) → library `last_session` / `last_session_checked` → game
|
||||
page "Last session" callout (Try this fix / Rebuild with this fix / Yes-No question / Report / Dismiss). Suggestions may
|
||||
be values (`adapter.scale=0.85`, `triage.split_suggestion`); adapter-only fixes are pushed live (`apply_suggestions_live`
|
||||
→ agent set_settings, like the Game settings dialog). CLI `frameport session <pkg> [--apply]`. PC VR (Proton) launchers
|
||||
log no session end, so they aren't triaged yet. pac_hints stays triage-only: a survey of all 68 dump APKs found unpaired
|
||||
PAC hints in 21 libraries of 18 games (OpenSSL's 38/40 in most UE4 libUE4.so and libEOSSDK.so, UE5 libUnreal.so ~400
|
||||
vs +2-3, libass, libopencv, …), most of them games that work, so default-on would rewrite many working builds.
|
||||
**Unresolved (as of 2026-09-28):** Arcsmith (right-eye distortion) and Time Stall (both eyes) — swap, tracking, Valve
|
||||
layers, depth, pacing ruled out. Sniper Elite VR (DEVICE LOST), Espire 1 (Mesa GL upload crash), HITMAN 3 (freedreno
|
||||
crash): use PC versions.
|
||||
|
||||
**Install links / FrameDrop button protocol (2026-10-07, not yet clicked end to end from a browser):** FrameDrop
|
||||
(framedropvr.com, a closed-source Windows sideloader) defines "Install with FrameDrop" buttons:
|
||||
`https://framedropvr.com/install?manifest=<url>|url=<file>` → that page opens `framedrop://install?…` (1.6 s, else its
|
||||
home page); manifest `{"schema":"framedrop.install/v1","name","files":[{"url","sha256"}]}` (name = Steam title; .apk
|
||||
or Linux .zip). `deeplink.py` (no Flet) parses framedrop://, frameport:// and the pasted https link, enforces its
|
||||
rules (https; http only on loopback; no credentials; no LAN/loopback/link-local IPs, also after DNS and redirects; URL
|
||||
ends in a file name), caps manifests at 256 KiB, ignores non-hex sha256 (FrameDrop's own example has a placeholder),
|
||||
downloads into `<data>/downloads/<slug>-<hash>/` (OBBs → `obb/` next to the APK, `.part` removed on cancel/mismatch);
|
||||
`pipeline.add_from_link` routes APK / Linux / exe and sets `title` + `title_locked` + `link` (a bare file link keeps
|
||||
FramePort's title). GUI: `views/link_dialog.py` (always asks first), Add games → "Install from a link…", CLI
|
||||
`frameport open-link [--yes --no-install --gui]`. **A `flet build` bundle can't take a URL argument** (the Flutter
|
||||
host treats any argv as a developer page URL), so `urlhandler.py` registers a script, not FramePort.exe: Windows
|
||||
HKCU `Software\Classes\{framedrop,frameport}` → hidden PowerShell `frameport-link-handler.ps1`; WSL (source runs)
|
||||
the same keys → `wsl.exe -d <distro> -e sh frameport-link-handler.sh`; Linux `frameport-links.desktop` +
|
||||
`xdg-mime`; macOS unsupported (Apple Events, paste instead). The script drops the link into `<data>/links/*.link`
|
||||
and starts FramePort unless `<data>/gui.alive` is < 10 s old (`gui.starting` stops double starts); the GUI's
|
||||
`_watch_links` thread touches the heartbeat and opens links (newest window session). Settings → Install links: one
|
||||
switch per scheme (`links.framedrop`, `links.frameport`, default on); a scheme another program owns (FrameDrop) is
|
||||
only taken with "Use FramePort for these links" (`register([s], force=True)`); off removes only FramePort's own
|
||||
registration (`MARK` in the command). Never registered with FRAMEPORT_HOME/FRAMEPORT_NO_LINK_HANDLER (tests,
|
||||
screenshots). Same round: files dropped on the Library (`ui/dropped.py`, bundles only like the Files tab), "Add a
|
||||
Windows program (.exe)…" (`pipeline.add_windows_exe`: exe in Downloads/home/drive root copied alone into
|
||||
`<data>/windows-apps/<slug>/`), patch `device.display_mode` (Automatic / VR / Flat window → `InstallContext.display`,
|
||||
`installer.show_window`). FramePort-only manifest extension `"frameport": {"description", "icon"}` (bad values ignored; icon: same URL rules,
|
||||
≤2 MiB, Pillow-checked, ≥32 px, saved as PNG in `<data>/downloads/icons/`, shown in the question by asset URL, then
|
||||
`sources.apply_custom(pkg, "icon")` unless `.picked` exists; description fills `details.description` only when
|
||||
empty). Bare file links get a title guessed from the file name (`title_from_filename`: version/arch dropped, package
|
||||
names → last part). Windows test of 0.12.1.dev191 (2026-10-07): a button click opened the dialog; but a click right
|
||||
after closing FramePort did nothing (the closed window's heartbeat was < 10 s old) → the handler scripts now wait
|
||||
up to 4 s for the link file to be taken before trusting the heartbeat, the window's CLOSE event / atexit delete
|
||||
`gui.alive`, and the Windows command runs under `conhost.exe --headless` (plain `-WindowStyle Hidden` flashed a
|
||||
console). Screens: `scripts/ui_smoke.py --links --fake-frame --game <pkg>` (tall pages: `--viewport
|
||||
1280x7000`; Flutter's popup menu ignores Escape).
|
||||
Demo link: the homepage's example button = `deeplink.DEMO_MANIFEST` (https://frameport.app/demo/cool-game.json, published
|
||||
from site/public/demo/); every form (https button page, frameport://, framedrop://, pasted) → `InstallRequest.demo`,
|
||||
no network (fetch_manifest returns `demo_manifest()`, download refuses), `link_dialog.show_demo` (Install =
|
||||
`easter.demo_install`: Cool Game's cover out of the portal; eggs off → "Nice, it works!"), CLI just says so.
|
||||
|
||||
## Releases, CI, GitHub
|
||||
Maintainer-only notes (accounts, credentials, key locations) live in the git-ignored `CLAUDE.local.md`.
|
||||
Public repo `github.com/spoopyghosty0/frameport` (branch `main`). Push a `v*` tag → CI (`.github/workflows/build.yml`)
|
||||
@@ -509,6 +908,12 @@ tests, builds Windows x64 / macOS arm64 / Linux x64 bundles, signs, attests and
|
||||
`FramePort-selfsigned.cer`; notes = "What's new" from the annotated tag message + the short `packaging/release-footer.md`; owner: keep release
|
||||
notes short — a few "What's new" bullets, nothing long after them).
|
||||
Installed apps find the release themselves (self-update), so the notes are what users see in the update dialog.
|
||||
- **Dev builds (for testers, no release):** Actions → build → Run workflow (main), "dev" ticked, optional "notes"
|
||||
(what to test). The bundles get version `<next patch>.dev<run number>` (`scripts/dev_version.py`, not committed) and
|
||||
replace the rolling `dev` pre-release (`dev-release` job; same assets + SHA256SUMS). Automatic update checks ignore
|
||||
pre-releases; testers use Settings → Updates → "Install the latest dev build…" (`updates.check_dev`,
|
||||
`Updater.install_dev`). `parse_version` sorts 0.9.0 < 0.9.1.devN < 0.9.1, so testers get the next release normally.
|
||||
- **Upstream trackers:** issues labelled `upstream` (#73 microphone, #74 haptics, #75 VrApi bridge) list our workarounds for OVRPort bugs and how to drop them; check them against OVRPort's latest release before each release.
|
||||
- **Release checklist:** bump `src/frameport/_version.py` (the only version; `scripts/package.py` fails a tag build
|
||||
whose tag ≠ `v<_version>`), commit, `git tag -a vX.Y.Z -m "FramePort X.Y.Z" -m "<What's new, Markdown bullets>"`,
|
||||
push the commit and the tag. Never publish a release without its `SHA256SUMS.txt` (the updater refuses it) and keep
|
||||
@@ -524,6 +929,11 @@ Installed apps find the release themselves (self-update), so the notes are what
|
||||
Windows console) plus PYTHONUTF8; macOS builds need `--python-version 3.12 --arch arm64` (cryptography has no wheels
|
||||
for flet's default Python / x86_64 cross-build), with a PyInstaller fallback step; `astral-sh/setup-uv` has no
|
||||
floating major tags after v7 → pin the exact version; force-moving a tag starts duplicate runs (cancel one).
|
||||
- `jni` (2026-10-06): jni_flutter 1.0.4/1.0.4+1 generate bindings that require jni ^1.1.0 while Flet's build template
|
||||
pins jni 1.0.0 → every `flet build` bundle failed (`JniVersionCheck`, "generated bindings expect package:jni
|
||||
^1.1.0"). pyproject `[tool.flet.flutter.pubspec.dependency_overrides] jni = "1.1.0"` fixes it (pinning
|
||||
jni_flutter 1.0.4 does not); drop it once Flet's template moves to jni 1.1. A failed tag build publishes nothing:
|
||||
delete and re-push the tag on the fixed commit.
|
||||
- macOS runner (2026-10-02): `macos-latest` jobs went unassigned (cancelled after 15 min, no steps); `macos-15`'s Xcode
|
||||
16.4 fails a Flutter plugin (`NWPath has no member`); `macos-26` (Xcode 26) builds with `flet build`. The PyInstaller
|
||||
fallback (`package.py --pyinstaller`) passes `--yes` so a failed `flet build`'s folder doesn't stop it at a prompt.
|
||||
@@ -579,5 +989,7 @@ the unresolved eye distortion, and testing the release bundles on real Windows/m
|
||||
fallback (`core/cache.py`). Don't hardcode what can be discovered (e.g. Lepton path via appmanifests).
|
||||
- Keep APK edits minimal and byte-stable (parity depends on it). Every new fix: a patch module + a triage signature +
|
||||
a PLAYBOOK row + a unit test.
|
||||
- New or changed patch, game setting, catalog recipe or triage signature → run `python scripts/patch_docs.py`
|
||||
(regenerates docs/PATCHES.md + site/src/data/patches.json, the site's id → anchor map; a test runs `--check`).
|
||||
- Known-good backups of every working APK and the signing keys: `PATCHED/_known-good-2026-09-28/` (don't delete).
|
||||
- Your development Frame: `frameport frame info` (remembered in `<user data>/frames.json`); SSH as `steamos@<frame>`.
|
||||
@@ -7,93 +7,127 @@
|
||||

|
||||

|
||||
|
||||
Install games that target the Meta Quest, Android, or general PCVR onto your **Valve Steam Frame**. FramePort handles everything from uploading game files, setting up your Frame, injecting compatibility patches, and adding shortcuts to your Steam library. FramePort aims to be as simple as possible by taking advantage of the fact that the Steam Frame runs on Linux.
|
||||
Install Quest games, Android apps, Linux apps and PC VR games on the **Valve Steam Frame**. FramePort sets up the
|
||||
Frame, patches each game so it runs there, uploads it and adds it to your Steam library with artwork.
|
||||
|
||||

|
||||
[](docs/media/frameport-tour.mp4)
|
||||
|
||||
> **Notice:** FramePort explicitly does NOT download, share, or unlock games. You must provide legally obtained game
|
||||
> executables. Core features of FramePort simply download and wrap other published tools (see [Built on](#built-on))
|
||||
> with patches provided by FramePort adding a hardware compatibility layer. This enables users to use games/apps legally
|
||||
> purchased on sites like [SideQuest](https://sidequestvr.com/).
|
||||
▶ [Watch the full tour](docs/media/frameport-tour.mp4) · New to FramePort? [Watch the install tutorial](docs/media/frameport-install.mp4)
|
||||
(about 90 seconds each)
|
||||
|
||||
> **Notice:** FramePort doesn't download, share or unlock games. Use games you got legally, for example from
|
||||
> [SideQuest](https://sidequestvr.com/). FramePort downloads published open-source tools (see [Built on](#built-on))
|
||||
> and adds its own patches so games run on the Frame.
|
||||
>
|
||||
> Not every game runs on the Frame: recipes are tested by the community, and some games need Meta's services or
|
||||
> hardware the Frame lacks.
|
||||
|
||||
## Features
|
||||
|
||||
- **Painless setup:** one short command on the Frame. No root, no `sudo`, no password.
|
||||
[What it changes](docs/FRAME_SETUP.md).
|
||||
- **Type on Frame:** use your computer's keyboard on the Frame: in VR apps, Android apps, Steam and the desktop.
|
||||
[More](#type-on-frame).
|
||||
- **One click per game:** convert, patch, sign, upload, add to Steam with artwork, launch test.
|
||||
- **Per-game recipes:** a tested catalog plus detection rules; every patch explained in plain words.
|
||||
- **FrameBridge:** FramePort's OpenXR adapter emulates what the Frame natively lacks (passthrough, room, controller models,
|
||||
curved and 360° layers); game settings as simple switches.
|
||||
- **Beyond Quest:** Android apps as windows, PC VR via Proton or Revive, Windows (non-VR) games via Proton, a Files
|
||||
tab with drag and drop.
|
||||
- **Self-updating** releases, redacted diagnostics, one-click problem reports and working-config sharing.
|
||||

|
||||
|
||||
- **Easy setup:** one line in the Frame's terminal. No root, no password. [What it changes](docs/FRAME_SETUP.md).
|
||||
- **One click per game:** patch, upload, add to Steam with artwork and test that it starts.
|
||||
- **Recipes:** tested patches and settings for 120+ games; other games get suggested patches, each explained in
|
||||
plain words.
|
||||
- **Game settings:** sharpness, refresh rate, controllers, 360° video and mixed reality as simple switches.
|
||||
- **More than Quest games:** Android apps in a window, Linux apps, PC VR games and Windows programs.
|
||||
- **Install links:** "Install with FramePort" buttons on websites ([frameport.app](https://frameport.app) links) open
|
||||
in FramePort.
|
||||
- **Your Frame from your PC:** Type on Frame (your keyboard on the Frame), Files, Screenshots, Live view (what the
|
||||
headset shows, in your browser) and Monitor (frame rate, temperatures, battery, processes).
|
||||
- **Updates itself**, and reports problems without personal data.
|
||||
|
||||
Details for each: [Install and first steps](docs/INSTALL.md).
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Quick start
|
||||
|
||||
1. [Download](https://github.com/spoopyghosty0/frameport/releases/latest) and unzip the build for Windows, macOS
|
||||
(Apple Silicon) or Linux, then start FramePort.
|
||||
2. **Connect the Frame** (once):
|
||||
1. In FramePort click **Steam Frame → Show setup command**. Keep FramePort open; the Frame and your computer must
|
||||
be on the same Wi-Fi.
|
||||
2. On the Frame open the **SteamVR dashboard → Launch a program → Desktop**: the Linux desktop opens on a virtual
|
||||
screen.
|
||||
3. Open the app menu (bottom-left corner of that desktop) → **System → Konsole** (or search for Konsole).
|
||||
4. Type the command FramePort shows exactly as shown (on-screen keyboard or any USB/Bluetooth keyboard) and press
|
||||
**Enter**. This will run the following [bash setup script](bootstrap/bootstrap.sh).
|
||||
5. After a few seconds the desktop closes by itself (Steam restarts once); that's expected. If
|
||||
Steam asks to install **Lepton**, confirm it. FramePort shows the Frame as connected within a minute. No
|
||||
password needed.
|
||||
3. **Add games → Scan a folder** with your game backups (APK + OBB, or PC VR game folders).
|
||||
4. Open a game → **Install on Frame**, then play it from the Frame's Steam library.
|
||||
1. [Download](https://github.com/spoopyghosty0/frameport/releases/latest) the build for Windows, macOS (Apple
|
||||
Silicon) or Linux, unpack it and start FramePort.
|
||||
2. In FramePort click **Steam Frame → Start setup**. The Frame and your PC must be on the same Wi-Fi (or use a USB
|
||||
cable).
|
||||
3. On the Frame open the **SteamVR dashboard → Launch a program → Desktop**, then the app menu → **System →
|
||||
Konsole**, and run:
|
||||
|
||||
Full guide, firewalls and troubleshooting: [docs/INSTALL.md](docs/INSTALL.md).
|
||||
```
|
||||
curl -fsSL https://frameport.app/s | bash
|
||||
```
|
||||
|
||||
## Type on Frame
|
||||
4. Click **Allow** in FramePort when it shows the same 4-digit code as the Frame. Steam restarts once; if it asks to
|
||||
install **Lepton** (Valve's Android runtime), confirm it.
|
||||
5. **Add games → Scan a folder…** with your games, open one and click **Install on Frame**. Play it from the Frame's
|
||||
Steam library.
|
||||
|
||||
Typing in VR is painful, so FramePort turns your computer's keyboard into a keyboard for the Frame. Open **Type on
|
||||
Frame** (keyboard icon on the sidebar's Frame card, the Steam Frame page, or a game's menu), select a text field in
|
||||
the headset and type: searches, logins, chat, in any app, in Steam or on the desktop. Paste longer text to type it in
|
||||
one go. Nothing to install: FramePort adds a virtual keyboard on the Frame while the window is open, without root.
|
||||
|
||||

|
||||
|
||||
Unity apps whose text fields close the moment you select them on the Frame (no system keyboard there) get a per-game
|
||||
fix, so Steam's on-screen keyboard and Type on Frame work in them too.
|
||||
[Details](docs/INSTALL.md#typing-on-the-frame).
|
||||
Full guide, firewalls and troubleshooting: [Install and first steps](docs/INSTALL.md). How to lay out game folders:
|
||||
[FAQ](docs/FAQ.md).
|
||||
|
||||
## Compatibility
|
||||
|
||||
If a game has already been tested with FramePort, it will automatically use the optimal game config. Otherwise, FramePort
|
||||
will attempt to guess key patches. If you find a new config that works for an app you are testing, please consider submitting it to the community!
|
||||
Tested games use their recipe (the patches and settings that work for them) automatically; for other games FramePort
|
||||
suggests patches. See the [list of tested games](docs/GAMES.md). Got a game working? Share its recipe from the game's
|
||||
**…** menu: [how](docs/INSTALL.md#share-a-recipe-or-report-a-problem).
|
||||
|
||||
**[List of tested games](docs/GAMES.md)**
|
||||
## Compared with other tools
|
||||
|
||||
**Tested something? Share it.** In FramePort open the game → **…** → **Share working config…** (it fills in the
|
||||
recipe for you) or **Report a problem…** (attaches a diagnostics zip with personal data removed). Without the app:
|
||||
[share a working config](https://github.com/spoopyghosty0/frameport/issues/new?template=working-config.yml) · [report a problem](https://github.com/spoopyghosty0/frameport/issues/new?template=bug-report.yml). Shared configs become built-in recipes for everyone.
|
||||
FrameDrop and Valve's own tools install an app as it is. FramePort differs in four ways:
|
||||
|
||||
- **Free and open source** (GPL-3.0). FrameDrop is free (donationware) without published source.
|
||||
- **Quest games that don't run on the Frame as they are** get converted and patched, with a tested recipe for 120+
|
||||
games. The others install the game unchanged.
|
||||
- **Windows, macOS and Linux.** FrameDrop is for Windows.
|
||||
- **Wi-Fi or a USB cable**, and no **Pair new host** step.
|
||||
|
||||
<details>
|
||||
<summary>All differences</summary>
|
||||
|
||||
| | **FramePort** | **FrameDrop** | **By hand with Valve's tools** |
|
||||
|---|---|---|---|
|
||||
| Cost and source code | Free, open source (GPL-3.0) | Free (donationware), source not published | Free, from Valve |
|
||||
| Runs on | Windows, macOS, Linux | Windows | Depends on the tool |
|
||||
| First connection | One line in the Frame's terminal; it turns on Developer Mode itself. No password | Turn on Developer Mode, then **Pair new host** | Turn on Developer Mode and pair, or use Android's debug tool (adb) |
|
||||
| Wireless or cable | Wi-Fi, the Frame's hotspot or a USB cable | Same Wi-Fi network | Wi-Fi, or adb |
|
||||
| Quest games that don't run as they are | Converted and patched for the Frame | Installed as they are | Installed as they are |
|
||||
| Knows which patches a game needs | Tested recipes for 120+ games, updated without an app update | Not stated | No |
|
||||
| In the Steam library | Yes, with artwork and tags | As a "Devkit Game" shortcut | As "Devkit Game: <title>"; not with adb |
|
||||
| Android apps, Linux apps, Windows programs | All three; Proton (runs Windows programs) is installed for you | All three; install Proton first | Yes; 2D Android apps need an extra file |
|
||||
| PC VR games | On your PC; some also on the Frame | Not supported | Not supported |
|
||||
| "Install with …" buttons on websites | Yes ([frameport.app](https://frameport.app) links) | Its own | No |
|
||||
| A game doesn't start | A launch test reads the logs and suggests a patch | A log viewer | No help |
|
||||
| Use the Frame from your PC | Live view, Monitor, Files, Screenshots, Type on Frame | Not stated | No |
|
||||
|
||||
</details>
|
||||
|
||||
Other tools as described on their own pages, checked 2026-10-09:
|
||||
[FrameDrop about](https://framedropvr.com/about) · [how-to](https://framedropvr.com/how-to) ·
|
||||
[Valve: loading games on Steam Frame](https://partner.steamgames.com/doc/steamhardware/steamframe/loadgames).
|
||||
Out of date? [Report a problem](https://github.com/spoopyghosty0/frameport/issues/new).
|
||||
|
||||
## Built on
|
||||
|
||||
Most of the work is done by these projects:
|
||||
[OVRPort](https://github.com/Android-XR-Bridge/OVRPort) (Quest → OpenXR, originally
|
||||
[ovrport/app](https://github.com/ovrport/app)) · Valve Lepton, Proton and SteamVR ·
|
||||
[Revive](https://github.com/LibreVR/Revive) · Mesa (Zink) · [Khronos OpenXR SDK](https://github.com/KhronosGroup/OpenXR-SDK)
|
||||
· Eclipse Temurin, Android apksigner and NDK · [Flet](https://flet.dev) · OculusDB and Steam store data.
|
||||
What FramePort adds itself: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
|
||||
What FramePort adds: [Architecture](docs/ARCHITECTURE.md).
|
||||
|
||||
## Documentation
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| [INSTALL.md](docs/INSTALL.md) | Install, connect, update, PC VR, files, problem reports |
|
||||
| [FRAME_SETUP.md](docs/FRAME_SETUP.md) | What setup changes on the Frame, networks and firewalls, undoing it |
|
||||
| [COMPATIBILITY.md](docs/COMPATIBILITY.md) | What runs and how well |
|
||||
| [GAMES.md](docs/GAMES.md) | Tested games and how well they run |
|
||||
| [PLAYBOOK.md](docs/PLAYBOOK.md) | Symptoms and fixes per game |
|
||||
| [FRAME_RUNTIME.md](docs/FRAME_RUNTIME.md) | Steam Frame runtime facts |
|
||||
| [ARCHITECTURE.md](docs/ARCHITECTURE.md) | How the code is organised |
|
||||
| [CONTRIBUTING.md](CONTRIBUTING.md) | Code, recipes, translations |
|
||||
| [Install and first steps](docs/INSTALL.md) | Install, connect, update, PC VR games, files, problem reports |
|
||||
| [What the setup changes](docs/FRAME_SETUP.md) | What setup changes on the Frame, networks and firewalls, undoing it |
|
||||
| [Compatibility](docs/COMPATIBILITY.md) | What runs and how well |
|
||||
| [Tested games](docs/GAMES.md) | Tested games and how well they run |
|
||||
| [FAQ](docs/FAQ.md) | Game folders and common questions |
|
||||
| [Porting playbook](docs/PLAYBOOK.md) | Symptoms and fixes per game |
|
||||
| [Steam Frame runtime reference](docs/FRAME_RUNTIME.md) | Facts about the Frame's runtime |
|
||||
| [Architecture](docs/ARCHITECTURE.md) | How the code is organized |
|
||||
| [Contributing](CONTRIBUTING.md) | Code, recipes, translations |
|
||||
|
||||
## Development
|
||||
|
||||
@@ -104,8 +138,9 @@ uv run frameport --help # command line
|
||||
uv run pytest # tests
|
||||
```
|
||||
|
||||
## AI Usage Notice
|
||||
While I would like to program everything manually, I no longer have much free time for personal projects. As a result I make use of AI tools to make it significantly quicker to debug compatibility issues.
|
||||
## AI usage
|
||||
|
||||
I don't have much free time for this project, so I use AI tools to debug compatibility problems faster.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ FramePort is GPL-3.0-only (see `LICENSE`). It includes or downloads the followin
|
||||
| Android NDK runtime (statically linked libc++) | native Android libraries | Apache-2.0 with LLVM exception, plus legacy notices (`native/vrapi-bridge/licenses/ANDROID-NDK.txt`) |
|
||||
| Flet and Flutter (desktop app runtime) | release bundles | Apache-2.0 / BSD-3-Clause |
|
||||
| flet-dropzone / desktop_drop (drag-and-drop) | release bundles | Apache-2.0 / MIT |
|
||||
| FFmpeg 7.1.1 hardware HEVC wrapper (LGPL configuration, without GPL/nonfree components) | `artifacts/hevc/libstagefrighthw.so`; source/rebuild instructions in `native/hevc/build.py` and `native/hevc/README.md` | LGPL-2.1-or-later (`artifacts/hevc/COPYING.FFmpeg`); unmodified source: https://ffmpeg.org/releases/ffmpeg-7.1.1.tar.xz |
|
||||
| AOSP Android 11 native media/utility headers | `native/hevc/platform/` | Apache-2.0; copyright/license notices retained in the headers |
|
||||
| Python packages (paramiko, zeroconf, psutil, pyelftools, capstone, UnityPy, PyYAML, requests, typer, pyaxmlparser, Pillow, cryptography, …) | release bundles | their own licenses (see each package's metadata) |
|
||||
|
||||
FramePort's own native code (the FrameBridge adapter, GL/Vulkan/OpenXR shims, the Windows helpers) is GPL-3.0-only.
|
||||
|
||||
+4787
-55
File diff suppressed because it is too large.
Load diff
+18
-8
@@ -1,14 +1,24 @@
|
||||
48a3ecad77ec0f3215b29070e994590eb9579e04d3c88a47f37f306f9aeb01d9 ./arm64-v8a/libfp_langpack.so
|
||||
a5b03bb7ae5d953d7ded2bf117b0a19c57fae604f16f54310847f8e89593dde6 ./arm64-v8a/libfp_ovrp.so
|
||||
6d2b9f8cea2f2cf33b8ff30d29a2f3d9c4d55549260a867168a24e44abada021 ./arm64-v8a/libfp_vk.so
|
||||
8e00552d1ab90f82749db3caad4f773351fa8a28edcc5ea5cbd1b31c434ff32a ./arm64-v8a/libframe_xrshim.so
|
||||
3dfcb0f2bec47857bcecc7261c58e076d872d4649718c40850f29e10c75477dd ./arm64-v8a/libglshim.so
|
||||
507bb1c62168da9335a5883ec264aabed2ce09d04b6524e67be504a273473f8d ./arm64-v8a/libopenxr_loader_generic.so
|
||||
5becb96ee86fbda0ce98c0e0fd064f8ba092314aceaa400e3dcc785705dc3c23 ./arm64-v8a/libVkLayer_fp_shaderfix.so
|
||||
e7554c6343ee2989b0a273ee6230e65c25bfe6499eefd00de19f5f0fee58754f ./arm64-v8a/libfp_langpack.so
|
||||
321da502e0f8f46f0880aad39fe6b84bdef88f925164568814cf704a1b31a577 ./arm64-v8a/libfp_ovrp.so
|
||||
40defdaddcda53bd2649eb48076fae1622bfc2bfc88e9e03c94fc8b45af2a4f2 ./arm64-v8a/libfp_ovrtrace.so
|
||||
e8c0965c188665a4e565e4a3af983ef1866d1cf9225665cc90d8df286bc96ef3 ./arm64-v8a/libfp_vk.so
|
||||
383054f8b3b41dde76d062c71856cd3163655e50009c08454bc1793f393ead65 ./arm64-v8a/libfpg.so
|
||||
5432674a59d02f411cd853a5dc57e3fa547d1ac518f25bf433fc54c43677177a ./arm64-v8a/libfpglmv.so
|
||||
8a8f6b1da8952cb3933a5b53558424a5fc56043b2527a16bb6dd62a00d2de01a ./arm64-v8a/libframe_xrshim.so
|
||||
19ba5ed3c4b780f5ae46251f6958f552acfc5f13c8bb39f995579593fa8990c6 ./arm64-v8a/libglshim.so
|
||||
449e13bbe58de6718d4e530177924e09552a95d748a082739899144e2cb3ccf3 ./arm64-v8a/libopenxr_loader_generic.so
|
||||
1feaeafad467c4cafdf2b018a4d84b0bee200c3f711697b4ce97e66a3ba256ca ./arm64-v8a/libovrplatformcompat.so
|
||||
32525cd0a9ee3d9993d8871cd6905bdcf22cde4f4ba28f5db6434ce7d6554758 ./arm64-v8a/libvrapi.so
|
||||
11b7019c6a8d3b2f0f89cf23011f84bbbcb8e907f0935978e1df675339abbb6e ./armeabi-v7a/libopenxr_loader_generic.so
|
||||
1d83eb94ec1df44f29a5f525dce4d5b608dbbb13a9c7a5efeb80a4b1dc54a721 ./arm64-v8a/libvrapi.so
|
||||
47f736dfeffce6aa56be2fc049c2d11b87b0d1f7389db7ee1737fcded9457811 ./armeabi-v7a/libopenxr_loader_generic.so
|
||||
1871eae093432d277da4bc751bf5f3269dfcf11f9168260b0c3caadb0dedb19d ./dex/oculusos-stubs.dex
|
||||
b634ab5640e258563c536e658cad87080553df6f34f62269a21d554844e58bfe ./hevc/COPYING.FFmpeg
|
||||
141de01f0d40b97d4f9a720db5ab8ea442f6aa35db5272048899292884994a1c ./hevc/libstagefrighthw.so
|
||||
4516971bbb2b635e19b14d2d37b9353962876f7de02043e7457327052c1fcbc0 ./hevc/manifest.json
|
||||
46c3dcad2c43cc30bea3b3680b362ed84a99c15d60714bbbbb422c80ecf90405 ./hevc/media_codecs_frameport.xml
|
||||
529ea35d5d82474afe9e09ad1216d269317f56dd24a05fb100fced52b7b4dbca ./hevc/podman.py.txt
|
||||
1aa733117cf57ccff7cf23f0425dbfd73b0332a3ef1c905ceeca0ccd0449c4e0 ./linux-arm64/XR_APILAYER_FRAMEPORT_timefix.json
|
||||
28c2430a02bbd8902c5bfb9562c6fd0e318654f9e05c095bfeb94b0450ab1b07 ./linux-arm64/libxr_frameport_timefix.so
|
||||
b10b3a5c10c3339fc63fd9f6cb4d01ea29be11a863d0ebad1389ea69fd471940 ./linux-arm64-bin/fp_venc
|
||||
c2c70f4af1a5c3e6089dff1130161dd417f8044f99ec639eeecf1083930aab16 ./win-x64/fp_oculushmd.exe
|
||||
60ebc5fea05b8945082e2ca8b9f2eb74616012a26eb50133af273dff0fcddc42 ./win-x64/fp_vrsettings.exe
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,502 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"revision": 8,
|
||||
"runtime_sha256": "456e912c75cd389abcf6a63bc80e2a53bdc334371d00b200c93680388ae955e2",
|
||||
"files": {
|
||||
"libstagefrighthw.so": "141de01f0d40b97d4f9a720db5ab8ea442f6aa35db5272048899292884994a1c",
|
||||
"podman.py": "529ea35d5d82474afe9e09ad1216d269317f56dd24a05fb100fced52b7b4dbca",
|
||||
"media_codecs_frameport.xml": "46c3dcad2c43cc30bea3b3680b362ed84a99c15d60714bbbbb422c80ecf90405",
|
||||
"COPYING.FFmpeg": "b634ab5640e258563c536e658cad87080553df6f34f62269a21d554844e58bfe"
|
||||
},
|
||||
"codecs": [
|
||||
"video/hevc",
|
||||
"video/avc",
|
||||
"video/x-vnd.on2.vp9"
|
||||
],
|
||||
"build": {
|
||||
"ndk_revision": "27.2.12479018",
|
||||
"ffmpeg_source_sha256": "733984395e0dbbe5c046abda2dc49a5544e7e0e1e2366bba849222ae9e3a03b1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<MediaCodecs>
|
||||
<Decoders>
|
||||
<MediaCodec name="OMX.frameport.hevc.decoder" type="video/hevc" rank="64">
|
||||
<Limit name="size" min="128x128" max="8192x8192" />
|
||||
<Limit name="alignment" value="2x2" />
|
||||
<Limit name="block-size" value="16x16" />
|
||||
<Limit name="block-count" range="1-138240" />
|
||||
<Limit name="blocks-per-second" range="1-7864320" />
|
||||
<Limit name="bitrate" range="1-245000000" />
|
||||
<Limit name="concurrent-instances" max="1" />
|
||||
</MediaCodec>
|
||||
<MediaCodec name="OMX.frameport.avc.decoder" type="video/avc" rank="64">
|
||||
<Limit name="size" min="128x128" max="8192x8192" />
|
||||
<Limit name="alignment" value="2x2" />
|
||||
<Limit name="block-size" value="16x16" />
|
||||
<Limit name="block-count" range="1-138240" />
|
||||
<Limit name="blocks-per-second" range="1-7864320" />
|
||||
<Limit name="bitrate" range="1-245000000" />
|
||||
<Limit name="concurrent-instances" max="1" />
|
||||
</MediaCodec>
|
||||
<MediaCodec name="OMX.frameport.vp9.decoder" type="video/x-vnd.on2.vp9" rank="64">
|
||||
<Limit name="size" min="128x128" max="4096x2304" />
|
||||
<Limit name="alignment" value="2x2" />
|
||||
<Limit name="block-size" value="16x16" />
|
||||
<Limit name="block-count" range="1-36864" />
|
||||
<Limit name="blocks-per-second" range="1-2211840" />
|
||||
<Limit name="bitrate" range="1-245000000" />
|
||||
<Limit name="concurrent-instances" max="1" />
|
||||
</MediaCodec>
|
||||
</Decoders>
|
||||
</MediaCodecs>
|
||||
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/python3
|
||||
"""Add shared codecs to Lepton containers without editing their shared rootfs."""
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
|
||||
def mounts(directory, config, args):
|
||||
if not args or args[0] != "run":
|
||||
return []
|
||||
name = None
|
||||
for i, arg in enumerate(args):
|
||||
if arg == "--name" and i + 1 < len(args):
|
||||
name = args[i + 1]
|
||||
elif arg.startswith("--name="):
|
||||
name = arg.partition("=")[2]
|
||||
if config.get("scope") == "shared":
|
||||
# The common launcher supplies these values. Do not intercept arbitrary
|
||||
# Podman containers or depend on a game's package/modified APK.
|
||||
appid = os.environ.get("SteamAppId", "")
|
||||
if not re.fullmatch(r"[0-9]+", appid) or name != f"lepton-steamlaunch-{appid}":
|
||||
return []
|
||||
app = Path(os.environ.get("STEAM_COMPAT_INSTALL_PATH", ""))
|
||||
if app.name != "lepton-app" or not app.is_dir():
|
||||
return []
|
||||
root_arg = None
|
||||
for i, arg in enumerate(args):
|
||||
if arg == "--rootfs" and i + 1 < len(args):
|
||||
root_arg = args[i + 1]
|
||||
elif arg.startswith("--rootfs="):
|
||||
root_arg = arg.partition("=")[2]
|
||||
if not root_arg or not root_arg.endswith(":O"):
|
||||
return []
|
||||
root = Path(root_arg[:-2]).resolve()
|
||||
else: # old per-game deployments remain compatible during migration
|
||||
if name != f"lepton-steamlaunch-{config['appid']}":
|
||||
return []
|
||||
root = Path(config["lepton"]).resolve().parent / "images" / "rootfs"
|
||||
expected_root = str(root) + ":O"
|
||||
if not any(arg in (expected_root, "--rootfs=" + expected_root) for arg in args):
|
||||
return []
|
||||
device = Path("/dev/video-dec0")
|
||||
runtime = root / "vendor/lib64/libstagefright_softomx.so"
|
||||
upstream_plugin = (root / "vendor/lib64/libstagefrighthw.so").exists()
|
||||
for i, arg in enumerate(args):
|
||||
if arg == "--mount" and i + 1 < len(args):
|
||||
fields = dict(item.split("=", 1) for item in args[i + 1].split(",") if "=" in item)
|
||||
target = fields.get("destination", fields.get("target"))
|
||||
if target == "/vendor/lib64/libstagefrighthw.so":
|
||||
upstream_plugin = True
|
||||
if target == "/vendor/lib64/libstagefright_softomx.so" and fields.get("source"):
|
||||
runtime = Path(fields["source"])
|
||||
if upstream_plugin:
|
||||
print("FramePort video: using the runtime's hardware codec plugin", file=sys.stderr)
|
||||
return []
|
||||
if not device.exists() or hashlib.sha256(runtime.read_bytes()).hexdigest() != config["runtime_sha256"]:
|
||||
print("FramePort video: device or runtime ABI differs; retaining the stock codecs", file=sys.stderr)
|
||||
return []
|
||||
xml = ET.parse(root / "vendor/etc/media_codecs.xml")
|
||||
if not any(node.get("href") == "media_codecs_frameport.xml" for node in xml.getroot().findall("Include")):
|
||||
ET.SubElement(xml.getroot(), "Include", href="media_codecs_frameport.xml")
|
||||
# Immutable plugin versions can serve simultaneous app launches and
|
||||
# different Lepton installations. Never share a temporary XML filename.
|
||||
# The version directory stays as the agent verified it: the merged list goes to the user's runtime dir.
|
||||
root_key = hashlib.sha256(str(root).encode()).hexdigest()[:16]
|
||||
merged = merged_dir() / f"media_codecs.{root_key}.xml"
|
||||
temporary = merged.with_suffix(f".{os.getpid()}.tmp")
|
||||
xml.write(temporary, encoding="utf-8", xml_declaration=True)
|
||||
temporary.replace(merged)
|
||||
# Lepton supplies its own /dev tmpfs. A Podman --device node disappears
|
||||
# beneath it; a bind mount matches Lepton's existing GPU/sound device setup.
|
||||
result = ["--mount", f"type=bind,source={device},destination=/dev/video-dec0,rw"]
|
||||
for path, target in (
|
||||
(directory / "libstagefrighthw.so", "/vendor/lib64/libstagefrighthw.so"),
|
||||
(merged, "/vendor/etc/media_codecs.xml"),
|
||||
(directory / "media_codecs_frameport.xml", "/vendor/etc/media_codecs_frameport.xml"),
|
||||
):
|
||||
if not path.is_file():
|
||||
raise FileNotFoundError(f"missing video codec mount: {path}")
|
||||
result += ["--mount", f"type=bind,source={path},destination={target},ro"]
|
||||
print("FramePort video: loading the Iris hardware codec plugin for this container", file=sys.stderr)
|
||||
return result
|
||||
|
||||
|
||||
def merged_dir():
|
||||
runtime = os.environ.get("XDG_RUNTIME_DIR", "")
|
||||
base = Path(runtime) if runtime and Path(runtime).is_dir() else Path.home() / ".cache"
|
||||
path = base / "frameport-video"
|
||||
path.mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
|
||||
def switched_off(directory):
|
||||
"""FRAMEPORT_NO_HW_VIDEO=1 (e.g. in a game's Steam launch options) or the Frame-wide switch (FramePort's
|
||||
setting; the agent writes video-codec/disabled) leave every container with Android's stock codecs."""
|
||||
if os.environ.get("FRAMEPORT_NO_HW_VIDEO", "") not in ("", "0"):
|
||||
return True
|
||||
return directory.parent.name == "versions" and (directory.parent.parent / "disabled").exists()
|
||||
|
||||
|
||||
def real_podman(directory):
|
||||
"""Find Podman independently of deployment.json, without recursing into this wrapper."""
|
||||
own_bin = (directory / "bin").resolve()
|
||||
own_script = Path(__file__).resolve()
|
||||
# Lepton runs its `podman exec` calls (boot wait, app pid, logcat mirror) with the Android guest's PATH
|
||||
# (/product/bin:/system/bin:...), which has no host Podman: the system folders come after PATH. Failing there
|
||||
# broke Lepton's logcat mirror and app-pid checks, and the container was stopped early.
|
||||
entries = os.environ.get("PATH", os.defpath).split(os.pathsep) + ["/usr/local/bin", "/usr/bin", "/bin"]
|
||||
for entry in entries:
|
||||
folder = Path(entry or os.curdir).resolve()
|
||||
if folder == own_bin:
|
||||
continue
|
||||
found = shutil.which("podman", path=str(folder))
|
||||
if found:
|
||||
executable = Path(found).resolve()
|
||||
if executable.parent != own_bin and executable != own_script:
|
||||
return str(executable)
|
||||
raise RuntimeError("no real Podman executable found outside the codec wrapper directory")
|
||||
|
||||
|
||||
def main():
|
||||
directory = Path(__file__).resolve().parent.parent
|
||||
args = sys.argv[1:]
|
||||
fallback = real_podman(directory)
|
||||
if switched_off(directory):
|
||||
os.execv(fallback, [fallback, *args])
|
||||
try:
|
||||
config = json.loads((directory / "deployment.json").read_text())
|
||||
podman = Path(config.get("podman", fallback)).resolve()
|
||||
if podman.parent == (directory / "bin").resolve() or podman == Path(__file__).resolve():
|
||||
raise ValueError("configured Podman points to the codec wrapper")
|
||||
extra = mounts(directory, config, args)
|
||||
launch_args = [args[0], *extra, *args[1:]] if extra else args
|
||||
os.execv(str(podman), [str(podman), *launch_args])
|
||||
except Exception as exc: # a codec/configuration failure must never prevent the stock container from starting
|
||||
print(f"FramePort video: retaining stock codecs: {exc}", file=sys.stderr)
|
||||
os.execv(fallback, [fallback, *args])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Binary file not shown.
@@ -30,13 +30,13 @@ user_systemd() {
|
||||
|
||||
say "FramePort setup for $(hostname) ($(. /etc/os-release; echo "$NAME $VERSION_ID"))"
|
||||
|
||||
say "Authorizing the FramePort app's key"
|
||||
say "Letting FramePort log in"
|
||||
key=$(curl -fsS "$PC_URL/key?code=$PAIR_CODE")
|
||||
[[ "$key" == ssh-ed25519\ * ]] || { echo "Could not fetch the app's key from $PC_URL (is the app still open?)"; exit 1; }
|
||||
[[ "$key" == ssh-ed25519\ * ]] || { echo "Couldn't reach FramePort at $PC_URL. Is it still open?"; exit 1; }
|
||||
mkdir -p ~/.ssh && chmod 700 ~/.ssh && touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys
|
||||
grep -qxF "$key" ~/.ssh/authorized_keys || echo "$key" >> ~/.ssh/authorized_keys
|
||||
|
||||
say "Configuring podman for Lepton"
|
||||
say "Setting up Lepton's containers"
|
||||
# rootless podman leaks one kernel keyring per container start; ~200 game launches would exhaust the quota
|
||||
mkdir -p ~/.config/containers
|
||||
grep -qs '^ *keyring *=' ~/.config/containers/containers.conf || printf '[containers]\nkeyring = false\n' >> ~/.config/containers/containers.conf
|
||||
@@ -117,7 +117,7 @@ JOB
|
||||
if [[ -f /etc/steamos-devkit-enabled ]]; then
|
||||
say "Developer Mode is on"
|
||||
bash "$JOB" finish "$PC_URL" "$PAIR_CODE" "$STEAM_CONFIG" "$DEVKIT_HELPER" 2>&1 | tee "$LOG"
|
||||
say "Done. Return to FramePort on your PC: this Frame should now appear as connected."
|
||||
say "Done. FramePort on your PC shows this Frame as connected."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -126,9 +126,8 @@ if [[ ! -x "$DEVKIT_HELPER" || ! -f "$STEAM_CONFIG" ]] || \
|
||||
! user_systemd systemd-run --user --collect --quiet --unit="frameport-setup-$$" \
|
||||
bash -c 'bash "$0" "$@" >"$HOME/.cache/frameport-setup.log" 2>&1' \
|
||||
"$JOB" devmode "$PC_URL" "$PAIR_CODE" "$STEAM_CONFIG" "$DEVKIT_HELPER"; then
|
||||
echo "Couldn't turn it on automatically. Turn it on in Settings > System > Developer, then run this command again."
|
||||
echo "Couldn't turn it on. Turn it on in Settings → System → Enable Developer Mode, then run this again."
|
||||
exit 1
|
||||
fi
|
||||
echo "Steam restarts to turn it on. That closes the desktop in a few seconds and the Frame returns to its"
|
||||
echo "normal view; setup finishes on its own. If Steam asks to install Lepton, confirm it."
|
||||
echo "Then return to FramePort on your PC: the Frame appears as connected within a minute."
|
||||
echo "Steam restarts and the desktop closes. Confirm Lepton if asked."
|
||||
echo "Then check FramePort on your PC."
|
||||
@@ -0,0 +1,234 @@
|
||||
#!/usr/bin/env bash
|
||||
# FramePort setup from the project page: the same for every Frame and every PC, nothing to copy from the PC. Run in
|
||||
# the Frame's desktop terminal (SteamVR dashboard -> Launch a program -> Desktop, then System -> Konsole):
|
||||
# curl -fsSL https://frameport.app/s | bash
|
||||
# with FramePort open on your PC at Steam Frame -> Connect. Options: --pc <address[:port]> (skip the search).
|
||||
#
|
||||
# What it does:
|
||||
# 1. finds FramePort on your network (it announces itself over mDNS while that page is open; else the USB cable's
|
||||
# address and a quick scan of this network)
|
||||
# 2. asks it to set up this Frame: FramePort shows the same 4 digits as this terminal, you click Allow there
|
||||
# 3. runs FramePort's setup script from your PC, the one the typed line `curl -fsS <pc>/<code> | bash` runs (it
|
||||
# authorizes the app's key, configures podman, turns on Developer Mode, asks for Lepton)
|
||||
# Nothing changes on the Frame before you allow it on the PC.
|
||||
set -euo pipefail
|
||||
say() { printf '\n\033[1;36m==> %s\033[0m\n' "$*"; }
|
||||
PORTS="8765 8766 8767"
|
||||
USB_PC=10.86.200.234 # the PC's fixed address on the Frame's USB cable network (Developer Mode only)
|
||||
|
||||
PC=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--pc) PC=${2:-}; shift 2 ;;
|
||||
--pc=*) PC=${1#--pc=}; shift ;;
|
||||
-h|--help) sed -n '2,13p' "$0" 2>/dev/null || true; exit 0 ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
for tool in curl python3; do
|
||||
command -v "$tool" >/dev/null || { echo "This needs $tool, which SteamOS normally has. Run the setup command FramePort shows instead."; exit 1; }
|
||||
done
|
||||
|
||||
# FramePort PCs, one per line: name<TAB>address:port<TAB>words. Stdlib Python: avahi isn't always answering on the
|
||||
# Frame, and its firewall only lets mDNS in on port 5353, so this listens there in the group, like avahi does.
|
||||
find_pcs() {
|
||||
python3 - "$PORTS" "$USB_PC" <<'PY'
|
||||
import json, socket, struct, sys, time, urllib.request
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
PORTS = [int(p) for p in sys.argv[1].split()]
|
||||
USB_PC = sys.argv[2]
|
||||
SERVICE = "_frameport-pair._tcp.local"
|
||||
|
||||
def qname(name):
|
||||
return b"".join(bytes([len(p)]) + p.encode() for p in name.split(".")) + b"\0"
|
||||
|
||||
def read_name(d, off):
|
||||
labels, jumped, end = [], False, off
|
||||
while True:
|
||||
n = d[off]
|
||||
if n == 0:
|
||||
off += 1
|
||||
break
|
||||
if n & 0xC0 == 0xC0:
|
||||
ptr = struct.unpack("!H", d[off:off + 2])[0] & 0x3FFF
|
||||
if not jumped:
|
||||
end = off + 2
|
||||
jumped, off = True, ptr
|
||||
continue
|
||||
labels.append(d[off + 1:off + 1 + n].decode(errors="replace"))
|
||||
off += 1 + n
|
||||
return ".".join(labels), (end if jumped else off)
|
||||
|
||||
def mdns(seconds=3.0):
|
||||
found, srv, txt, addr = {}, {}, {}, {}
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
if hasattr(socket, "SO_REUSEPORT"):
|
||||
try:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
except OSError:
|
||||
pass
|
||||
s.bind(("", 5353))
|
||||
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton("224.0.0.251") + socket.inet_aton("0.0.0.0"))
|
||||
query = struct.pack("!6H", 0, 0, 1, 0, 0, 0) + qname(SERVICE) + struct.pack("!HH", 12, 1)
|
||||
s.sendto(query, ("224.0.0.251", 5353))
|
||||
except OSError:
|
||||
return []
|
||||
end, again = time.time() + seconds, True
|
||||
while time.time() < end:
|
||||
if again and time.time() > end - seconds / 2:
|
||||
s.sendto(query, ("224.0.0.251", 5353))
|
||||
again = False
|
||||
s.settimeout(max(0.05, min(0.5, end - time.time())))
|
||||
try:
|
||||
d, src = s.recvfrom(9000)
|
||||
except socket.timeout:
|
||||
continue
|
||||
try:
|
||||
_, _, qd, an, ns, ar = struct.unpack("!6H", d[:12])
|
||||
off = 12
|
||||
for _ in range(qd):
|
||||
_, off = read_name(d, off)
|
||||
off += 4
|
||||
for _ in range(an + ns + ar):
|
||||
name, off = read_name(d, off)
|
||||
rtype, _, _, rdlen = struct.unpack("!HHIH", d[off:off + 10])
|
||||
off += 10
|
||||
if rtype == 12 and name.lower() == SERVICE:
|
||||
found.setdefault(read_name(d, off)[0], src[0])
|
||||
elif rtype == 33:
|
||||
srv[name] = (read_name(d, off + 6)[0], struct.unpack("!H", d[off + 4:off + 6])[0])
|
||||
elif rtype == 16:
|
||||
items, p = {}, off
|
||||
while p < off + rdlen:
|
||||
n = d[p]
|
||||
k, _, v = d[p + 1:p + 1 + n].decode(errors="replace").partition("=")
|
||||
items[k] = v
|
||||
p += 1 + n
|
||||
txt[name] = items
|
||||
elif rtype == 1 and rdlen == 4:
|
||||
addr[name] = socket.inet_ntoa(d[off:off + 4])
|
||||
off += rdlen
|
||||
except (IndexError, struct.error, UnicodeError):
|
||||
continue
|
||||
out = []
|
||||
for inst, src in found.items():
|
||||
target, port = srv.get(inst, ("", PORTS[0]))
|
||||
props = txt.get(inst, {})
|
||||
out.append((props.get("pc") or inst.split(".")[0], f"{addr.get(target) or src}:{port}", props.get("words", "")))
|
||||
return out
|
||||
|
||||
def ping(host_port):
|
||||
try:
|
||||
with urllib.request.urlopen(f"http://{host_port}/ping", timeout=1.5) as r:
|
||||
info = json.load(r)
|
||||
return (info.get("pc", host_port), host_port, info.get("words", ""))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def scan():
|
||||
"""This network's /24 (and the USB cable's PC address) for a FramePort setup port."""
|
||||
hosts = [f"{USB_PC}:{p}" for p in PORTS]
|
||||
try:
|
||||
out = __import__("subprocess").run(["ip", "-4", "-o", "addr"], capture_output=True, text=True).stdout
|
||||
except OSError:
|
||||
out = ""
|
||||
for line in out.splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) > 3 and parts[1] not in ("lo",) and "/" in parts[3]:
|
||||
mine = parts[3].split("/")[0]
|
||||
base = mine.rsplit(".", 1)[0]
|
||||
hosts += [f"{base}.{i}:{p}" for i in range(1, 255) if f"{base}.{i}" != mine for p in PORTS[:1]]
|
||||
def open_port(hp):
|
||||
h, p = hp.rsplit(":", 1)
|
||||
try:
|
||||
with socket.create_connection((h, int(p)), timeout=0.4):
|
||||
return hp
|
||||
except OSError:
|
||||
return None
|
||||
with ThreadPoolExecutor(64) as ex:
|
||||
live = [hp for hp in ex.map(open_port, hosts) if hp]
|
||||
return [r for r in map(ping, live) if r]
|
||||
|
||||
pcs = mdns()
|
||||
if not pcs:
|
||||
pcs = scan()
|
||||
seen = set()
|
||||
for name, hp, words in pcs:
|
||||
key = (name, words) if words else hp # one PC on several links (home Wi-Fi and the Frame's hotspot): once
|
||||
if key not in seen:
|
||||
seen.add(key)
|
||||
print(f"{name}\t{hp}\t{words}")
|
||||
PY
|
||||
}
|
||||
|
||||
say "FramePort setup for $(hostname)"
|
||||
if [[ -n "$PC" ]]; then
|
||||
[[ "$PC" == *:* ]] || PC="$PC:${PORTS%% *}"
|
||||
WORDS=""
|
||||
else
|
||||
echo "Looking for FramePort on your network (in FramePort on your PC: Steam Frame → Start setup)..."
|
||||
mapfile -t pcs < <(find_pcs)
|
||||
if [[ ${#pcs[@]} -eq 0 ]]; then
|
||||
echo
|
||||
echo "FramePort wasn't found. Check that:"
|
||||
echo " - FramePort is open on your PC, at Steam Frame → Start setup"
|
||||
echo " - the Frame and the PC are on the same Wi-Fi (or connected with a USB cable)"
|
||||
echo "Or run the setup command FramePort shows there (Use the setup command)."
|
||||
exit 1
|
||||
fi
|
||||
pick=0
|
||||
if [[ ${#pcs[@]} -gt 1 ]]; then
|
||||
echo "More than one FramePort is open on this network:"
|
||||
for i in "${!pcs[@]}"; do
|
||||
IFS=$'\t' read -r name hp words <<<"${pcs[$i]}"
|
||||
echo " $((i + 1))) $name ($hp) $words"
|
||||
done
|
||||
# stdin is this script itself (curl | bash): ask on the terminal; without one, take the first
|
||||
if { exec 3</dev/tty; } 2>/dev/null; then
|
||||
read -r -u 3 -p "Which one? [1-${#pcs[@]}] " n
|
||||
exec 3<&-
|
||||
[[ "$n" =~ ^[0-9]+$ && $n -ge 1 && $n -le ${#pcs[@]} ]] || { echo "No such number."; exit 1; }
|
||||
pick=$((n - 1))
|
||||
else
|
||||
echo "No terminal to ask in: using the first. (Choose with --pc <address:port>.)"
|
||||
fi
|
||||
fi
|
||||
IFS=$'\t' read -r name PC WORDS <<<"${pcs[$pick]}"
|
||||
echo "Found FramePort on $name ($PC)${WORDS:+, PC words: $WORDS}"
|
||||
fi
|
||||
|
||||
nonce=$(python3 -c 'import secrets; print(secrets.token_hex(12))')
|
||||
digits=$(python3 -c 'import hashlib, sys; print(f"{int(hashlib.sha256(sys.argv[1].encode()).hexdigest()[:8], 16) % 10000:04d}")' "$nonce")
|
||||
tmp=$(mktemp)
|
||||
trap 'rm -f "$tmp"' EXIT
|
||||
status=$(curl -sS -o "$tmp" -w '%{http_code}' -G --data-urlencode "host=$(hostname)" --data-urlencode "nonce=$nonce" \
|
||||
"http://$PC/hello") || true
|
||||
case $status in
|
||||
200) reply=$(cat "$tmp") ;;
|
||||
429) echo "FramePort at $PC already has several setup requests waiting. Allow or deny them there (requests from"
|
||||
echo "setups that ended go away by themselves within a few minutes), then run this again."; exit 1 ;;
|
||||
000|"") echo "FramePort at $PC didn't answer. Is it still open at Steam Frame → Start setup?"; exit 1 ;;
|
||||
*) echo "FramePort at $PC refused the request (HTTP $status). Run this again, or run the setup command FramePort shows."
|
||||
exit 1 ;;
|
||||
esac
|
||||
id=$(python3 -c 'import json, sys; print(json.loads(sys.argv[1])["id"])' "$reply")
|
||||
WORDS=$(python3 -c 'import json, sys; print(json.loads(sys.argv[1]).get("words", ""))' "$reply")
|
||||
|
||||
say "FramePort on your PC asks to set up this Frame"
|
||||
printf ' Click Allow there if it shows \033[1;33m%s\033[0m (PC: %s)\n' "$digits" "${WORDS:-?}"
|
||||
code=""
|
||||
for _ in $(seq 12); do # up to about 20 minutes
|
||||
status=$(curl -sS -o "$tmp" -w '%{http_code}' -m 115 "http://$PC/wait?id=$id" || echo 000)
|
||||
case $status in
|
||||
200) code=$(python3 -c 'import json, sys; print(json.load(open(sys.argv[1]))["code"])' "$tmp"); break ;;
|
||||
202) continue ;;
|
||||
403) echo "Not allowed on the PC. Nothing was changed on this Frame."; exit 1 ;;
|
||||
*) echo "Lost FramePort at $PC (HTTP $status). Run this again, or run the setup command FramePort shows."; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
[[ -n "$code" ]] || { echo "Not allowed in time. Run this again when you're at your PC."; exit 1; }
|
||||
|
||||
say "Allowed. Running the setup from $PC"
|
||||
curl -fsS "http://$PC/$code" | bash
|
||||
@@ -6,10 +6,14 @@ notes: 'Native OpenXR GLES video player. 360 theatres/videos (equirect2 layers,
|
||||
at 72 fps (the runtime asks for 4536 px per eye). Confirmed in the headset by the owner (2026-10-01). Its "Internal
|
||||
Storage" list is /sdcard/4XPlayer: send videos with the game page''s "Add videos".'
|
||||
details: 'Settings: equirect_emul (GLES worker draws 360 layers into a projection layer), stable_local, focus_hold,
|
||||
scale 0.75; frame.swapchain_limit for its 7680x3840 theatre swapchains.'
|
||||
scale 0.75; frame.swapchain_limit for its 7680x3840 theatre swapchains. Its hardware decoding mode (Java MediaCodec,
|
||||
Vr4pMediaPlayer; its own FFmpeg is the software path) gets the Frame''s video hardware with frame.hw_video_decode
|
||||
(added 2026-10-09, not yet checked in the headset).'
|
||||
tested_version: 2.0.22
|
||||
engine: Other
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.hw_video_decode
|
||||
adapter:
|
||||
equirect_emul: 1
|
||||
stable_local: 1
|
||||
@@ -17,4 +21,6 @@ adapter:
|
||||
scale: 0.75
|
||||
verified:
|
||||
date: '2026-10-01'
|
||||
updated: '2026-10-10'
|
||||
min_app: 0.12.1
|
||||
source_hint: 4XVR Video Player
|
||||
@@ -0,0 +1,21 @@
|
||||
package: com.Aldin.WotW
|
||||
title: 'Waltz of the Wizard: Extended Edition'
|
||||
status: works
|
||||
notes: From what I tested, it just works. My only complaint is that the haptics go pretty crazy. But I don't remember
|
||||
what it was like when I played it in my Quest. So it could be fine. 100% playable.
|
||||
tested_version: 5.0.8.1578
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
verified:
|
||||
date: '2026-10-05'
|
||||
known_good_sha256: 522cfebf954e1cab6054798479b0b32c5ba9e38cca83dd98db2ac0f7e3c0802a
|
||||
app: 0.9.1
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20260922.6101926'
|
||||
agent: 48
|
||||
issue: 44
|
||||
source_hint: WotW
|
||||
@@ -1,9 +1,9 @@
|
||||
package: com.Armature.VR4
|
||||
title: VR4
|
||||
status: works
|
||||
notes: Installs the no-ForceQuit build and fixes a campaign shader that hung the GPU after the opening cutscene.
|
||||
details: Entering the campaign gave a black screen, slowing audio and a crash (Mercenaries loaded). One fragment shader
|
||||
reads a loop counter before setting it, which hangs the GPU; the Vulkan shim zeroes it (vk_shader_fix). The
|
||||
notes: Installs the no-ForceQuit build and fixes two campaign shaders that hung the GPU at cutscenes.
|
||||
details: Entering the campaign gave a black screen, slowing audio and a crash (Mercenaries loaded). Two permutations of one fragment shader
|
||||
read a loop counter before setting it, which hangs the GPU; the Vulkan shim zeroes it (vk_shader_fix). The
|
||||
no-ForceQuit build is kept. Diagnosed and confirmed on a Frame by the reporter of GitHub issue 10.
|
||||
tested_version: '2.3'
|
||||
engine: Unreal
|
||||
@@ -14,8 +14,8 @@ use_alt: true
|
||||
adapter:
|
||||
# one campaign shader reads an uninitialized loop counter and hangs the GPU (black screen, slowing audio, crash);
|
||||
# the Vulkan shim inserts two OpStores that zero it and its accumulator (from the reporter's capture, GitHub
|
||||
# issue 10)
|
||||
vk_shader_fix: 6488:5dcd842db8d21e3fdd13ec916d2e34dd0cfdf12a92fffaf8a27115072ef55c59:2624:0x0003003e,138,18,0x0003003e,150,42
|
||||
# issue 10); a second permutation of the same shader (6604 bytes) hung the GPU at a later cutscene (GitHub #140)
|
||||
vk_shader_fix: 6488:5dcd842db8d21e3fdd13ec916d2e34dd0cfdf12a92fffaf8a27115072ef55c59:2624:0x0003003e,138,18,0x0003003e,150,42;6604:a0e6edd8a8e969acbefe1a193f0bc535d8c0c18d004def4c361840a9f2601f13:2956:0x0003003e,122,18,0x0003003e,135,42
|
||||
verified:
|
||||
date: '2026-10-02'
|
||||
known_good_sha256: 40ff74e37264b28ae8826923b69d0731d9620094c76462be34159643d6ae91b3
|
||||
@@ -24,5 +24,5 @@ verified:
|
||||
frame_build: '20260930.6234839'
|
||||
agent: 26
|
||||
issue: 1
|
||||
updated: '2026-10-04'
|
||||
updated: '2026-10-11'
|
||||
source_hint: Resident Evil 4
|
||||
@@ -0,0 +1,36 @@
|
||||
package: com.CMGames.IntoTheRadius2
|
||||
title: Into The Radius 2
|
||||
status: works
|
||||
notes: Plays well (owner-verified 2026-10-05). Space warp is off, the headset's brief focus losses are hidden
|
||||
(focus_hold, up to 2.5 s), only Valve's foveation layer is loaded (its render-pass optimizer removed the fog) and
|
||||
an invalid depth resolve in Unreal's render passes is dropped (vk_spec_fixes; it made models flicker and windows
|
||||
show behind them). Needs FramePort 0.10.0 or newer. Loading a save can take over a minute.
|
||||
details: Unreal Engine 5 game that uses Meta's Application SpaceWarp (extra motion-vector and depth swapchains). OVRPort's
|
||||
patch_disable_space_warp doesn't reach Unreal Engine 5, so FrameBridge's hide_space_warp hides XR_FB_space_warp and
|
||||
removes the space warp info from the eye images. Working config shared in GitHub issue 35. In the headset
|
||||
(2026-10-05) the Frame dropped focus for 0.4 s and 1.0 s within ten seconds and Unreal paused each time; loading a save
|
||||
shows no frames for 40-80 s while the game's Sentry SDK (debug mode) logs its breadcrumbs. Valve's VALVE_rpo
|
||||
(render-pass optimizer) Vulkan layer removes the game's fog (owner-verified 2026-10-05; with only VALVE_fdm_injection
|
||||
loaded the fog renders), which also hid the distance culling; ruled out for the remaining pop-in - Vulkan occlusion
|
||||
query slots, Valve's FDM layer. The cause, found with Khronos' validation layer (vk_validation) - Unreal's render
|
||||
passes use Qualcomm's shader resolve (VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM) together with a depth resolve
|
||||
of a single-sampled depth attachment (VUID-VkSubpassDescription2-flags-04908, -03179), harmless on Quest's driver,
|
||||
wrong depth on the Frame's Mesa; the Vulkan shim's vk_spec_fixes drops that resolve.
|
||||
tested_version: 1.2.4
|
||||
engine: Unreal
|
||||
xr: OpenXR
|
||||
alt_overport:
|
||||
- patch_remove_unreal_force_quit
|
||||
adapter:
|
||||
equirect_emul: 1
|
||||
hide_space_warp: 1
|
||||
focus_hold: 1
|
||||
focus_hold_ms: 2500
|
||||
vk_spec_fixes: 1
|
||||
lepton_env:
|
||||
VK_INSTANCE_LAYERS: VK_LAYER_VALVE_fdm_injection
|
||||
verified:
|
||||
date: '2026-10-05'
|
||||
issue: 35
|
||||
min_app: 0.10.0
|
||||
source_hint: Into The Radius 2
|
||||
@@ -0,0 +1,23 @@
|
||||
package: com.CortopiaStudios.DTRH
|
||||
title: Down the Rabbit Hole
|
||||
status: works
|
||||
tested_version: 01.04.434
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_gl_shim
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: ce9e02e9b8fc64dc37bec2bd869d2ed1c4a99f28adffd6b5ec38c87b3cb639f7
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 145
|
||||
source_hint: '8430807483680247'
|
||||
@@ -0,0 +1,19 @@
|
||||
package: com.CortopiaStudios.Graves
|
||||
title: GORN2
|
||||
status: works
|
||||
tested_version: 1.10.1
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-07'
|
||||
known_good_sha256: d8a7949edc90c19902eb89f888ad6a350475387d630991767b3097667ad16d4c
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
issue: 88
|
||||
source_hint: GORN 2
|
||||
@@ -0,0 +1,24 @@
|
||||
package: com.CyberneticWalrus.DoesitStackMeta
|
||||
title: Does it Stack?
|
||||
status: issues
|
||||
notes: Mixed reality mode does not work (works on Demeo), everything else seems to be OK
|
||||
tested_version: 1.02 (3683)
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.oculusos
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
refresh_rate: 90.0
|
||||
verified:
|
||||
date: '2026-10-09'
|
||||
known_good_sha256: e9fce6ac812f1bb317a7a6959be89c5ff54e87f56af9ad4c42dff5fecad4efe8
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261006.6173745'
|
||||
agent: 59
|
||||
issue: 112
|
||||
source_hint: Does It Stack
|
||||
@@ -0,0 +1,25 @@
|
||||
package: com.FireproofStudios.TheRoomVR
|
||||
title: The Room VR
|
||||
status: works
|
||||
notes: Plays normally (it used to stay on a grey screen).
|
||||
details: Unity 2018.4 with built-in Oculus support. It started VR only with frame.unity_oculus_check, then Unity's
|
||||
render thread crashed in the GL driver (Zink) when OVROverlay copied a 4x4 fade texture into its overlay layer;
|
||||
frame.unity_no_overlay_copy skips that copy (the fade still works). frame.unity_runtime_msaa_off keeps the eye buffer
|
||||
at one sample (also GitHub issue 38).
|
||||
tested_version: '1.02'
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
frame:
|
||||
- frame.unity_no_msaa
|
||||
- frame.unity_oculus_check
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_no_overlay_copy
|
||||
device:
|
||||
- device.text_input_window
|
||||
verified:
|
||||
date: '2026-10-05'
|
||||
issue: 38
|
||||
updated: '2026-10-05'
|
||||
min_app: 0.11.0
|
||||
source_hint: The Room VR
|
||||
@@ -0,0 +1,20 @@
|
||||
package: com.ForwardXP.nuke
|
||||
title: Please Don't Touch Anything
|
||||
status: works
|
||||
notes: Seems functional
|
||||
tested_version: '2.2'
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
verified:
|
||||
date: '2026-10-08'
|
||||
known_good_sha256: 17460c8691b9ff82f6c7ca5597afd0aacf1a94aa30ebb6a6108802991b60f51b
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261006.6173745'
|
||||
agent: 59
|
||||
issue: 106
|
||||
source_hint: apks
|
||||
@@ -0,0 +1,21 @@
|
||||
package: com.FourPlayersStudio.Retronika
|
||||
title: Retronika
|
||||
status: works
|
||||
tested_version: 3.2.601
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-07'
|
||||
known_good_sha256: 290bfda3969547d5f1ff621ab5a8be9dd322fc9eee6b243c863576d4ff5b4b81
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261006.6173745'
|
||||
agent: 59
|
||||
issue: 97
|
||||
source_hint: Retronika
|
||||
@@ -0,0 +1,21 @@
|
||||
package: com.FunktronicLabs.TheLightBrigade
|
||||
title: The Light Brigade
|
||||
status: issues
|
||||
notes: Judder in weapons
|
||||
tested_version: '746'
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-09'
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 114
|
||||
source_hint: The Light Brigade
|
||||
@@ -0,0 +1,36 @@
|
||||
package: com.ILMxLAB.VaderImmortal.ep1
|
||||
title: 'Vader Immortal: Episode I'
|
||||
status: works
|
||||
notes: 'Plays (owner''s headset test): loading card, grip/trigger, hands and the lightspeed sequence fixed. Thumbs
|
||||
follow the touches with frame.unreal_thumb_touch (Klownicle''s engine fix).'
|
||||
details: Unreal Engine 4 (GLES). Found and verified in gameplay on a Frame by Klownicle (GitHub issue 49); FramePort
|
||||
reimplements them as patches that match the game's code exactly and change nothing when it differs. The engine's
|
||||
Quest checks (IsRunningOnSantaCruz) answer "not a Quest" on the Frame - the menu waited for a Quest-only shader
|
||||
precompile that never starts (frame.unreal_quest_precompile makes the non-Quest branch report 100 %), and the
|
||||
game's key map picked the empty Gear VR controls (frame.unreal_quest_keymap keeps the Quest set). Two lightspeed
|
||||
shaders, as the Frame's GL driver (Zink) compiles them, read loop counters and accumulators before setting them
|
||||
and hang the GPU; a Vulkan layer under Zink inserts the stores that zero them (frame.zink_shader_fix, zink_shader_fix;
|
||||
the captured modules are from Lepton 2.8.14 and stop matching if a Frame update changes the driver's output -
|
||||
the layer logs that). OVRPlugin asks for poses at its own clock (pose_time_fix). Unreal's Oculus input animates the
|
||||
thumbs from near-touch, which the Frame never reports (frame.unreal_thumb_touch reads the touches instead; binding
|
||||
the proximity to the touch inputs in FrameBridge didn't reach the game). The Frame's SteamVR runtime
|
||||
leaks about 20 MB a second while the game runs (reported to Valve).
|
||||
tested_version: 1.1.1+667256.cl.387770
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
alt_overport:
|
||||
- patch_remove_unreal_force_quit
|
||||
frame:
|
||||
- frame.unreal_quest_precompile
|
||||
- frame.unreal_quest_keymap
|
||||
- frame.unreal_thumb_touch
|
||||
- frame.zink_shader_fix
|
||||
adapter:
|
||||
pose_time_fix: 1
|
||||
zink_shader_fix: 12016:b2919629761ad268e0b21c72ac8520f81b76e72c3adc26897f97eae9a3e70a66:3092:0x0003003e,166,82,0x0003003e,177,82,0x0003003e,183,82,0x0003003e,189,82,0x0003003e,256,82,0x0003003e,266,82,0x0003003e,272,82,0x0003003e,278,82,0x0003003e,318,82,0x0003003e,328,82,0x0003003e,334,82,0x0003003e,340,82;11436:6f18be49f2aaa2452f962eb16f7af744c5c22a7b25f37ce681e0d6399e35a869:2932:0x0003003e,164,80,0x0003003e,175,80,0x0003003e,181,80,0x0003003e,187,80,0x0003003e,254,80,0x0003003e,264,80,0x0003003e,270,80,0x0003003e,276,80,0x0003003e,316,80,0x0003003e,326,80,0x0003003e,332,80,0x0003003e,338,80
|
||||
verified:
|
||||
date: '2026-10-09'
|
||||
issue: 49
|
||||
updated: '2026-10-09'
|
||||
min_app: 0.12.1
|
||||
source_hint: Vader Immortal- Episode I
|
||||
@@ -0,0 +1,24 @@
|
||||
package: com.ILMxLAB.VaderImmortal.ep2
|
||||
title: 'Vader Immortal: Episode II'
|
||||
status: works
|
||||
notes: Plays (owner's headset test, 2026-10-09) with Episode I's fixes, which FramePort finds in this episode's
|
||||
code too.
|
||||
details: 'Unreal Engine 4 (GLES), the same engine build as Episode I: the Quest-only shader precompile and key map
|
||||
(frame.unreal_quest_precompile, frame.unreal_quest_keymap), thumbs from the capacitive touches (frame.unreal_thumb_touch)
|
||||
and poses at OVRPlugin''s clock (pose_time_fix). Episode I''s lightspeed shader fix doesn''t apply. The campaign
|
||||
intro once stayed black with its dialogue playing; it rendered on the next run.'
|
||||
tested_version: 2.0.3+667261.cl.387778
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
alt_overport:
|
||||
- patch_remove_unreal_force_quit
|
||||
frame:
|
||||
- frame.unreal_quest_precompile
|
||||
- frame.unreal_quest_keymap
|
||||
- frame.unreal_thumb_touch
|
||||
adapter:
|
||||
pose_time_fix: 1
|
||||
verified:
|
||||
date: '2026-10-09'
|
||||
min_app: 0.12.1
|
||||
source_hint: Vader Immortal- Episode II
|
||||
@@ -0,0 +1,24 @@
|
||||
package: com.ILMxLAB.VaderImmortal.ep3
|
||||
title: 'Vader Immortal: Episode III'
|
||||
status: works
|
||||
notes: Plays (owner's headset test, 2026-10-09) with Episode I's fixes, which FramePort finds in this episode's
|
||||
code too.
|
||||
details: 'Unreal Engine 4 (GLES), the same engine build as Episode I: the Quest-only shader precompile and key map
|
||||
(frame.unreal_quest_precompile, frame.unreal_quest_keymap), thumbs from the capacitive touches (frame.unreal_thumb_touch)
|
||||
and poses at OVRPlugin''s clock (pose_time_fix). Episode I''s lightspeed shader fix doesn''t apply. The campaign
|
||||
intro starts on a black screen with only dialogue: that is the game, not a bug.'
|
||||
tested_version: 3.0.3+667263.cl.387932
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
alt_overport:
|
||||
- patch_remove_unreal_force_quit
|
||||
frame:
|
||||
- frame.unreal_quest_precompile
|
||||
- frame.unreal_quest_keymap
|
||||
- frame.unreal_thumb_touch
|
||||
adapter:
|
||||
pose_time_fix: 1
|
||||
verified:
|
||||
date: '2026-10-09'
|
||||
min_app: 0.12.1
|
||||
source_hint: Vader Immortal- Episode III
|
||||
@@ -4,14 +4,25 @@ tested_version: '1.03'
|
||||
source_hint: Sniper Elite VR
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
status: unsupported
|
||||
notes: 'GPU hang (zink: DEVICE LOST) even with MSAA off.'
|
||||
details: Legacy VrApi via OVRPlugin.
|
||||
status: works
|
||||
notes: Plays with controllers that follow the hands (owner's headset test, Quest 1 build 35713).
|
||||
details: 'Unity 2019.4 on its built-in Oculus VR (no Oculus XR Plugin). Its legacy frame loop never waited for frames
|
||||
(GPU hang, zink: DEVICE LOST) and Unity reported only Go controllers: frame.unity_oculus_check. Its frame wait then
|
||||
deadlocked at the Init scene (Unity skipped beginning a frame) and the physics-step pose update located the
|
||||
controllers in the past (hands lagged): both fixed in that patch (revision 5), switched on for this game by ovrp_begin_gate and ovrp_hold_physics. Grabbing an object in the tutorial
|
||||
vibrates the controller; OVRPort''s envelope conversion then allocated ~1 GB/s until the memory cap ended the game:
|
||||
haptic_fix. The Quest 1 build has no arraysources_assets.hd, which the game loads on the Frame (it detects a Quest
|
||||
2) and then skips the .sd bundle: textures are missing unless the .sd file is also present under the .hd name in the
|
||||
game''s obb folder.'
|
||||
frame:
|
||||
- frame.unity_no_msaa
|
||||
- frame.unity_oculus_check
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
ovrp_begin_gate: 1
|
||||
ovrp_hold_physics: 1
|
||||
pcvr_alternative: Sniper Elite VR (Steam PC VR). Winter Warrior (Quest) works on the Frame.
|
||||
verified:
|
||||
date: '2026-09-28'
|
||||
overport_cli: 1.2.3
|
||||
overport_runtime: 3.4.3-23204ea
|
||||
known_good_sha256: e80883eb6945e267a375edf0ffd789268686440819add9d4dee9fdbdf466cbe4
|
||||
date: '2026-10-07'
|
||||
updated: '2026-10-07'
|
||||
min_app: 0.12.1
|
||||
@@ -0,0 +1,24 @@
|
||||
package: com.MightyCoconut.WalkaboutMiniGolf
|
||||
title: Walkabout Mini Golf
|
||||
status: works
|
||||
tested_version: '6.7'
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.oculusos
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
controller_models: 1
|
||||
haptic_fix: 1
|
||||
scale: 1.5
|
||||
verified:
|
||||
date: '2026-10-08'
|
||||
known_good_sha256: cf3b5cbe131af8a2baaa3f5c665b1c7001c8b299473abfcf98d90e3c02b08a2f
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 111
|
||||
source_hint: Walkabout Mini Golf
|
||||
@@ -1,13 +1,14 @@
|
||||
package: com.NewFolderGames.IAmCat
|
||||
title: I Am Cat
|
||||
status: issues
|
||||
notes: The view vibrates slightly, even when holding still.
|
||||
status: works
|
||||
notes: Steady view with pose_consistency (the judder is gone).
|
||||
details: Unity 2022.3 with the Oculus XR Plugin falls back to GLES (Zink) on the Frame, where its single-pass
|
||||
(multiview) rendering drew the right eye wrong (GitHub issue 8); frame.unity_multipass renders each eye in its own
|
||||
pass (OculusSettings.GetStereoRenderingMode + its inlined read in OculusLoader.Initialize). Poses and frame times
|
||||
are consistent, but every frame reaches the runtime one display period late (two passes on Zink) and the game
|
||||
clamps its physics step to 10 ms, so the view judders; scale 0.8 didn't change it. Possible next steps are fixing the
|
||||
multiview path instead or the game's fixed timestep.
|
||||
clamps its physics step to 10 ms. The judder came from the game locating its views about three times per frame and
|
||||
getting slightly different poses each time; FrameBridge pose_consistency returns the same views for repeated
|
||||
queries of one display time (proposed in GitHub issue 8). The grey right eye on the loading screen is still open.
|
||||
tested_version: 1.4.0.0
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
@@ -16,8 +17,11 @@ frame:
|
||||
- frame.unity_multipass
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
pose_consistency: 1
|
||||
verified:
|
||||
date: '2026-10-03'
|
||||
date: '2026-10-05'
|
||||
issue: 8
|
||||
updated: '2026-10-04'
|
||||
updated: '2026-10-05'
|
||||
min_app: 0.11.0
|
||||
source_hint: I Am Cat
|
||||
@@ -0,0 +1,22 @@
|
||||
package: com.PeanutButton.Retropolis
|
||||
title: Retropolis
|
||||
status: works
|
||||
tested_version: '0.87'
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.oculusos
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: 4656b980fccd812d8edac15805dcd86c2f44029abdab98c3187699d1fc878f88
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 153
|
||||
source_hint: com.PeanutButton.Retropolis
|
||||
@@ -0,0 +1,29 @@
|
||||
package: com.PixelToys.BattleSisters
|
||||
title: BattleSisters
|
||||
status: works
|
||||
notes: Plays; controller buttons and vibration work, hands follow the controllers (pose_time_fix).
|
||||
details: Unity 2019.4 with Unity's built-in Oculus support. frame.unity_oculus_check starts VR (Meta's system-app
|
||||
check), adds the frame wait its legacy loop never makes (without it the GPU hung, black screen) and lets Unity's
|
||||
Oculus input accept Lepton's device model (it only reported controllers on a device named "Oculus ...", so buttons
|
||||
were dead). haptic_fix stops the first controller vibration from freezing the Frame (OVRPort's loader read its
|
||||
duration in nanoseconds as seconds and the game ran out of memory). With pose_time_fix the hands follow the controllers
|
||||
- OVRPlugin asked for hand poses at Android's monotonic "now", which on SteamOS 0.4.5 is 2.56 s behind the runtime's
|
||||
XrTime, so the hands lagged far behind.
|
||||
tested_version: 1.2.4
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
frame:
|
||||
- frame.unity_no_msaa
|
||||
- frame.unity_oculus_check
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_gl_shim
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
pose_time_fix: 1
|
||||
verified:
|
||||
date: '2026-10-08'
|
||||
issue: 48
|
||||
updated: '2026-10-08'
|
||||
min_app: 0.12.1
|
||||
source_hint: Battle Sister
|
||||
@@ -0,0 +1,19 @@
|
||||
package: com.SchellGames.DieScreaming
|
||||
title: SilentSlayer
|
||||
status: works
|
||||
tested_version: 1.1.2+323863s
|
||||
engine: Unreal
|
||||
xr: OpenXR
|
||||
alt_overport:
|
||||
- patch_remove_unreal_force_quit
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-11'
|
||||
known_good_sha256: 5cb085909f9a54488543102242f19c5b2f176a1439659a29a9cca04362601f68
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 176
|
||||
source_hint: Silent Slayer - Vault of the Vampire
|
||||
@@ -0,0 +1,22 @@
|
||||
package: com.ScytheDevTeam.ContainmentProtocol
|
||||
title: Deep Cuts
|
||||
status: works
|
||||
tested_version: 1.1.3271
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.oculusos
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: d8d4bfb0954059c5ed1bcaa0edfb8b18f154ff710049f4808c42cfd8d88ee5a2
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 144
|
||||
source_hint: com.ScytheDevTeam.ContainmentProtocol
|
||||
@@ -1,15 +1,25 @@
|
||||
package: com.StressLevelZero.BONELAB
|
||||
title: BONELAB
|
||||
status: unsupported
|
||||
notes: Crashes while starting its graphics (Vulkan); no fix yet.
|
||||
details: SIGSEGV in vkCreateInstance, called from the game's own Vulkan hooks (libSLZQuestNative.so) into code that no
|
||||
longer exists, right after OVRPlugin's pre-initialization creates and destroys an OpenXR instance. Keeping the
|
||||
runtime (vrclient.so) and Steam's Fossilize layer loaded didn't help, so the stale pointer is to memory freed with
|
||||
that first instance.
|
||||
status: works
|
||||
notes: Builds 1.2068 and 1.2974 play (owner's headset tests). One recipe serves both builds - 1.2068 needs frame.unity_user_presence (head tracking and controls); 1.2974 also needs
|
||||
frame.slz_vulkan_hooks (72 fps after about 35 s of shader prewarming), which changes nothing on 1.2068.
|
||||
details: Both builds - the game runs (72 fps), but its player body stayed frozen - no head tracking, the controllers stuck to the
|
||||
model, no buttons - although OpenXR input and OVRPlugin's poses were fine. A few seconds after start (when Unity
|
||||
switches XR loaders) OVRPlugin reports the worn headset as not worn (ovrp_GetUserPresent2 -> 0), and BONELAB's
|
||||
Marrow rig only follows the player while Unity's HMD device reports UserPresence (OpenControllerRig checks
|
||||
XRHMD.IsUserPresent every frame). frame.unity_user_presence reports the headset as worn. Build 1.2974 only - it adds Stress
|
||||
Level Zero's graphics plugin (libSLZQuestNative.so), which hooks Unity's Vulkan start-up (vkCreateInstance/
|
||||
vkCreateDevice) and vkCreateSampler; on the Frame its vkCreateInstance wrapper calls an invalid pointer right after
|
||||
OVRPlugin's pre-init instance is destroyed (a jump to 0, into an unloaded library, or a hang). frame.slz_vulkan_hooks
|
||||
switches its two registrations off, so Unity starts Vulkan itself; the plugin's pipeline cache isn't used.
|
||||
tested_version: 1.2974.57485
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_user_presence
|
||||
- frame.slz_vulkan_hooks
|
||||
verified:
|
||||
date: '2026-10-04'
|
||||
updated: '2026-10-04'
|
||||
date: '2026-10-08'
|
||||
updated: '2026-10-08'
|
||||
source_hint: BONELAB
|
||||
min_app: 0.12.1
|
||||
@@ -0,0 +1,22 @@
|
||||
package: com.ToastVR.Matilda
|
||||
title: Max Mustard
|
||||
status: works
|
||||
tested_version: 1.1.0
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-08'
|
||||
known_good_sha256: 9a67459fa36c3b9ac0d035e6c43b1809b39772699333e8cfd7d2a4d7f17926a7
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 110
|
||||
updated: '2026-10-09'
|
||||
source_hint: com.ToastVR.Matilda
|
||||
@@ -0,0 +1,22 @@
|
||||
package: com.ToastVR.RichiesPlankExperience
|
||||
title: Richie's Plank Experience
|
||||
status: works
|
||||
tested_version: 2.3.585
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.oculusos
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-07'
|
||||
known_good_sha256: 7eb1366c2398e554624a5d66aefb0c731c64b1b6944bc1726d9187f123c37fb7
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20260922.6101926'
|
||||
agent: 59
|
||||
issue: 95
|
||||
source_hint: apks
|
||||
@@ -0,0 +1,22 @@
|
||||
package: com.UNIVRS.Freedom
|
||||
title: Freedom
|
||||
status: issues
|
||||
notes: Volume can't be changed in the game.
|
||||
tested_version: 1.0.2950
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: 24662272c4cbe5dc7c4f3c803bdb9dead9661ac3d59bf80cca2e260256acc4bc
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 132
|
||||
source_hint: AOT VR
|
||||
@@ -0,0 +1,23 @@
|
||||
package: com.VR_HOT.VR_HOT_Quest
|
||||
title: VR HOT Quest
|
||||
status: works
|
||||
tested_version: 0.8.8.1
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.oculusos
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
controller_models: 1
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-08'
|
||||
known_good_sha256: c321606c536eba1673572a9f44fd9e20d2cb5bd9609d536189e6ad7057b9cd67
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20260922.6101926'
|
||||
agent: 59
|
||||
issue: 98
|
||||
source_hint: VR-HOT-Quest-0.8.8.1
|
||||
@@ -0,0 +1,25 @@
|
||||
package: com.WhiteDoorGames.Cosmodread
|
||||
title: Cosmodread
|
||||
status: works
|
||||
notes: First run wouldn't allow you to interact with the "New Game" button. Restarting it fixed it.
|
||||
tested_version: 1.0.0.28
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
frame:
|
||||
- frame.unity_oculus_check
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_gl_shim
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-11'
|
||||
known_good_sha256: 44911a4607d0de2770705eefb6339a4c7353cb5dfb49f389d6731edbb9cbea92
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 174
|
||||
source_hint: Cosmodread
|
||||
@@ -0,0 +1,22 @@
|
||||
package: com.XRGames.ZombielandVR
|
||||
title: ZombielandVR
|
||||
status: works
|
||||
tested_version: 1.7.0
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.vrapi_stub
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: 750067107cd739c80b65516dd1c18219283b2a29b39430bb21ed85d48b9b94ac
|
||||
app: 0.12.1.dev252
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 71
|
||||
issue: 141
|
||||
source_hint: ZombielandVR
|
||||
@@ -1,16 +1,26 @@
|
||||
package: com.YourCompany.RoboRecall
|
||||
title: Robo Recall
|
||||
tested_version: '1.0'
|
||||
source_hint: 'Robo Recall- Unplugged'
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
status: works
|
||||
notes: Needed the LAUNCHER category fix.
|
||||
details: Source APK is a community 'patch+savefix+90Hz' build.
|
||||
details: Source APK is a community 'patch+savefix+90Hz' build. Shares Vader Immortal's Unreal 4 Oculus input (GitHub
|
||||
49) - thumbs follow the touches (frame.unreal_thumb_touch) and controller poses are located at the right time
|
||||
(pose_time_fix); added 2026-10-09 from Vader's headset results, not yet checked in this game.
|
||||
sync_guard added 2026-10-11 for a vrclient xrSyncActions crash on the loading splash after the intro (GitHub
|
||||
#188; the same runtime race as Myst).
|
||||
tested_version: '1.0'
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
alt_overport:
|
||||
- patch_remove_unreal_force_quit
|
||||
frame:
|
||||
- frame.unreal_thumb_touch
|
||||
adapter:
|
||||
pose_time_fix: 1
|
||||
sync_guard: 1
|
||||
verified:
|
||||
date: '2026-09-28'
|
||||
overport_cli: 1.2.3
|
||||
overport_runtime: 3.4.3-23204ea
|
||||
known_good_sha256: 4ce6e3563da131e288645f4b62a634ac98043e55bc42b7c0ad5f461207d5f53c
|
||||
updated: '2026-10-11'
|
||||
source_hint: Robo Recall- Unplugged
|
||||
@@ -0,0 +1,25 @@
|
||||
package: com.YourCompany.SWP_VR
|
||||
title: Star Wars Pinball VR
|
||||
tested_version: '1.4'
|
||||
source_hint: Star Wars Pinball VR
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
status: works
|
||||
notes: Plays (owner's headset test); one short audio hiccup seen.
|
||||
details: 'Unreal 4.25 (GLES) built against OVRPlugin 1.44. Four Frame problems in a row: Zink crashed on Unreal''s
|
||||
multisampled render-to-texture (fault addr 0x10000 on the RHIThread): frame.unreal_gl_shim, which keeps Unreal''s
|
||||
multiview and draws those passes single-sampled; OVRPort''s OVRPlugin lacks ovrp_GetPTWNear, so Unreal''s Oculus
|
||||
module never started VR (then a null pointer in GetMultiViewSceneColor): frame.unreal_ovrp_entrypoints; the engine''s
|
||||
OpenSSL assembly has unpaired return-address checks the Frame''s CPU enforces (SIGILL on the HttpManager thread):
|
||||
frame.pac_hints. Needs its OBB (main.10765).'
|
||||
frame:
|
||||
- frame.unreal_gl_shim
|
||||
- frame.unreal_ovrp_entrypoints
|
||||
- frame.pac_hints
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-08'
|
||||
issue: 83
|
||||
updated: '2026-10-08'
|
||||
min_app: 0.12.1
|
||||
@@ -0,0 +1,23 @@
|
||||
package: com.asg.clockworkdev
|
||||
title: Clockwork
|
||||
status: works
|
||||
tested_version: '1.29'
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_gl_shim
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-09'
|
||||
known_good_sha256: 99a14eed320ba267153963c2d87d4e11e943b9920d48dfd5352c681de28ba95e
|
||||
app: 0.12.1.dev203
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 67
|
||||
issue: 116
|
||||
source_hint: com.asg.clockworkdev
|
||||
@@ -0,0 +1,22 @@
|
||||
package: com.beatgames.beatsaber.lj369vr
|
||||
title: Beat Saber
|
||||
status: works
|
||||
notes: Purchased DLC isn't detected (ownership comes from Meta's servers).
|
||||
tested_version: 1.40.8_7379
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_gl_shim
|
||||
device:
|
||||
- device.text_input_window
|
||||
verified:
|
||||
date: '2026-10-06'
|
||||
known_good_sha256: 6366a5411328b21de9e2da034af78290882b3c27b7a8f0c61688196e91320055
|
||||
app: 0.11.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261005.6135832'
|
||||
agent: 56
|
||||
issue: 68
|
||||
source_hint: Beat Saber
|
||||
@@ -1,6 +1,7 @@
|
||||
package: com.beatgames.beatsaber
|
||||
title: Beat Saber
|
||||
status: works
|
||||
notes: Purchased DLC isn't detected (ownership comes from Meta's servers).
|
||||
tested_version: 1.40.8_7379
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
package: com.camouflaj.manta
|
||||
title: 'Batman: Arkham Shadow'
|
||||
tested_version: 1.4.1-350961
|
||||
source_hint: 'Batman- Arkham Shadow'
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
status: works
|
||||
notes: Large data set (~27 GiB) incl. all language packs.
|
||||
details: Uses Meta XR Audio (Wwise), handled by patch_meta_xr_audio. Includes all language packs (~28 GB).
|
||||
details: 'Uses Meta XR Audio (Wwise), handled by patch_meta_xr_audio. Cutscenes are 8K HEVC panoramas: frame.hw_video_decode
|
||||
decodes them on the Frame''s hardware, adapter.surface_native shows them in stereo (PRs #96, #128). Includes all
|
||||
language packs (~28 GB). sync_guard (to test, GitHub #102) - a reporter''s game quit to Steam''s waiting screen
|
||||
after the Frame runtime crashed in xrSyncActions (CVRInputLatest::UpdateActionState), the input race Myst had.'
|
||||
tested_version: 1.4.1-350961
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
overport_extra:
|
||||
- patch_disable_space_warp
|
||||
frame:
|
||||
- frame.hw_video_decode
|
||||
adapter:
|
||||
sync_guard: 1
|
||||
surface_native: 1
|
||||
verified:
|
||||
date: '2026-09-28'
|
||||
overport_cli: 1.2.3
|
||||
overport_runtime: 3.4.3-23204ea
|
||||
known_good_sha256: 796d1cab08f614d72c50d9702974c719c0908f5de91183cfe02d1fd5eb09551a
|
||||
updated: '2026-10-10'
|
||||
min_app: 0.12.1
|
||||
source_hint: Batman- Arkham Shadow
|
||||
@@ -0,0 +1,17 @@
|
||||
package: com.camouflaj.salmon
|
||||
title: Marvel's Iron Man VR
|
||||
status: works
|
||||
tested_version: 0.1.0-250068
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
verified:
|
||||
date: '2026-10-05'
|
||||
known_good_sha256: 41e0ab90f622afd50accb0b6e2553520d30a6cc82e5ab4cbb2341b48f7316a71
|
||||
app: 0.9.1
|
||||
overport_cli: 1.2.5
|
||||
issue: 47
|
||||
source_hint: Marvel's Iron Man VR
|
||||
@@ -0,0 +1,21 @@
|
||||
package: com.characterBank.ruinsmagus
|
||||
title: RUINSMAGUS
|
||||
status: works
|
||||
tested_version: 1.2.3
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-07'
|
||||
known_good_sha256: 230e5625fbdfebb47c4d96aeb87f45270e4e6c79a33c08315c90626ab7465fe5
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261006.6173745'
|
||||
agent: 59
|
||||
issue: 89
|
||||
source_hint: RUINSMAGUS
|
||||
@@ -0,0 +1,22 @@
|
||||
package: com.cloudheadgames.pistolwhip
|
||||
title: Pistol Whip
|
||||
status: works
|
||||
notes: 120 HZ
|
||||
tested_version: 1.6.0.2
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-08'
|
||||
known_good_sha256: ebd97af54c59d59d473c98492c1bb26ce6059b7e59f27a08acfc5ada23d0b774
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261006.6173745'
|
||||
agent: 59
|
||||
issue: 100
|
||||
source_hint: Pistol Whip
|
||||
@@ -0,0 +1,25 @@
|
||||
package: com.coatsink.alone
|
||||
title: Jurassic World Aftermath Collection
|
||||
status: works
|
||||
notes: Plays (owner's headset test, 2026-10-06).
|
||||
tested_version: v3.16
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
frame:
|
||||
- frame.unity_no_msaa
|
||||
- frame.unity_oculus_check
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_gl_shim
|
||||
- frame.vrapi_stub
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-06'
|
||||
known_good_sha256: 0566b39d96258fa15eb1b03f27a7ad4219eb5442b0355cd803ed1cf13313f1b3
|
||||
app: 0.12.0
|
||||
updated: '2026-10-06'
|
||||
min_app: 0.12.0
|
||||
source_hint: Jurassic World Aftermath Collection
|
||||
@@ -1,17 +1,18 @@
|
||||
package: com.crowscrowscrows.AccountingPlus
|
||||
title: Accounting+
|
||||
status: issues
|
||||
notes: Starts in VR, but doesn't get past its "press any button" screen.
|
||||
status: works
|
||||
notes: Plays normally, including the "press any button" screen after choosing to sit or stand.
|
||||
details: Unity 2017.4 built-in Oculus support. frame.unity_oculus_check gets VR started (Meta's system-app check and
|
||||
the frame wait its legacy loop never makes, libfp_ovrp.so), ~72 fps. Controller input reaches the game's OVRInput
|
||||
cleanly (both Touch controllers connected, individual button bits, input focus 1 while focused) but the screen
|
||||
doesn't continue; Cpp2IL can't read this Unity version to see what it waits for.
|
||||
the frame wait its legacy loop never makes, libfp_ovrp.so), ~72 fps. The motion warning after the stance selection
|
||||
waits for a mouse click (Input.GetMouseButtonDown), which a Quest delivers for the controller press and Lepton never
|
||||
does; the same library now counts a newly pressed trigger or A/B/X/Y as that click.
|
||||
tested_version: 1.24.35
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
frame:
|
||||
- frame.unity_oculus_check
|
||||
verified:
|
||||
date: '2026-10-04'
|
||||
updated: '2026-10-04'
|
||||
date: '2026-10-06'
|
||||
updated: '2026-10-06'
|
||||
min_app: 0.11.0
|
||||
source_hint: Accounting+
|
||||
@@ -0,0 +1,22 @@
|
||||
package: com.curif.AgeOfJoy
|
||||
title: AgeOfJoy
|
||||
status: works
|
||||
tested_version: '1.0'
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.oculusos
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-08'
|
||||
known_good_sha256: 9780e71e417b86290147c67c83755305801d8957909734c717a519034edefdf9
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261006.6173745'
|
||||
agent: 59
|
||||
issue: 103
|
||||
source_hint: AgeOfJoy 05 rc30
|
||||
@@ -0,0 +1,24 @@
|
||||
package: com.drawandcode.TaskmasterVR
|
||||
title: Taskmaster
|
||||
status: works
|
||||
notes: Launched and played a few tasks. Seems to work pretty much flawlessly other than some occasionally finicky
|
||||
grip detection - very possible that this is just the game itself though
|
||||
tested_version: 1.0.17557.29562
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.oculusos
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-11'
|
||||
known_good_sha256: e3b3c49d77284afb76e9f960828cbfaa4250d66c11b7fd9e4053d489aca662ae
|
||||
app: 1.0.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 78
|
||||
issue: 183
|
||||
source_hint: Taskmaster VR
|
||||
@@ -0,0 +1,23 @@
|
||||
package: com.drbeef.doom3quest
|
||||
title: Doom3Quest
|
||||
status: works
|
||||
notes: HUD and PDA show with frame.gl_multiview_fbo (Xandrix1987's headset test, GitHub 77, no frame-rate drop with
|
||||
the PDA open). Walking and running can switch when the frame rate drops, which seems to be the game's own behaviour.
|
||||
details: Doom3Quest compiles every vertex shader for two views (OVR_multiview) and draws its HUD/PDA into single-view
|
||||
framebuffers. Mesa (the Frame's GL) rejects that combination as the spec requires and drops the draw, so they
|
||||
stayed black; frame.gl_multiview_fbo draws those passes with a single-view copy of the shader. Xandrix1987's framebuffer.cpp
|
||||
fix went upstream as a PR to Team-Beef-Studios/Doom3Quest.
|
||||
tested_version: 1.4.8
|
||||
engine: Other
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.gl_multiview_fbo
|
||||
verified:
|
||||
date: '2026-10-06'
|
||||
known_good_sha256: 3c733f5cd08b6fa5d9e2775b1c1aee36b5943518b5d8451ba87e3c59a702afdf
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.3
|
||||
issue: 77
|
||||
updated: '2026-10-09'
|
||||
min_app: 0.12.1
|
||||
source_hint: doom3quest148
|
||||
@@ -0,0 +1,17 @@
|
||||
package: com.drbeef.lambda1vr
|
||||
title: Lambda1VR
|
||||
status: works
|
||||
notes: 'Needs the game data: upload the release''s xash folder to the game''s storage (Files tab) and set --msaa
|
||||
1 in xash/commandline.txt. Owner''s headset test, 2026-10-06.'
|
||||
tested_version: 1.7.3
|
||||
engine: Other
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.tbxr_vendor
|
||||
verified:
|
||||
date: '2026-10-06'
|
||||
known_good_sha256: 10bbccceb8d9a84ff3bcc9a21fd59db52784ff7742998bbe5b30d938dd7b97d2
|
||||
app: 0.12.0
|
||||
updated: '2026-10-06'
|
||||
min_app: 0.12.0
|
||||
source_hint: Lambda1VR
|
||||
@@ -0,0 +1,25 @@
|
||||
package: com.endspace.quest
|
||||
title: End Space
|
||||
status: issues
|
||||
notes: Controls stop responding after the first mission loads.
|
||||
tested_version: 1.0.6.1
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
frame:
|
||||
- frame.unity_oculus_check
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_gl_shim
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: c5eb892311a130686f238e6f61cbdb619324f5c21f8c0641c0fcd49d77369eaf
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 148
|
||||
source_hint: com.endspace.quest
|
||||
@@ -0,0 +1,18 @@
|
||||
package: com.enhanceexperience.tetriseffect
|
||||
title: TetrisEffect
|
||||
status: works
|
||||
notes: Opened the game, messed around with the menu, and played the first level. Works great.
|
||||
tested_version: 2.0.2
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
alt_overport:
|
||||
- patch_remove_unreal_force_quit
|
||||
verified:
|
||||
date: '2026-10-05'
|
||||
known_good_sha256: 9ea10027ad8b7aefba8c14f23575c9cbbcaf3b38d12b971ea9c06635721b23c1
|
||||
app: 0.9.1
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20260922.6101926'
|
||||
agent: 48
|
||||
issue: 45
|
||||
source_hint: tetriseffect
|
||||
@@ -0,0 +1,22 @@
|
||||
package: com.forcefieldxr.explorevr
|
||||
title: ExploreVR
|
||||
status: issues
|
||||
notes: The 3D world warps and shifts with head movement (wrong perspective).
|
||||
tested_version: 1.2.1
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
alt_overport:
|
||||
- patch_remove_unreal_force_quit
|
||||
frame:
|
||||
- frame.nodebug
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: 04576c46a91f4bb851a344c75e022b17a5d2837d054e7323a184b81436cf8019
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 149
|
||||
source_hint: com.forcefieldxr.explorevr
|
||||
@@ -1,18 +1,24 @@
|
||||
package: com.forcefieldxr.timestall
|
||||
title: Time Stall
|
||||
tested_version: '1.0'
|
||||
source_hint: Time Stall
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
status: issues
|
||||
notes: Both eyes distort during movement (unresolved).
|
||||
details: Legacy VrApi via OVRPlugin.
|
||||
details: Legacy VrApi via OVRPlugin. Shares Vader Immortal's Unreal 4 Oculus input (GitHub 49) - thumbs follow the
|
||||
touches (frame.unreal_thumb_touch) and controller poses are located at the right time (pose_time_fix); added 2026-10-09
|
||||
from Vader's headset results, not yet checked in this game.
|
||||
tested_version: '1.0'
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
alt_overport:
|
||||
- patch_remove_unreal_force_quit
|
||||
frame:
|
||||
- frame.unreal_thumb_touch
|
||||
- frame.nodebug
|
||||
adapter:
|
||||
pose_time_fix: 1
|
||||
verified:
|
||||
date: '2026-09-28'
|
||||
overport_cli: 1.2.3
|
||||
overport_runtime: 3.4.3-23204ea
|
||||
known_good_sha256: 50c4d05b663b2f1851f5a7d650a89fd34587d65fafba13cc58671c96f7016db6
|
||||
updated: '2026-10-09'
|
||||
source_hint: Time Stall
|
||||
@@ -0,0 +1,25 @@
|
||||
package: com.fyian.TheThrillOfTheFight
|
||||
title: The Thrill Of The Fight
|
||||
status: issues
|
||||
notes: Right eye doesn't draw the opponent or the referee.
|
||||
tested_version: '251113.0'
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_gl_shim
|
||||
- frame.unity_no_overlay_copy
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: af86dfbd6f7d3ab504d1f4f92f4dbc4c0f0cb36e30443b80fa0b91322f481394
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 164
|
||||
source_hint: ThrillOfTheFight_Release
|
||||
@@ -0,0 +1,9 @@
|
||||
package: com.golfscope.proputt
|
||||
title: GOLF+
|
||||
status: unsupported
|
||||
notes: Black screen. The game's servers reject the login on this platform (Authentication not supported).
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
issue: 147
|
||||
@@ -0,0 +1,24 @@
|
||||
package: com.harmonixmusic.kata
|
||||
title: Audica
|
||||
status: works
|
||||
tested_version: 1.0.3.4
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
frame:
|
||||
- frame.unity_no_msaa
|
||||
- frame.unity_oculus_check
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_gl_shim
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-09'
|
||||
known_good_sha256: bc2c00fe049fd97643a296e622e3a90e08f0bd796ee454b0278525e9d72f8139
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 126
|
||||
@@ -0,0 +1,27 @@
|
||||
package: com.ilmxlab.tales
|
||||
title: 'Star Wars: Tales from the Galaxy''s Edge'
|
||||
status: unknown
|
||||
notes: Not working yet - after the intro logos and a loading animation the picture goes black (GitHub issue 61).
|
||||
Gets the controller fixes found for Vader Immortal (same studio) to test.
|
||||
details: Unreal Engine 4 (GLES) by ILMxLAB, like Vader Immortal, but a different engine build - it has none of Vader's
|
||||
Quest-only branches (no GetQuestShaderPrecompilePercent or RPOC key map; it precompiles shaders through Unreal's
|
||||
own pipeline cache), so frame.unreal_quest_precompile/_keymap don't apply. Its seasons and Wwise banks are Meta
|
||||
platform asset files (frame.asset_files). Vader's OVRPlugin findings apply to it as well - poses asked for at
|
||||
OVRPlugin's own clock (pose_time_fix) and thumbs from near-touch, which the Frame never reports (frame.unreal_thumb_touch).
|
||||
Ruled out for the black picture so far (see the diagnostics in issue 61) - asset-file paks, Valve's foveation,
|
||||
GL errors; the game's own eye image reads back black.
|
||||
tested_version: 1.1.9+734762.cl.439699
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
alt_overport:
|
||||
- patch_remove_unreal_force_quit
|
||||
frame:
|
||||
- frame.asset_files
|
||||
- frame.unreal_thumb_touch
|
||||
adapter:
|
||||
pose_time_fix: 1
|
||||
verified:
|
||||
issue: 61
|
||||
updated: '2026-10-09'
|
||||
min_app: 0.12.1
|
||||
source_hint: Star Wars- Tales from the Galaxys Edge
|
||||
@@ -0,0 +1,20 @@
|
||||
package: com.innerspacevr.afishermanstale
|
||||
title: Fisherman
|
||||
status: issues
|
||||
notes: Loading screens have corruption, but everything works otherwise.
|
||||
tested_version: '1.087'
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
alt_overport:
|
||||
- patch_remove_unreal_force_quit
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-11'
|
||||
known_good_sha256: c645ca6321845bd460b4ff1b8ac871e941adb26d280cccba0285e79e4a96a231
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 175
|
||||
source_hint: A Fisherman's Tale
|
||||
@@ -0,0 +1,21 @@
|
||||
package: com.kluge.SynthRiders
|
||||
title: SynthRiders
|
||||
status: works
|
||||
tested_version: 3.6.7a1
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.oculusos
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
controller_models: 1
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: fb982ad7617629df50190c98d1de906ac9b41903d782c0ada0087b32fc95a5f1
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
issue: 158
|
||||
source_hint: com.kluge.SynthRiders
|
||||
@@ -0,0 +1,24 @@
|
||||
package: com.markschramm.gravitylab
|
||||
title: Gravity Lab
|
||||
status: works
|
||||
notes: Works great, along with passthrough mode
|
||||
tested_version: '1.221'
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_gl_shim
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-09'
|
||||
known_good_sha256: 82c95ee7487a130b6e37b967dd97c8362bf4d7e873fcff823f2c7877a62ff99f
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 113
|
||||
source_hint: Gravity Lab
|
||||
@@ -0,0 +1,21 @@
|
||||
package: com.miketeevee.shoresofloci
|
||||
title: Shores of Loci
|
||||
status: works
|
||||
tested_version: '1.2'
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: c223a99cc1f1597a477a4677c1390cf744d517bd1e8fa7cf82e20b1baefaba26
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 154
|
||||
source_hint: com.miketeevee.shoresofloci
|
||||
@@ -1,18 +1,25 @@
|
||||
package: com.nDreams.PhantomQuest
|
||||
title: 'Phantom: Covert Ops'
|
||||
tested_version: '1.2'
|
||||
source_hint: 'Phantom- Covert Ops'
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
status: issues
|
||||
notes: DLC/store button crashes (no Meta store). Installs the no-ForceQuit build.
|
||||
details: The regular build quits itself on the Frame (System.exit after a failed platform check), so the no-ForceQuit
|
||||
build is installed.
|
||||
build is installed. Shares Vader Immortal's Unreal 4 Oculus input (GitHub 49) - thumbs follow the touches (frame.unreal_thumb_touch)
|
||||
and controller poses are located at the right time (pose_time_fix); added 2026-10-09 from Vader's headset results,
|
||||
not yet checked in this game.
|
||||
tested_version: '1.2'
|
||||
engine: Unreal
|
||||
xr: VrApi
|
||||
alt_overport:
|
||||
- patch_remove_unreal_force_quit
|
||||
use_alt: true
|
||||
frame:
|
||||
- frame.unreal_thumb_touch
|
||||
adapter:
|
||||
pose_time_fix: 1
|
||||
verified:
|
||||
date: '2026-09-28'
|
||||
overport_cli: 1.2.3
|
||||
overport_runtime: 3.4.3-23204ea
|
||||
known_good_sha256: bb02b20a23fbe5e7a580ecde15a3cd015a4183421a51aab4a4dbcddbe7a09d1d
|
||||
updated: '2026-10-09'
|
||||
source_hint: Phantom- Covert Ops
|
||||
@@ -0,0 +1,21 @@
|
||||
package: com.newfoldergames.fruitsalon
|
||||
title: Fruit Salon
|
||||
status: works
|
||||
tested_version: 1.0.2
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: 1c2e6e44e046e1683670a6b0f97e7766fd5edd0c888443910ce3c8e2291a9865
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261006.6173745'
|
||||
agent: 59
|
||||
issue: 165
|
||||
source_hint: Fruit Salon
|
||||
@@ -0,0 +1,19 @@
|
||||
package: com.odders.shapeit
|
||||
title: BodyCombat
|
||||
status: works
|
||||
tested_version: 3.0.0
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
verified:
|
||||
date: '2026-10-05'
|
||||
known_good_sha256: 1aa5b2a46b25c86db65e3355a575af14b5d4d89b967c812274f181aab2e62b33
|
||||
app: 0.9.1
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20260930.6234839'
|
||||
agent: 48
|
||||
issue: 56
|
||||
source_hint: LES MILLS BODYCOMBAT
|
||||
@@ -0,0 +1,23 @@
|
||||
package: com.onehamsa.RNXQ
|
||||
title: 'Racket: Nx'
|
||||
status: works
|
||||
tested_version: 2.8.31
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_gl_shim
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: f78de8051c5808e4244d9fa757050d6c131350112315ad6e01e2b1457f2e2053
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 151
|
||||
source_hint: '9030540110323274'
|
||||
@@ -1,13 +1,12 @@
|
||||
package: com.owlchemylabs.jobsimulator
|
||||
title: Job Simulator
|
||||
status: works
|
||||
notes: 'Without Lepton''s Android window: with it, the hands froze after a few seconds (GitHub #193).'
|
||||
tested_version: 1.4.0.6515
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
frame_remove:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
verified:
|
||||
date: '2026-10-03'
|
||||
known_good_sha256: 64638d7f93bb93cc61f422a43a08bb0d35b9bb4b463e90a42da7002147f03d1b
|
||||
@@ -16,4 +15,5 @@ verified:
|
||||
frame_build: '20260930.6234839'
|
||||
agent: 33
|
||||
issue: 11
|
||||
updated: '2026-10-11'
|
||||
source_hint: Job Simulator
|
||||
@@ -0,0 +1,23 @@
|
||||
package: com.owlchemylabs.vacationsimulator
|
||||
title: Vacation Simulator
|
||||
status: works
|
||||
notes: 'Without Lepton''s Android window: with it (or after clicking it), the hands stayed on the floor (GitHub
|
||||
#163).'
|
||||
tested_version: 1.3.0.40142
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame_remove:
|
||||
- frame.unity_text_input
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
refresh_rate: 90.0
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: b02e0a24292a8305ca752fef1884356f0191b53793d356b999cd50bba3fe7a3c
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 163
|
||||
updated: '2026-10-11'
|
||||
source_hint: com.owlchemylabs.vacationsimulator
|
||||
@@ -1,11 +1,12 @@
|
||||
package: com.playful.LuckysTale
|
||||
title: Lucky's Tale
|
||||
status: issues
|
||||
notes: Runs out of memory within a few minutes and freezes the Frame until the game is killed.
|
||||
details: Unity's asset loading thread (Loading.PreloadManager) grows the game's native heap by about 1 GB/s
|
||||
shortly after start (10+ GB plus all swap, then the kernel kills it; perf page-fault profile, 2026-10-04). The text
|
||||
window and the MSAA patch aren't the cause (same with the window off); the OBB is byte-identical to the original.
|
||||
The earlier "headset restarted in a menu" reports (GitHub issue 9) were most likely this. No fix yet.
|
||||
status: works
|
||||
notes: Plays normally; touching a save slot no longer freezes the Frame (haptic_fix).
|
||||
details: Touching a save slot vibrates the controller through XR_FB_haptic_amplitude_envelope; OVRPort's OpenXR
|
||||
loader converts that envelope with the duration in nanoseconds read as seconds and allocates an enormous buffer, so
|
||||
the Frame ran out of memory and froze (found by the GitHub issue 9 reporter). FrameBridge's extension shim turns the
|
||||
envelope into a plain vibration before OVRPort's loader sees it (haptic_fix). Brief pauses came from the headset's
|
||||
wear sensor flickering, now hidden by focus_hold (on by default).
|
||||
tested_version: 1.0.55
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
@@ -14,8 +15,11 @@ frame:
|
||||
- frame.unity_runtime_msaa_off
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-03'
|
||||
date: '2026-10-05'
|
||||
issue: 9
|
||||
updated: '2026-10-04'
|
||||
updated: '2026-10-05'
|
||||
min_app: 0.11.0
|
||||
source_hint: Luckys Tale
|
||||
@@ -1,13 +1,18 @@
|
||||
package: com.qcxr.qcxr
|
||||
title: QuestCraft
|
||||
status: issues
|
||||
notes: The launcher runs, but Minecraft crashes while starting its graphics (OpenGL error 1282).
|
||||
status: works
|
||||
notes: Minecraft runs in VR (Vivecraft, about 72 fps; drops while worlds load). Needs FramePort 0.10.0 or newer for
|
||||
the picture (older versions show black). On first start QuestCraft downloads its Java runtime, which didn't unpack on
|
||||
the Frame once (see details).
|
||||
details: QuestCraft downloads its Java runtime (JRE.zip from QuestCraft's GitHub releases) into files/runtimes/JRE
|
||||
on first start; on the Frame that step left nothing behind ("libjvm.so not found"). With the runtime unpacked there
|
||||
by hand, Java, Fabric and the mods load and Minecraft 1.21.5 starts initializing, then its first texture fails with
|
||||
OpenGL error 1282 in QuestCraft's GL-to-GLES layer on Zink. Leads (2026-10-04) - Android's libEGL ran out of
|
||||
eglGetProcAddress slots for desktop GL functions; LWJGL and JNA natives in the APK don't match the newer files
|
||||
QuestCraft downloads.
|
||||
on first start; on the Frame that step once left nothing behind ("libjvm.so not found"; unpacked by hand). Two
|
||||
Frame fixes, both owner-verified 2026-10-05. (1) Minecraft 1.21.5 checks GL errors strictly and aborted with
|
||||
"OpenGL error 1282", an error the Frame's Mesa (Zink) reports where Quest's driver doesn't; QuestCraft's GL-to-GLES
|
||||
layer (LTW) ignores GL errors when LIBGL_NOERROR is set, which Lepton passes from LEPTON_ENV_LIBGL_NOERROR. (2) LTW
|
||||
passes desktop GL_DEPTH_COMPONENT32 through, which Mesa rejects, so every framebuffer was incomplete and the picture
|
||||
black; frame.ltw_depth (automatic for APKs with libltw.so) loads GLES through a shim that asks for
|
||||
GL_DEPTH_COMPONENT32F. Left over, harmless so far - Mesa still reports about 4 GL_INVALID_OPERATION per frame
|
||||
("in glthread", so the call is unknown; mesa_glthread=false would name it).
|
||||
tested_version: 6.0.0-RC4
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
@@ -15,7 +20,9 @@ frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
lepton_env:
|
||||
LEPTON_ENV_LIBGL_NOERROR: '1'
|
||||
verified:
|
||||
date: '2026-10-04'
|
||||
updated: '2026-10-04'
|
||||
date: '2026-10-05'
|
||||
updated: '2026-10-05'
|
||||
source_hint: QuestCraft
|
||||
@@ -0,0 +1,22 @@
|
||||
package: com.quaternionsoftware.rcpilottrainer
|
||||
title: RC Pilot Trainer
|
||||
status: works
|
||||
tested_version: 0.7.0.2
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
- frame.oculusos
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: 1b98f0dea74f058a838ac17299a0e1c67362cb1ecfbc97e4cb2aad3bcbdfb173
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 152
|
||||
source_hint: com.quaternionsoftware.rcpilottrainer
|
||||
@@ -0,0 +1,20 @@
|
||||
package: com.resolutiongames.abvriop.santacruz
|
||||
title: palazzo_santacruz
|
||||
status: works
|
||||
notes: Played a few levels. Passthrough also works.
|
||||
tested_version: 3.7.6.10866-release
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.unity_text_input
|
||||
device:
|
||||
- device.text_input_window
|
||||
verified:
|
||||
date: '2026-10-05'
|
||||
known_good_sha256: 394214a68297c1e5b019dc4f65b145e7bd0e98604205616364e0c151c1afd894
|
||||
app: 0.9.1
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20260922.6101926'
|
||||
agent: 48
|
||||
issue: 46
|
||||
source_hint: abvriop
|
||||
@@ -0,0 +1,20 @@
|
||||
package: com.rrrgames.CaveCrave
|
||||
title: Caves
|
||||
status: works
|
||||
notes: First cave loads up perfectly, didn't play to unlock other game modes
|
||||
tested_version: 1.1.4
|
||||
engine: Unity
|
||||
xr: OpenXR
|
||||
frame:
|
||||
- frame.oculusos
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-10'
|
||||
known_good_sha256: 9720a9af42590dfdb742f278352583e56f36efbf5d96c56c59ac4b6a615f276e
|
||||
app: 0.12.0
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 59
|
||||
issue: 142
|
||||
source_hint: com.rrrgames.CaveCrave
|
||||
@@ -0,0 +1,24 @@
|
||||
package: com.schellgames.ieytd3
|
||||
title: IEYTD3
|
||||
status: works
|
||||
tested_version: 1.0.1.211419 (ReleaseCandidate_1.0.0_QuestStore)
|
||||
engine: Unity
|
||||
xr: VrApi
|
||||
frame:
|
||||
- frame.unity_no_msaa
|
||||
- frame.unity_text_input
|
||||
- frame.unity_runtime_msaa_off
|
||||
- frame.unity_gl_shim
|
||||
device:
|
||||
- device.text_input_window
|
||||
adapter:
|
||||
haptic_fix: 1
|
||||
verified:
|
||||
date: '2026-10-11'
|
||||
known_good_sha256: 06f901ae22f7c07a79e11fc1af6b3941a51bbc99624336d6a0939f9593f3bfa0
|
||||
app: 0.12.1.dev263
|
||||
overport_cli: 1.2.5
|
||||
frame_build: '20261007.6125817'
|
||||
agent: 78
|
||||
issue: 181
|
||||
source_hint: 2026-10-08T23-57-41.310Z_211419
|
||||
Loaded 100 of 693 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user