Skip to content

Latest commit

 

History

History
248 lines (209 loc) · 13 KB

File metadata and controls

248 lines (209 loc) · 13 KB

XR Menu Button Conventions — Research

Date: 2026-05-30 · Author: Claude research session for Mike Wise Question: How should senckenberg.messelpit.viewer_xr reconcile its in-VR floating panel (MesselpitMenuTool) with Kit's stock right-B settings menu (XRMenuTool)? Is there a "right" way to surface app-specific menu items in Omniverse Kit XR, and what does the broader VR ecosystem do?

TL;DR

  • No public API exists today to add items into Kit's stock XR settings menu. The right-B menu (omni.kit.xr.ui.stage/xr_tools/xr_menu_tool.py) builds its panel from hard-coded internal modules. Subclassing or monkey-patching it is the only way in, and both are fragile.
  • Industry convention is firmly "B/Y opens the app menu, not the system menu". Unreal's VR Template binds B to its quick-menu; Half-Life Alyx uses hold-Y; Meta's own button-mapping tech note labels the dedicated ≡ Menu button as the recommended app-menu entry point. Quest 3 Touch Plus keeps the ≡ button on the left controller; Kit ignores it. The system overlay (Meta dashboard, SteamVR dashboard) has its own reserved button — apps cannot intercept it.
  • Recommendation: keep the dual-handed left-Y binding as the default and stop trying to preserve the stock settings menu in single-right mode. The stock menu contains exactly two settings the user cares about (locomotion speed, render quality); reproducing them in MesselpitMenuTool is much cheaper than fighting Kit's tool framework for a private extension hook. See "Recommendation" below.

1. Findings — Omniverse Kit specific

1.1 No documented extension point for the stock XR menu

Searching the Omniverse Kit docs and developer forums for "XRMenuTool", "XR settings menu", "extend XR menu", and omni.kit.xr.ui.stage returned zero results describing a supported way to add items into the stock right-B menu. The closest references are:

1.2 What the stock XRMenuTool actually is

From the prior in-VR-UI research (docs/in-vr-ui-research-2026-05-30.md) we already know the stock menu lives at omni.kit.xr.ui.stage/xr_tools/xr_menu_tool.py and is the canonical XRToolComponentBase reference implementation. Its panel content is not data-driven — it instantiates fixed widget classes from omni.kit.xr.advanced_settings_ui / omni.kit.xr.system.openxr.ui. There is no register_settings_item() API. Adding to it from outside means either (a) subclassing the tool and re-binding the action, which gives us the same situation as our own tool but with more coupling to NVIDIA internals, or (b) editing the vendored Kit source — which the project CLAUDE.md explicitly warns against.

1.3 The action-map merge is the extension point Kit did design

The action-map files in source/extensions/senckenberg.messelpit/xrmanifests/action_maps/ already demonstrate the supported extension model: ship a JSON manifest at higher priority, and Kit's action-map merger will either add a new tool (the dual-handed left-Y case — both tools coexist) or override an existing binding (the single-right right-B case — our tool wins, stock loses). This is working as designed; there's no third option that quietly inserts items into a tool you don't own.

1.4 kit-xr-samples is the closest official reference

The kit-xr-samples repo ships one sample — omni.kit.xr.samples.usd_scene_ui — which builds a SceneUI menu using the same XRSceneView + WidgetComponent shape we landed on for our own panel. It does not demonstrate extending the stock settings menu. NVIDIA's own answer to "I want a VR menu" is "build a separate SceneUI panel" — exactly what MesselpitMenuTool is.

2. Findings — broader VR ecosystem

2.1 Meta's official Touch button guidance

Meta's Touch Button Mapping Best Practices (Horizon developer blog) is explicit:

Button Recommended use
A / X Primary select
B / Y "Backing out or cancelling a selection" (B preferred over Y)
Trigger Select / fire
Grip Grab
≡ Menu "Bring up or dismiss a pause or in-app menu" ("strongly recommended … provides a more consistent experience across titles")
Oculus / Meta Reserved system button, not available to developers

Two important wrinkles for our project:

  1. The ≡ menu button still exists on Quest 3 Touch Plus, but only on the left controller (it's the three-line icon next to X/Y). Touch Plus docs. We could in principle bind to it.
  2. Kit's action maps don't expose it. The stock and our custom oculus_touch.json files bind to A/B/X/Y/triggers/grips but not to /input/menu/click. Adding it would require a custom binding plus verifying that Meta's Horizon Link OpenXR runtime forwards it to the app (some runtimes reserve it for the system overlay — see 2.4).

2.2 Shipping apps & engines

  • Unreal Engine VR Template binds B to a quick menu (Reset Orientation / Reset / Exit). UE docs.
  • Half-Life Alyx uses hold-Y for its pause menu on Rift/Quest — a deliberate long-press to avoid clashes with quick taps. Steam community guidance. This is the closest analogue to option (d) in the question.
  • Unity XR Interaction Toolkit documents the menu button as "used to pause, go back, or otherwise exit gameplay"; on Vive it's remapped to primaryButton for cross-platform consistency. Unity XR docs.
  • Walkabout / Job Simulator / Vacation Simulator etc. — convention is overwhelmingly "B or Y opens the app menu, hold for a moment to disambiguate from back/cancel" or "wrist/hand menu, no button".

2.3 The wrist-menu / hand-menu alternative

Meta's design docs have moved toward hand/wrist menus as the recommended pattern for hand-tracking-first apps. The pattern is "no button at all — look at the back of your left wrist to summon the menu". This is worth noting as a future option (especially for the eventual guided-tour mode for Senckenberg visitors who may not have controllers at all), but is out of scope for the current controller-driven build.

2.4 System menus are off-limits to apps

OpenXR has both /input/menu/click and /input/system/click. The system path is universally reserved by the runtime — pressing it opens the SteamVR dashboard or the Meta dashboard, and the application's event loop pauses (OnApplicationFocus(false) in Unity). See Khronos OpenXR interaction class notes and the Unity discussion "How to detect Universal Menu through OpenXR?". This means there is no "share the button with the system" trick — the system and app menus are always on different buttons.

The dashed /input/menu/click is technically available to apps but in practice some SteamVR/WMR runtimes also reserve it. The widely-supported binding for an in-app menu is therefore a face button, which is what every shipping app uses.

3. Recommendation for messelpit_viewer

Adopt option (a) with one improvement. Specifically:

  1. Dual-handed (Quest with both controllers) — keep left-Y for our menu, leave right-B with the stock Kit settings menu. This is the current default and matches no convention specifically but trades well: every shipping VR app uses B/Y for the app menu, and we put our app menu on Y (one of the two), reserving B for Kit's system-style settings. Document it in-headset (welcome panel / tooltip on first XR session).

  2. Single-right Touch — accept that we lose the stock Kit settings menu in this mode, and replicate its two useful settings inside MesselpitMenuTool. The stock menu's contents are largely navigation/locomotion speed and render-quality presets. Both are already-exposed carb.settings keys:

    • xr.navigation.speed (locomotion speed)
    • /rtx/* render presets (or /persistent/rtx/...)

    Adding a "Comfort" tab to MesselpitMenuTool with a speed slider plus a "Quality: Low/Med/High" segmented control closes the gap completely. This is exactly the pattern the project CLAUDE.md describes under "How to extend the UI".

  3. Do NOT attempt to subclass / monkey-patch XRMenuTool to inject our items into the stock panel. The Kit team did not design that extension point. Past attempts at touching upstream Kit internals in this repo (see git history around the application_mode startup) have produced fragile results.

  4. Defer the ≡-menu-button option. Binding to /input/menu/click on Touch Plus is technically possible and would be the "most conventional" choice on Quest, but it needs an OpenXR-runtime probe first (does Horizon Link forward it? does SteamVR over Link reserve it?) and a new action-map entry. Worth a 1-day spike after the Comfort tab lands. Track as roadmap item #12.

The reasoning, in one sentence: our MesselpitMenuTool is already the right architecture; the gap is content (locomotion / render settings), not plumbing.

4. Open questions / what I couldn't determine

  • Is there an internal-but-unexposed registry inside XRMenuTool for appending widgets? The forum results and docs say no, but I can't rule out an undocumented _register_settings_widget() hook in the Kit source. Worth a 10-minute grep in omni.kit.xr.ui.stage/, omni.kit.xr.advanced_settings_ui/, and omni.kit.xr.system.openxr/ before committing to option (a). If one exists, option (b) becomes feasible and we could ship a single-button experience.
  • Does Kit 110.x or 111 add an XR-menu extension API? I couldn't find an XR-specific changelog in the search results. The Kit changelog index would be worth scanning if a deeper investigation is justified.
  • Does Horizon Link's OpenXR runtime expose /input/menu/click to apps when using Quest 3 Touch Plus? This is the prerequisite for the deferred ≡-button option. Easiest answer: bind it in a test action map and see if events fire.

Sources

Omniverse / Kit:

VR ecosystem: