Files
lhns--steam-frame-nix/docs/development.md
T
Pierre Kisters af32cf72ce VR keyboard swipe with two controllers: the path from the controller bridge
With both lasers on the keyboard SteamVR forwards per poll only one laser's
movement, so the pressing one may get no touchmoves and its swipe became a
tap. keyboard.vr.swipe.twoHanded (default on; turns on the controller
bridge): a press is attributed to the hand whose laser hit is nearest to it
(within 30 px, the trigger as a tiebreak; without recent frames the first
frame after the press decides, within 60 px). The path comes from runs of
one source: the touch's own moves while they flow, that hand's laser hit
per bridge frame once they pause for 60 ms, touchmoves again if frames
pause. A touchend disagreeing with the laser during a bridge run ends at
the laser. Touchmoves on the other hand's laser move the attribution.
Without fresh frames: touch events only, as before. A second press during
a swipe stays Steam's tap (one text field, one word at a time).

The bridge sends full rate only on demand now: hub.js demand(ms) goes
back through the relay (CDP binding __sfuiCtlIn) to systemui's
__sfuiCtl.demand; the swipe patch asks from the press to the release.

Tests: gesture-input with the two-controller recording and synthetic bridge
frames (a pressing hand without touchmoves, attribution with both lasers on
the keyboard, a stale bridge, frames stopping mid-swipe, re-attribution,
interleaved hover and presses); hub demand.
2026-10-01 03:45:47 +02:00

10 KiB

Development

Which file does what and where it runs, for working on steam-frame-nix itself. Writing your own patches: UI patches.

Conventions

  • modules/<name>.nix is homeManagerModules.<name>; files only it uses are in modules/<name>/.
  • patch.js / unpatch.js: a runtime patch of a web page and the expression that reverts it. A module with several patches has one directory per patch (launcher-menu/grid/, vr-keyboard/suggestions-panel/).
  • *.js in modules/ is browser JavaScript evaluated in Steam's or SteamVR's pages (single expressions, no imports); *.mjs is a Node program (user service, test or script).
  • check.nix is the module's flake check (nix flake check, attribute checks.<system>.<name>); tests/*.test.mjs are run by it.
  • Runtime names (patch names, user services, state files) are listed below; they stay stable when files move.

Where things run:

Tag Where
Steam Steam's UI, page SharedJSContext, DevTools 127.0.0.1:8080
SteamVR SteamVR's dashboard (vrwebhelper), page systemui, DevTools 127.0.0.1:8087
service systemd user service (outer Steam/VR session)
switch Home Manager activation (home-manager switch)
build Nix build time
app started with an app (launcher, LD_PRELOAD, autostart)
test nix flake check
dev run by hand from a checkout

Repository layout

flake.nix                      homeManagerModules, packages/apps (cleanup), checks, template
install.sh                     install / uninstall / cleanup (curl | bash; also the
                               steam-frame-nix-cleanup package and the debugger arm step)
template/                      `nix flake init -t` / installer config: flake.nix, home.nix
docs/                          one page per feature; ui-patches.md for patch authors
scripts/
  check-signatures.mjs         dev: check signatures.json against the installed Steam
  webpack-modules.mjs          dev: webpack module extraction (used by check-signatures)
  vr-keyboard-replay.mjs       dev: replay recorded swipes through the swipe decoder
modules/
  session.nix                  switch: outer bus/runtime dir, user services on switch
  cleanup.nix                  switch: `cleanup --orphans`; steam-frame-nix-cleanup on PATH
  cleanup/package.nix          build: install.sh as a command (cleanup, steamvr-debugger-arm)
  cleanup/check.nix            test: install.sh cleanup on fake home/runtime dirs
  portal.nix                   Steam session portal config (session.portalFix)
  portal/check.nix             test: KDE FileChooser on by default, absent when disabled
  applications-menu.nix        applications.menu link for KDE apps (session.applicationsMenu)
  applications-menu/check.nix  test: the link on by default, absent when disabled
  keyboard-layout.nix          gamescope-session drop-in with XKB_DEFAULT_* (keyboard.layout)
  clipboard-sync.nix           app: KDE autostart of clipboard-sync (clipboardSync)
  hidden-apps.nix              Hidden=true desktop entries (launcherMenu.hiddenApps)
  launchers.nix                links, MIME defaults, units of the launchers (launchers.<id>)
  launchers/
    lib.nix                    the launcher option type, Exec quoting, package entries (build)
    rewrite.awk                build+service: rewrites a desktop entry's Exec lines and keys
    generate.sh                service steam-frame-nix-launchers (+ .path), switch: entries
                               of Flatpaks/host files in <runtimeDir>/steam-frame-nix/applications
    check.nix, fixtures/       test: real entries rewritten (fixtures/expected), generator runs
  docker.nix                   service: rootless dockerd (docker)
  screenshots.nix              ~/Pictures link to the SteamVR screenshots (screenshots)
  screenshots/
    link.sh, package.nix       service steam-frame-nix-screenshots (+ .path), switch:
                               <runtimeDir>/steam-frame-nix/screenshots -> account folder
    check.nix                  test: account detection on fake Steam dirs
  firefox.nix                  Flatpak prefs extension and launcher (firefox)
  firefox/wrapper.nix          app: launcher wrapper (desktop profile, fullscreen fix)
  firefox/check.nix            test: wrapper against a fake flatpak
  jellyfin.nix                 launcher with flatpak run options (jellyfin.hardwareDecoding)
  jellyfin/mpv-hwdec-shim.c    app: LD_PRELOAD shim, hwdec auto* -> v4l2m2m-copy
  jellyfin/shim.nix            build: the shim as lib/mpv-hwdec-shim.so
  jellyfin/check.nix           test: shim ELF and rewriting
  steam-ui-patches.nix         service steam-ui-patches: runs the injector (uiPatches.*)
  steam-ui-patches/
    injector.mjs               service: injects/re-injects/reverts patches over DevTools
    lib/default.nix            build: mkPatch (wraps a patch.js with the library)
    lib/finders.js             Steam+SteamVR: signature lookup of webpack modules/React fibers
    lib/hooks.js               Steam+SteamVR: shared method hooks (e.g. SendMessage)
    lib/signatures.json        build+dev: per-patch signatures (also check-signatures)
  steamvr-debugger.nix         service steamvr-webhelper-debugger: DevTools port 8087
  launcher-menu.nix            the VR "+" menu (launcherMenu.*), icon fallbacks
  launcher-menu/
    order/                     Steam: sort the list
    pinned-desktop/            Steam: pin Desktop above/below the list
    launch/                    Steam: close on launch, debounce
    grid/                      Steam: programs as a grid of tiles
    show-all/                  Steam: all programs without Developer Mode
    icon-fallbacks.sh          switch: hints for iconFallbacks (read-only)
  vr-keyboard-extra-keys.nix   Esc/Ctrl/Alt/arrows etc. (keyboard.vr.extraKeys)
  vr-keyboard-extra-keys/
    patch.js, unpatch.js       Steam: the extra bottom row and key routing
    xdotool-helper.mjs         service steam-keyboard-patch: own injector + xdotool keys
    allowlist.mjs              service: the keys the helper may send
    check.nix, tests/          test: the allowlist
  vr-keyboard.nix              swipe, suggestions, Backspace drag (keyboard.vr)
  vr-keyboard/
    patch.js, unpatch.js       Steam: gestures, text model, suggestion strip
    swipe-decoder.js           Steam (argument of patch.js): swipe path -> words
    textmodel.js               Steam (argument of patch.js): what the keyboard typed
    corrector.js               Steam (argument of patch.js): corrections, completions
    gesture-input.js           Steam (argument of patch.js): a gesture's own events, its hand's bridge path
    suggestions-panel/
      patch.js, unpatch.js     SteamVR: the strip as a panel above/below the keyboard
      relay.mjs                service vr-keyboard-relay: strip state Steam <-> SteamVR
    dictionary.nix, gen-dict.py  build: dictionary from wordfreq + Hunspell
    check.nix, tests/          test (also built before the patch): text model,
                               corrector, swipe-decoder accuracy, gesture input
  vr-keyboard-controllers.nix  controller bridge for keyboard features (internal option)
  vr-keyboard-controllers/
    bridge-patch.js, unpatch.js  SteamVR: keyboard pose, tips, laser hits, triggers
    geometry.js                SteamVR (argument of bridge-patch.js): tip/laser -> keyboard
    hub.js                     Steam (argument of consumer patches): __sfuiControllers
    relay.mjs                  service vr-keyboard-controllers-relay: frames SteamVR -> Steam, demand back
    check.nix, tests/          test: geometry, hub
  vr-keyboard-touch.nix        touch typing (keyboard.vr.touchTyping)
  vr-keyboard-touch/
    keyboard-patch.js, keyboard-unpatch.js  Steam: presses touched keys
    tracker.js                 Steam (argument of keyboard-patch.js): contact, hysteresis
    check.nix, tests/          test (also built before the patch): touch paths
  dashboard-windows.nix, dashboard-windows/     SteamVR: window scale/distance limits
  steam-close-button.nix, steam-close-button/   SteamVR: X on the Steam window
  window-curvature.nix, window-curvature/       SteamVR: curvature wheel
  frame-controls.nix, frame-controls/           SteamVR: window control bar

Runtime names

Patch names appear in the journal, key signatures.json and name the state files (~/.local/state/steam-frame-nix/ui-patches/<name>.json), so they are kept even where a file name says more:

Module Option Patches (page) User services
launcher-menu launcherMenu launcher-menu-{order,pinned-desktop,launch,grid,show-all} (Steam) steam-ui-patches
vr-keyboard keyboard.vr vr-keyboard (Steam), vr-keyboard-panel = suggestions-panel/ (SteamVR) steam-ui-patches, vr-keyboard-relay
vr-keyboard-extra-keys keyboard.vr.extraKeys steam-keyboard-patch (Steam) steam-keyboard-patch
vr-keyboard-touch keyboard.vr.touchTyping vr-keyboard-touch (Steam) steam-ui-patches
vr-keyboard-controllers (internal, keyboard.vr.controllers) vr-keyboard-controllers (SteamVR) steam-ui-patches, vr-keyboard-controllers-relay
dashboard-windows dashboard.windows dashboard-windows (SteamVR) steam-ui-patches
steam-close-button dashboard.steamCloseButton steam-close-button (SteamVR, state) steam-ui-patches
window-curvature dashboard.windowCurvature window-curvature (SteamVR) steam-ui-patches
frame-controls dashboard.frameControls frame-controls (SteamVR, state) steam-ui-patches
steamvr-debugger steamvrDebugger steamvr-webhelper-debugger
launchers launchers steam-frame-nix-launchers (.service, .path)
screenshots screenshots steam-frame-nix-screenshots (.service, .path)

Log of all patches: journalctl --user -u steam-ui-patches -u steam-keyboard-patch -u vr-keyboard-relay -u vr-keyboard-controllers-relay.

Checks

nix flake check                                            # all checks, on aarch64-linux
nix shell nixpkgs#nodejs -c node scripts/check-signatures.mjs --strict   # on the Frame