Commit Graph
128 Commits
Author SHA1 Message Date
Pierre Kisters 1612c162fb Docs: the VR keyboard relays under "Only while running" 2026-10-01 04:10:14 +02:00
Pierre Kisters e99a9e4a96 Cleanup: the artifact list lives in docs/cleanup.md only
install.sh's header comment repeated docs/cleanup.md's table; it points
there now. The script's tool list names grep, sed and awk too.
2026-10-01 04:10:04 +02:00
Pierre Kisters b48f644b1b Cleanup: the debugger restore reports a key changed to false as false, not absent
jq's // treats false like null. The runtime restore script changes with
it, so the next SteamVR start rewrites it once (one daemon-reload).
2026-10-01 04:10:04 +02:00
Pierre Kisters 1dd5df3d8b Install: a long list of untracked files doesn't abort the install
With pipefail, head closing the pipe early made git's SIGPIPE fail the
assignment under set -e.
2026-10-01 04:10:04 +02:00
Pierre Kisters 04023a72d9 Cleanup: drop an unused return value, a no-op case branch and a repeated mode test
debugger_ensure_hook's "changed" status was never read; clean_icons'
case for Home Manager's links did nothing; cmd_cleanup computes --all
once. --quiet in the usage synopsis.
2026-10-01 04:10:04 +02:00
Pierre Kisters eb8206f4ee Cleanup: a prefs.js that can't be read is left as is, not emptied
grep's read error (status 2) was swallowed with || true, so the profile's
prefs.js could be rewritten with nothing. Now it warns and keeps both
prefs.js and user.js for the next run.
2026-10-01 04:10:04 +02:00
Pierre Kisters 32bd719284 Cleanup: rewrites count as actions and keep the --quiet header to one
c_write was fed through a pipe, so it ran in a subshell: its action
count and the printed header were lost, which printed "nothing to clean
up" after a rewrite, and a second header with --quiet. It takes the
content as an argument now.
2026-10-01 04:10:04 +02:00
Pierre Kisters daf7c70ed3 Cleanup: a Firefox profile in use is detected again with other users' processes running
ff_in_use piped find into grep -q; with pipefail, find's error on any
unreadable /proc/<pid>/fd (root's processes) made every profile look
unused, so cleanup could remove user.js and rewrite prefs.js under a
running Firefox. The Nix check's sandbox has no such processes.
2026-10-01 04:10:04 +02:00
Pierre Kisters c810df7f5a VR keyboard: the keyboard popup lookup in the finder library
find.keyboardPopup() (finders.js VERSION 3) replaces the same
g_PopupManager lookup in the swipe, extra keys and touch typing patches
(VERSIONs 29, 24, 4). hub.js keeps its own: it gets no finder library.
2026-10-01 04:09:59 +02:00
Pierre Kisters 0908713667 VR keyboard: the strip relay resyncs once per burst of new page contexts
Every executionContextCreated (Runtime.enable alone reports each existing
context) started its own resync timer; one debounced timer now, as the
injectors do.
2026-10-01 04:09:13 +02:00
Pierre Kisters 378265389d VR keyboard: unused exports and fields removed
textmodel's lastReset (the patch logs the reason itself), __sfuiSwipe.model,
corrector's distance/fold and the decoder's N/W/cost/resample exports were
read by nothing (patch, tests, replay script).
2026-10-01 04:09:13 +02:00
Pierre Kisters 70e9a1aeb1 VR keyboard: a closed keyboard popup's instance detaches itself
Its 0.5 s poll (a SharedJSContext timer) kept running until the next
injection and could publish a hidden strip with its own sequence numbers
over the new popup's, so a pick on the panel was dropped as stale. The
poll now detaches it once the popup's document is gone.
2026-10-01 04:09:13 +02:00
Pierre Kisters 76de7734c1 VR keyboard extra keys: docs: the helper's debug variables, one place for the other-keymaps note
VRKBD_VERBOSE and VRKBD_DISPLAY were undocumented; the extra keys section
gets a Debugging line like the others. That the routing is harmless on
other keymaps was said twice; now once, under Layouts.
2026-10-01 04:09:13 +02:00
Pierre Kisters c99b0b699f VR keyboard extra keys: the helper reconnects when Steam's page closes while connecting
A CDP call sent after the socket closed (e.g. Steam restarting between
Runtime.enable and the first inject) waited forever, so the helper hung
instead of reconnecting. Such calls now resolve empty at once.
2026-10-01 04:09:13 +02:00
Pierre Kisters 514f073a6e VR keyboard extra keys: the helper holds only Ctrl/Alt, checked in allowlist.mjs
down:/up: were checked with MODKEY[arg], which inherited names like
constructor or toString pass (xdotool then got a junk key name). The check
is now allowedMod() in allowlist.mjs, covered by its test.
2026-10-01 04:09:13 +02:00
Pierre Kisters ad7e4f7678 UI patches: docs line wrapping, mkPatch link text 2026-10-01 04:09:13 +02:00
Pierre Kisters 521092d66b UI patches: no re-injection timer for a session that closed while attaching
The attach handler set the 15 s interval after its awaits even when the
socket had closed meanwhile (onclose already ran), leaking a timer per
such session. Also documents the optional stateDir config key.
2026-10-01 04:09:13 +02:00
Pierre Kisters d6ddad9cfe Window curvature docs: the control bar's factor 3 only in its own docs 2026-10-01 04:09:13 +02:00
Pierre Kisters df501a169b Window curvature: the press contract documented once
- docs/window-curvature.md holds the full contract (now also: the next
  press starts at 1x, return values, a drag keeps its value, neither side
  restores the other's state); the patch headers of window-curvature and
  frame-controls point to it and keep only their own side.
- frame-controls header: the localStorage history points to
  docs/ui-patches.md#persistent-state.
Comments only, no VERSION bumps.
2026-10-01 04:09:13 +02:00
Pierre Kisters c2427b4e20 Window control bar: signature expects without minified names
The Float action's import alias (l.yWq) and the frame controls' local
names (p, v) change with every SteamVR build and would warn after each
update; match the stable parts only.
2026-10-01 04:09:13 +02:00
Pierre Kisters 503a9e742b Jellyfin docs: "approximately" for command (as for the launchers' command), rewrapped lines 2026-10-01 04:09:13 +02:00
Pierre Kisters 11e41874c1 Firefox docs: one parenthesis for the desktop profile, rewrapped lines 2026-10-01 04:09:13 +02:00
Pierre Kisters b08600f9c3 Jellyfin: the shim check runs with set -euo pipefail, like the Firefox one 2026-10-01 04:09:13 +02:00
Pierre Kisters 2e259c49fe Firefox: the desktop profile wrapper keeps prefs.js when rewriting it fails
A failed write of prefs.js.sfn (e.g. a full disk) emptied prefs.js; now
prefs.js and the user.js link stay for the next launch to retry.
2026-10-01 04:09:13 +02:00
Pierre Kisters 4538ecea06 VR "+" menu grid: a popup whose window throws no longer stops the scan
p.window was read outside the guard, so an exception there ended the
1 s scan for the remaining popups (pinned-desktop already guards it).
2026-10-01 04:09:13 +02:00
Pierre Kisters 9cd22f047d Launchers, VR "+" menu: module header comments point to the docs
The patch list, keyring summary, triggers and icon fallback history are
in docs/launchers.md, docs/launcher-menu.md and next to the code they
describe.
2026-10-01 04:09:13 +02:00
Pierre Kisters b0ca57d4bd VR "+" menu icon fallbacks: hints no longer stop at an unreadable entry
A launcher whose Flatpak is not installed leaves a dangling link in
~/.local/share/applications; awk failed on it and set -e ended the
hints there.
2026-10-01 04:09:13 +02:00
Pierre Kisters 667422cb50 Docker: its user service in the runtime names table 2026-10-01 04:09:13 +02:00
Pierre Kisters 1b581a40e2 Session modules: header comments point to the docs instead of repeating them
SteamVR debugger and Docker keep only the internals the docs lack; the
screenshots script names package.nix as what prepends its variables.
2026-10-01 04:09:13 +02:00
Pierre Kisters 464099c1cd Clipboard sync: a process gone since pgrep no longer aborts the switch
ps fails for it, and with pipefail the assignment ended the activation;
it is skipped now.
2026-10-01 04:09:13 +02:00
Pierre Kisters ae8686e284 Session: the outer bus and runtime dir only for the user-service step
steamFrameUserServices exported XDG_RUNTIME_DIR and DBUS_SESSION_BUS_ADDRESS
into the rest of the activation script; a subshell keeps them to its
systemctl calls.
2026-10-01 04:09:13 +02:00
Pierre Kisters 2442527514 VR keyboard: cleanup of the controller bridge, gesture input and docs
- gesture-input.js: drop the unused mode getter and release().
- relay.mjs: both sides have a binding; no condition.
- hub.js: the frame cadence is documented once (bridge-patch.js); errors
  listed.
- docs: the bridge's idle cost and "one word at a time" stated once;
  twoHanded's README row points to the docs; rewrapped lines.
2026-10-01 04:01:00 +02:00
Pierre Kisters 605e42ffa1 VR keyboard controller bridge: no 11 ms timer while the keyboard is hidden
The TICK_MS loop ran (and returned at once) at ~90 Hz all the time; it now
runs only while SteamVR shows the keyboard, started and stopped by the
250 ms poll. The pose query's 1 s timeout timer is cleared once the query
settles. Frames are unchanged.
2026-10-01 04:01:00 +02:00
Pierre Kisters aa8ccc67c9 VR keyboard F-keys: F1-F12 in the suggestion strip while AltGr is active
keyboard.vr.functionKeys.enable (default off): while AltGr (Fn on layouts
without AltGr) is one-shot, locked or held, the strip shows F1-F12 instead
of the suggestions. A tap types the extra key VKX_F<n>, which the extra
keys' patch presses with xdotool together with the active Ctrl/Alt/Shift
(Alt+F4, Ctrl+F5) and releases the toggles; the text model resets, like
for Esc. The suggestions are kept as they are meanwhile, so when AltGr goes
off the strip shows the same items and selection again.

- vr-keyboard/function-keys.js: what the strip shows, the F-key keys.
- patch.js (VERSION 27): the strip's view, F-key picks, AltGr changes via
  a componentDidUpdate on the keyboard instance (and the poll).
- Allowlist: F1-F12 with any modifiers; Enter still refused.
- relay.mjs: up to 12 strip items.
- Assertions: needs keyboard.vr.enable, extraKeys.enable and a strip
  position "above"/"below" ("inside" would hide the AltGr number row).
- Tests: function-keys.test.mjs (switching and restoring, keys against
  the allowlist, text model), allowlist F-key cases.
2026-10-01 03:53:01 +02:00
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
Pierre Kisters 0699d0f532 VR keyboard touch typing: the touched key highlights (and long-presses)
Steam's UpdateTouchState counts the event's `touches` per key: that count
is the pressed highlight (rgLayoutTouchCount -> Touched) and where the long
press (Backspace repeat, accents) starts. The touch-start event left the
new touch out of `touches`, like a touchend; now it is in, as in a DOM
touchstart (tracker.js touchEvent, with a test against a model of Steam's
handlers).
2026-10-01 03:36:13 +02:00
Pierre Kisters 63d589c261 VR keyboard controller bridge: no traffic while idle
The relay no longer enables the Runtime domain on systemui (bindings work
without it; it streamed the page's console and context events). Full-rate
frames only while a tip is within 10 cm of the keyboard or a trigger is
pulled (not for a laser resting on it); idle frames every 1 s instead of
250 ms. Nothing while the keyboard is hidden.
2026-10-01 03:33:07 +02:00
Pierre Kisters 224715d926 VR keyboard touch typing, on a controller bridge from the SteamVR dashboard
keyboard.vr.touchTyping (off by default): a key is pressed when a
controller's tip, where SteamVR's laser starts (/pose/tip: the device pose
times the render model's "tip" component), passes through it from the
front; released when pulled back 1 cm, re-armed 5 mm in front. Both hands,
also at once. Keys go through the keyboard's own HandleTouchStart /
HandleTouchEnd, like a laser press (Backspace repeats while held). Options
depth (cm) and haptics.

The geometry lives in a reusable controller bridge (vr-keyboard-controllers,
internal option): a systemui patch reads the keyboard's pose with SteamVR's
SGQueryService (an empty transform of ours in the keyboard's mount) and both
controllers' poses, and streams per hand the tip, the laser's hit on the
keyboard and the trigger (~90 Hz while relevant) through the
vr-keyboard-controllers-relay service to __sfuiControllers in Steam's
SharedJSContext. Tests: flake checks vr-keyboard-controllers and
vr-keyboard-touch.
2026-10-01 03:30:01 +02:00
Pierre Kisters 7d050b8c44 VR keyboard swipe: only the pressing controller's laser builds the path
With both controllers on the keyboard, the idle one's hover (mouse events,
pointerId 1) arrived during the other's press and went into the swipe path
("tust" for "test"). A gesture now belongs to the contact that started it
(touch identifier, pointerId or mouse; gesture-input.js): other contacts'
moves, releases and cancels are ignored, a second press meanwhile stays
Steam's tap. Test with a recorded two-controller swipe.
2026-10-01 03:12:36 +02:00
Pierre Kisters 26641d76a0 SteamVR screenshots linked into ~/Pictures
Steam keeps VR screenshots (Steam button + trigger) in
~/.local/share/Steam/userdata/<account ID>/760/remote/250820/screenshots,
where Dolphin, Gwenview and the file pickers don't look.

screenshots (module screenshots, opt-in) links ~/Pictures/<name> (default
"SteamVR Screenshots") there. The account ID comes from steamUserId, or,
unset, at runtime: the Home Manager link points to
<runtimeDir>/steam-frame-nix/screenshots (tmpfs), which the oneshot user
service steam-frame-nix-screenshots points to the account last logged in
(loginusers.vdf MostRecent, else latest timestamp, else the only userdata
folder), on switch, at login and when loginusers.vdf or userdata changes.

install.sh cleanup knows the tmpfs link (--keep screenshots while used).
Flake checks: account detection on fake Steam dirs; cleanup of the link.
2026-09-30 22:54:13 +02:00
Pierre Kisters b2babb3859 VR keyboard: Shift+Tab moves the focus backwards
Steam sends Tab as the text "\t" and drops Shift. With Shift, Tab now
goes to the xdotool helper (shift+Tab, ISO_Left_Tab on any keymap); the
suggestions text model resets on it like on Shift+arrows. The helper's
allowlist moves to allowlist.mjs with a test (flake check
vr-keyboard-extra-keys).
2026-09-30 22:53:24 +02:00
Pierre Kisters 50e5991c59 Portal: KDE file dialog for Flatpak apps in the Steam session
Neither Valve backend (gamescope, holo) implements FileChooser, so sandboxed
apps fell back to an in-sandbox dialog without the home dir (e.g. Element's
Attachments). session.portalFix.fileChooser (on) links kde.portal into the
Steam session's portal dir and selects kde for FileChooser;
xdg-desktop-portal-kde is D-Bus activated with DISPLAY=:0. Flake check
for the portal config.
2026-09-29 03:20:12 +02:00
Pierre Kisters 356dcb9012 docs: VR keyboard screenshots (extra keys, swipe) 2026-09-29 03:20:04 +02:00
Pierre Kisters 59513bcbb1 Applications menu for KDE apps in the Steam session
SteamOS only ships /etc/xdg/menus/plasma-applications.menu; the nested
desktop sets XDG_MENU_PREFIX=plasma-, the Steam session doesn't, so KDE
apps started there (Dolphin from the "+" menu) built an empty app
database: "no installed application can open" every file.

session.applicationsMenu (module applications-menu, on by default) links
~/.config/menus/applications.menu to Plasma's menu. Flake check: the link
is generated by default and absent when disabled.
2026-09-29 03:01:38 +02:00
Pierre Kisters 0d9a238e5a Launchers: apps' own desktop entries, rewritten; keyring moves into them
steamFrame.launchers.<desktop ID> takes the app's own entry (a Flatpak's
export, a Nix package's or a host file) and rewrites only its command lines
(hostEnv, env, flatpakArgs, args, wrappers) and the keys asked for
(mimeTypes, defaultFor, settings), so name, icon, translations and actions
stay the app's and follow its updates.

- Package entries are rewritten at build time; Flatpak/host file entries at
  runtime by steam-frame-nix-launchers into
  <session.runtimeDir>/steam-frame-nix/applications (tmpfs), on switch, at
  login and when Flatpak installs/updates apps (path unit); the Home Manager
  links point there.
- keyring.{enable,electron} replaces steamFrame.keyring.{flatpaks,programs}
  (clean break: the old options fail with the new form).
- Firefox's launcher is the Flatpak's entry with the profile wrapper in
  front (no --profile for the profile manager); Jellyfin's hardware
  decoding sets flatpakArgs and env.
- cleanup --all removes the tmpfs entries.
- Check on copies of the real Element, KRDC, Firefox, gedit, Jellyfin and
  Claude entries, quoting and generator behaviour.
2026-09-29 02:05:06 +02:00
Pierre Kisters 5d15902ba1 Clearer file names and layout; docs/development.md
- steam-keyboard-patch.nix -> vr-keyboard-extra-keys.nix (option
  keyboard.vr.extraKeys), helper.mjs -> xdotool-helper.mjs;
  homeManagerModules.steam-keyboard-patch stays as an alias.
- vr-keyboard: panel.js/unpatch-panel.js/relay.mjs ->
  suggestions-panel/{patch,unpatch}.js + relay.mjs; decoder.js ->
  swipe-decoder.js; build.nix split into dictionary.nix and check.nix.
- modules/lib -> modules/steam-ui-patches/lib (the UI patch library).
- docs/development.md: repository layout (what runs where), runtime names.

Patch names, user services and state files are unchanged.
2026-09-29 01:42:34 +02:00
Pierre Kisters b3d93e8526 Trim: firefox launcher, debugger-arm migration, comments and docs
- firefox: the prefs.js patterns built once; the extension link condition
  without the redundant desktopFix (it implies a pref).
- install.sh steamvr-debugger-arm: no second copy of the old-marker
  migration (cleanup, run on every switch before the arm unit exists,
  already does it).
- steam-ui-patches: shorter state option description; lib's description
  mentions extraArgs.
- README: uiPatches.patches fields in the options table (moved from
  docs/ui-patches.md); docs/ui-patches.md: the finder helpers;
  docs/steamvr-debugger.md: repeated sentences removed.
2026-09-29 01:33:16 +02:00
Pierre Kisters a5ecbad76d lib: shared resolvePatch, ensureStyle, logger; legacy wrapper code removed
- finders.js (VERSION 2): resolvePatch (resolveAll or the "signature not
  found, ... left unpatched" status), ensureStyle and logger, used by the
  patches instead of their own copies.
- hooks.js (VERSION 2) no longer unwinds per-patch wrappers of old patch
  versions; dashboard-windows and steam-close-button drop their handling of
  versions <= 2 / <= 6 (in-page only, gone with any SteamVR restart).
- VERSIONs: dashboard-windows 4, steam-close-button 9, window-curvature 21,
  frame-controls 7, steam-keyboard-patch 22, launcher-menu grid 7,
  pinned-desktop 4, show-all 2.
2026-09-29 01:32:41 +02:00
Pierre Kisters 36886d61df Small fixes: quiet service stops, duplicate scheme handlers, script nits
- session.services.stop: stop only units that are active; stopping a unit
  that isn't loaded printed "Failed to stop" on every switch (by default
  for steam-ui-patches, steam-keyboard-patch and vr-keyboard-relay).
- keyring: a scheme in the schemeHandlers of two apps is an assertion
  instead of one app silently winning.
- vr-keyboard-replay.mjs: recorded paths have no time; label them by index.
- webpack-modules.mjs: skip page files that are missing instead of throwing.
2026-09-29 01:32:31 +02:00
Pierre Kisters b942352663 Injectors: no re-injection once the unpatch has started
On SIGTERM the periodic (and debounced) re-injection of injector.mjs and
helper.mjs could still run while or after the unpatch was evaluated, leaving
the page patched after the service was gone (e.g. a feature just turned off).
helper.mjs also gets a timeout on /json/list, like the injector.
2026-09-29 01:32:31 +02:00