13 KiB
Quest controller mode: native stream
Original implementation
native/adapter/quest_buttons.cparses the numeric adapter setting. Only 0, 1 and 2 are accepted; malformed, fractional and out-of-range values select 0 and logquest_buttons: invalid setting; using 0.xrCreateInstancereuses the existing next-layer extension enumeration. Modes 1 and 2 enableXR_VALVE_frame_controller_interactiononly when offered, without adding a second copy when the game requested it. An unavailable/failed enumeration disables this feature. Failed instance creation clears the enabled flag.suggest_frame()insession_fixes.ctranslates successful Touch suggestions using the tables corresponding toquest_buttons.json. It preserves action handles and click/touch components, supports both hands, poses, thumbstick vector/components and haptics, and counts unknown/dropped source bindings. Each input produces at most two Frame bindings. Mode 0 adds no runtime calls or feature logs.- Touch Pro/Plus fallback translates the filtered Touch-shaped copy after Touch succeeds. Proximity-emulation additions are deliberately excluded. A later Touch suggestion submits a fresh Frame set. Frame suggestion errors are logged without changing the game's result. A successful game-owned Frame suggestion prevents subsequent automatic Frame suggestions; if it arrives after an automatic suggestion, its runtime call replaces that set.
- With the setting and extension enabled,
xrGetCurrentInteractionProfiletranslates Valve profiles to Touch even withcontroller_fix=0. Existingprofile_remapthen still reports the game's Touch Pro/Plus profile when needed. Input diagnostics continue to show the runtime's original profile and additionally log every translated binding. - Both shipped adapter architectures were rebuilt, with updated
artifacts/SHA256SUMS. No agent, analysis, patch or setting revision was changed. Contract files and the VrApi bridge are unchanged.
Original offline validation
Commands run with UV_CACHE_DIR=/tmp/frameport-qb-native-uv to keep uv cache writes inside the writable sandbox;
--offline --no-sync uses the existing worktree environment without fetching dependencies.
FRAMEPORT_NATIVE_TESTS=1 uv run --offline --no-sync pytest -q tests/test_quest_buttons.py tests/test_profile_report.py
uv run --offline --no-sync python native/build.py --only adapter
uv run --offline --no-sync python scripts/patch_docs.py --check
uv run --offline --no-sync pytest -q
The new host harness includes quest_buttons.c, input_diag.c, and the complete session_fixes.c, plus verbatim
copies of the adapter's exported create-instance/current-profile functions. It compiles with host cc -Werror.
Python derives expected Frame bindings from the unchanged JSON contract, independently of the C mapping table.
The targeted run passed 73 tests, including the existing profile-report tests. Two consecutive adapter builds
produced identical SHA-256 hashes for both architectures; the manifest matches both files. Documentation checks pass.
The cached NDK/OpenXR inputs were already present. During the builds, the worktree's ignored native/.cache was
temporarily a local directory containing input symlinks, so shader intermediates were written only inside this
worktree. The original shared-cache symlink was restored afterward. No inputs were fetched or changed and no device
was contacted.
Additional native baseline issue: FRAMEPORT_NATIVE_TESTS=1 ... tests/test_input_diag.py has an existing failure in
test_logs_each_finding_once_and_nothing_when_off. Its harness requests input_diag_hook("xrCreateAction"), which
the baseline implementation no longer exposes: action diagnostics moved to session_fixes.c. Compiling the harness
against pristine 25e282d's input_diag.c reproduced the same line-85 failure. That unrelated behavior/test was left
unchanged.
Full-suite result: 1,993 passed, 116 skipped, 12 failed, 16 errors. All 28 failing/erroring cases stop at socket
creation with PermissionError: [Errno 1] Operation not permitted: local HTTP fixtures in deep links, link updates,
devkit, livestream, setup URL, and pairing UI tests. The sandbox disallows even loopback sockets. No feature
assertion failed. The required unfiltered command was run; a green unfiltered run still requires a host that permits
local sockets. The socket restriction was not relaxed.
An additional run deselecting exactly those 28 unsupported cases passed: 1,993 passed, 116 skipped, 28 deselected.
Integration questions and limits
- The contract has no missing mapping needed by this implementation. The right controller's Menu button has no
mapping in modes 1/2; only left Touch
menu/clickmaps to Frameview/click. Unknown paths, including menu touch and proximity, are intentionally dropped. - The game's own Frame bindings win only after its Frame suggestion succeeds. A rejected Frame suggestion does not suppress automatic translation. This avoids disabling working input because the game submitted an invalid set.
- Headless/host tests cannot establish SteamVR's actual preference for these suggested bindings, physical button layout, capacitive touch behavior, haptics, or whether the installed Frame runtime offers the extension. These require the maintainer's checks below. An empty translated set is submitted and its result logged/ignored like any other Frame suggestion; the runtime can reject it.
Maintainer device checks (not executed)
Use an isolated .home already containing a test library entry, its signing key/toolchain, and a paired test Frame.
Use a normal Quest OpenXR game and a VrApi game using the bridge. Repeat with a game that suggests only Touch
Pro/Plus if available. Replace the sample package name with each game's actual package. Stop other games first.
-
Rebuild/reinstall the test game so the new adapter is actually inside the APK. A setting update alone cannot upgrade an already installed adapter. Keep the original
controller_fixandinput_diagvalues for restoration.export FRAMEPORT_HOME="$PWD/.home" export PKG=com.example.game uv run frameport install "$PKG" --force -
Collect a headless startup log for each mode, with
controller_fix=0and diagnostics enabled. Each launch test stops its game before the next iteration. The script uses the remembered Frame in this isolated home.for MODE in 0 1 2; do export MODE uv run frameport settings "$PKG" quest_buttons="$MODE" input_diag=1 controller_fix=0 uv run python - <<'PY' import os from pathlib import Path from frameport.cli import _target from frameport.core.events import Reporter target = _target(None) try: result, log = target.launch_test(os.environ["PKG"], Reporter(), seconds=30) output = Path(os.environ["FRAMEPORT_HOME"]) / f"quest-buttons-mode-{os.environ['MODE']}.log" output.write_text(log) print(output) for line in log.splitlines(): if "quest_buttons" in line or "controller profile" in line: print(line) finally: target.frame.close() PY doneExpected mode 0: no
quest_buttons:orinput_diag: quest_buttonslines. Modes 1/2, if offered:quest_buttons: extension XR_VALVE_frame_controller_interaction enabled quest_buttons: frame_controller bindings (mode 1, K bindings, D dropped): 0 input_diag: quest_buttons /user/hand/left/input/x/click -> /user/hand/left/input/dpad_down/click input_diag: quest_buttons /user/hand/left/input/x/click -> /user/hand/left/input/dpad_right/click input_diag: quest_buttons /user/hand/left/input/menu/click -> /user/hand/left/input/view/clickK/Ddepend on the game. Mode 2 reportsmode 2and only the first X mapping above. Face-button touch paths keep/touch. Frame suggestions must return0; a negative result indicates runtime rejection to investigate. A Touch Pro/Plus-only game should also logcontroller profile ... -> oculus/touch_controllerand a Frame binding summary. If the runtime lacks the extension, expectquest_buttons: extension XR_VALVE_frame_controller_interaction not offered by the runtime, with no translated bindings; input should retain its previous behavior. -
For each mode, set it with the command below, then Play from the FramePort library or Steam while wearing the headset. Exit through Steam before changing modes; settings are read at process startup.
uv run frameport settings "$PKG" quest_buttons=1 input_diag=1 controller_fix=0Mirror: left down/right = Touch X; left up/left = Touch Y; right A/X = Touch A; right B/Y = Touch B. Quest pair: left down = X, left left = Y, right A = A, right B = B; the unused face buttons should not activate those actions. In both modes left View activates Touch Menu. Check thumbstick movement/click/touch, trigger/squeeze values, trigger touch, tracking/aim, and haptic feedback. Verify both hands remain connected with
controller_fix=0. Mode 0 should match the previous SteamVR mapping. Capacitive-touch verification needs a game that exposes finger animation or an input sample; absence of a suggested touch action is not a failure of this mapping. -
Restore settings after stopping the game. The example restores the usual defaults; substitute recorded original values if different. Reinstalling the prior known-good APK also removes the new adapter if an artifact rollback is needed; setting 0 alone disables the feature on the next launch.
uv run frameport settings "$PKG" quest_buttons=0 input_diag=0 controller_fix=1
For explicit current-profile verification in an OpenXR input sample, call xrGetCurrentInteractionProfile for
/user/hand/left and /user/hand/right, then xrPathToString on each non-null result after action-set attachment.
With modes 1/2 and controller_fix=0, expect /interaction_profiles/oculus/touch_controller, or the game's own
Touch Pro/Plus profile when its binding was remapped. Existing input_diag profile lines intentionally show the raw
runtime answer, so they cannot alone verify the profile returned to the game.
Round 2
The review fixes supersede the original proximity exclusion, broad Valve-profile rewrite, empty-set submission, and failed-instance state handling described above:
- Added bare
grip/aimto the JSON common mapping andFRAME_COMMON; no other contract fields changed. - Current-profile rewriting with
controller_fix=0requires the exact Frame profile and a successful generated binding set still in effect. A successful game-owned Frame suggestion ends that ownership; failed suggestions leave the last accepted set in effect. Existingcontroller_fix=1behavior is retained. - Frame bindings are generated at the point a Touch set succeeds, including proximity-emulation touch bindings. Direct Touch, Touch Pro/Plus remapping, and rejected-expansion fallback all use the effective accepted set.
- An all-dropped translation makes no Frame suggestion and logs
quest_buttons: frame_controller bindings (mode N, 0 bindings, D dropped): not suggested. An earlier accepted generated set can remain stale; it is intentionally retained because an empty replacement is invalid. - Extension decisions remain local until instance creation succeeds. Only successful creation publishes the active instance and resets binding ownership/Touch-remap state; a failed second attempt preserves the live first state.
- One mutex serializes each complete suggestion transaction, including fallback/remapping, Frame generation, and flags. Instance-state publication and current-profile reporting use the same mutex. Internal helpers call the next layer directly and never reacquire it.
Regression tests reproduced items 1–5 before the fixes (21 failed cases). Afterward, the requested host run passed
97 tests, including timeout-bounded nested proximity fallback and Touch Pro remapping. Diagram regeneration made no
changes; controller-diagram and patch-documentation checks pass. Both adapter architectures were rebuilt twice with
identical hashes matching SHA256SUMS. The full suite finished with 2,015 passed, 140 skipped, 12 failed and 16 errors;
all 28 failures/errors are the same sandbox-denied local socket creation described above.
Device follow-up: repeat the install/log/physical checks above, adding proximity_emul=2 to the mode-1/mode-2 settings
command for a game exposing finger proximity. Expect translated proximity actions on face-button/thumbstick touch
and trigger touch, and working finger animation. In an OpenXR sample with controller_fix=0, also query profiles
before any generated set, after a successful generated set, and after the app's own successful Frame suggestion:
expect Frame, Touch, Frame respectively. Another Valve profile must stay unchanged. Restore the recorded original
proximity_emul setting along with the other settings. No device operations were performed in this worktree.