mirror of
https://github.com/lhns/steam-frame-nix.git
synced 2026-10-06 01:00:13 +02:00
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.
This commit is contained in:
1 parent
b3d93e8526
commit
5d15902ba1
40 files changed
+330
-161
No files matched your search
@@ -57,6 +57,8 @@ configuration, limitations and how it works.
|
|||||||
|
|
||||||
**Your own patches** of Steam's UI, and fixing patches after a Steam update: [UI patches](docs/ui-patches.md).
|
**Your own patches** of Steam's UI, and fixing patches after a Steam update: [UI patches](docs/ui-patches.md).
|
||||||
|
|
||||||
|
**Working on steam-frame-nix:** [Development](docs/development.md) (repository layout: which file runs where, runtime names, checks).
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
On the Frame (or a Steam Deck), in a terminal (Konsole in desktop mode or the
|
On the Frame (or a Steam Deck), in a terminal (Konsole in desktop mode or the
|
||||||
@@ -213,9 +215,11 @@ home-manager switch --flake .#steamos # manual setup, from the flake's director
|
|||||||
In your own flake, add the input as above and
|
In your own flake, add the input as above and
|
||||||
`steam-frame-nix.homeManagerModules.default` to the modules. `default`
|
`steam-frame-nix.homeManagerModules.default` to the modules. `default`
|
||||||
imports all modules; single ones:
|
imports all modules; single ones:
|
||||||
`homeManagerModules.{session,portal,keyboard-layout,steam-keyboard-patch,vr-keyboard,hidden-apps,steam-ui-patches,launcher-menu,steamvr-debugger,cleanup,dashboard-windows,steam-close-button,window-curvature,frame-controls,clipboard-sync,firefox,jellyfin,keyring,docker}`.
|
`homeManagerModules.{session,portal,keyboard-layout,vr-keyboard-extra-keys,vr-keyboard,hidden-apps,steam-ui-patches,launcher-menu,steamvr-debugger,cleanup,dashboard-windows,steam-close-button,window-curvature,frame-controls,clipboard-sync,firefox,jellyfin,keyring,docker}`
|
||||||
Every module imports `cleanup` (see
|
(`steam-keyboard-patch` still works as the former name of
|
||||||
[Changes outside Nix](#changes-outside-nix-exceptions)).
|
`vr-keyboard-extra-keys`). Every module imports `cleanup` (see
|
||||||
|
[Changes outside Nix](#changes-outside-nix-exceptions)); which file is
|
||||||
|
which: [Repository layout](docs/development.md).
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
# Development
|
||||||
|
|
||||||
|
Which file does what and where it runs, for working on steam-frame-nix
|
||||||
|
itself. Writing your own patches: [UI patches](ui-patches.md).
|
||||||
|
|
||||||
|
## 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](#runtime-names); 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
|
||||||
|
|
||||||
|
```text
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
keyring.nix app: launchers sharing the KDE wallet (keyring)
|
||||||
|
docker.nix service: rootless dockerd (docker)
|
||||||
|
firefox.nix Flatpak prefs extension and launcher entry (firefox)
|
||||||
|
firefox/launcher.nix app: launcher script (desktop profile, fullscreen fix)
|
||||||
|
firefox/check.nix test: launcher against a fake flatpak
|
||||||
|
jellyfin.nix desktop entry 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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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` |
|
||||||
|
| `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` |
|
||||||
|
|
||||||
|
Log of all patches:
|
||||||
|
`journalctl --user -u steam-ui-patches -u steam-keyboard-patch -u vr-keyboard-relay`.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nix flake check # all checks, on aarch64-linux
|
||||||
|
nix shell nixpkgs#nodejs -c node scripts/check-signatures.mjs --strict # on the Frame
|
||||||
|
```
|
||||||
+10
-7
@@ -8,7 +8,8 @@ The keyboard layout of the Steam session itself is a separate fix
|
|||||||
|
|
||||||
## Extra keys
|
## Extra keys
|
||||||
|
|
||||||
`keyboard.vr.extraKeys.enable`, module `steam-keyboard-patch`.
|
`keyboard.vr.extraKeys.enable`, module `vr-keyboard-extra-keys` (sources in
|
||||||
|
`modules/vr-keyboard-extra-keys/`).
|
||||||
|
|
||||||
**Problem:** Steam's VR keyboard has no Ctrl, Alt or Esc, can't press real
|
**Problem:** Steam's VR keyboard has no Ctrl, Alt or Esc, can't press real
|
||||||
keys, and its text emulation only maps plain ASCII: non-ASCII and
|
keys, and its text emulation only maps plain ASCII: non-ASCII and
|
||||||
@@ -55,9 +56,10 @@ Steam client 1790377368 (UI build 11041156).
|
|||||||
|
|
||||||
### How it works
|
### How it works
|
||||||
|
|
||||||
- The `steam-keyboard-patch` user service (`helper.mjs`) injects a patch
|
- The `steam-keyboard-patch` user service
|
||||||
into Steam's `SharedJSContext` over DevTools (`127.0.0.1:8080`) and
|
(`modules/vr-keyboard-extra-keys/xdotool-helper.mjs`) injects a patch into
|
||||||
re-injects it after Steam restarts; stopping it (or disabling the option)
|
Steam's `SharedJSContext` over DevTools (`127.0.0.1:8080`) and re-injects
|
||||||
|
it after Steam restarts; stopping it (or disabling the option)
|
||||||
runs the unpatch.
|
runs the unpatch.
|
||||||
- Ctrl/Alt chords and Esc are sent with `xdotool key` on `:0` (focus follows
|
- Ctrl/Alt chords and Esc are sent with `xdotool key` on `:0` (focus follows
|
||||||
the VR-selected window); a toggled Ctrl/Alt is held down with `xdotool`.
|
the VR-selected window); a toggled Ctrl/Alt is held down with `xdotool`.
|
||||||
@@ -133,15 +135,16 @@ has the letters.
|
|||||||
### How it works
|
### How it works
|
||||||
|
|
||||||
- A Steam UI patch (`vr-keyboard`, injected by `steam-ui-patches` like the
|
- A Steam UI patch (`vr-keyboard`, injected by `steam-ui-patches` like the
|
||||||
other [UI patches](ui-patches.md)).
|
other [UI patches](ui-patches.md)); sources in `modules/vr-keyboard/`.
|
||||||
- Swiped words are matched by shape (SHARK2-style template matching)
|
- Swiped words are matched by shape (SHARK2-style template matching)
|
||||||
against a dictionary built at build time from wordfreq frequency lists
|
against a dictionary built at build time from wordfreq frequency lists
|
||||||
and Hunspell, both from nixpkgs (per language the `words` most frequent
|
and Hunspell, both from nixpkgs (per language the `words` most frequent
|
||||||
wordfreq entries, shifted by `frequencyOffset`, filtered by Hunspell
|
wordfreq entries, shifted by `frequencyOffset`, filtered by Hunspell
|
||||||
except words at or above `keepFrequentAbove`).
|
except words at or above `keepFrequentAbove`).
|
||||||
- The strip below/above the keyboard is a SteamVR dashboard panel
|
- The strip below/above the keyboard is a SteamVR dashboard panel
|
||||||
(`panel.js`, a patch of SteamVR's `systemui` page on port 8087), fed by
|
(`suggestions-panel/patch.js`, a patch of SteamVR's `systemui` page on
|
||||||
the `vr-keyboard-relay` user service (`relay.mjs`) between the two pages.
|
port 8087), fed by the `vr-keyboard-relay` user service
|
||||||
|
(`suggestions-panel/relay.mjs`) between the two pages.
|
||||||
With `inside` neither the panel nor the relay runs.
|
With `inside` neither the panel nor the relay runs.
|
||||||
- Found by signature (entries `vr-keyboard`, `vr-keyboard-panel`).
|
- Found by signature (entries `vr-keyboard`, `vr-keyboard-panel`).
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ its option is set and reverted by its unpatch when unset:
|
|||||||
sounds and controller navigation keep working;
|
sounds and controller navigation keep working;
|
||||||
- `show-all/`: empties the list Steam hides without Developer Mode.
|
- `show-all/`: empties the list Steam hides without Developer Mode.
|
||||||
|
|
||||||
Their anchors (APIs, React props, CSS) are in `modules/lib/signatures.json`
|
Their anchors (APIs, React props, CSS) are in `modules/steam-ui-patches/lib/signatures.json`
|
||||||
and verified by the [offline checker](ui-patches.md#after-a-steam-update).
|
and verified by the [offline checker](ui-patches.md#after-a-steam-update).
|
||||||
|
|
||||||
## Hidden apps
|
## Hidden apps
|
||||||
|
|||||||
+7
-6
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
`uiPatches.patches`, `uiPatches.lib` (modules `steam-ui-patches`,
|
`uiPatches.patches`, `uiPatches.lib` (modules `steam-ui-patches`,
|
||||||
`steamvr-debugger`). For patch authors, and for fixing patches after a Steam
|
`steamvr-debugger`). For patch authors, and for fixing patches after a Steam
|
||||||
update. Options: [README, Options](../README.md#options).
|
update. Options: [README, Options](../README.md#options). Which file runs
|
||||||
|
where: [Development](development.md).
|
||||||
|
|
||||||
## Problem
|
## Problem
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ it runs. What they have in common:
|
|||||||
- Each turns on the [SteamVR debugger](steamvr-debugger.md) (**the first
|
- Each turns on the [SteamVR debugger](steamvr-debugger.md) (**the first
|
||||||
time, restart SteamVR once**).
|
time, restart SteamVR once**).
|
||||||
- They depend on SteamVR UI internals: each is found by signature (its entry
|
- They depend on SteamVR UI internals: each is found by signature (its entry
|
||||||
in `modules/lib/signatures.json` has the patch's name); after an update
|
in `modules/steam-ui-patches/lib/signatures.json` has the patch's name); after an update
|
||||||
that changes them the dashboard stays stock (see
|
that changes them the dashboard stays stock (see
|
||||||
[after a Steam update](#after-a-steam-update)). Tested with SteamVR build
|
[after a Steam update](#after-a-steam-update)). Tested with SteamVR build
|
||||||
11008059.
|
11008059.
|
||||||
@@ -110,7 +111,7 @@ see [changes outside Nix](../README.md#changes-outside-nix-exceptions).
|
|||||||
## Finders and signatures
|
## Finders and signatures
|
||||||
|
|
||||||
Webpack module ids and export names change with every Steam UI build, so
|
Webpack module ids and export names change with every Steam UI build, so
|
||||||
patches never use them. `modules/lib/finders.js` (like Decky Loader's
|
patches never use them. `modules/steam-ui-patches/lib/finders.js` (like Decky Loader's
|
||||||
`findModule`/`findInReactTree` or Vencord's `find`) locates by *signature*:
|
`findModule`/`findInReactTree` or Vencord's `find`) locates by *signature*:
|
||||||
|
|
||||||
- a **module** by strings/regexes in its factory source;
|
- a **module** by strings/regexes in its factory source;
|
||||||
@@ -126,7 +127,7 @@ candidates r_, xy` as the patch's result. Results are cached per page. The
|
|||||||
library also has `ensureStyle(doc, id, css)` and `logger(buffer)` (a capped
|
library also has `ensureStyle(doc, id, css)` and `logger(buffer)` (a capped
|
||||||
debug log).
|
debug log).
|
||||||
|
|
||||||
Signatures live in `modules/lib/signatures.json`, shared by patches and the
|
Signatures live in `modules/steam-ui-patches/lib/signatures.json`, shared by patches and the
|
||||||
offline checker. An entry can also list `expects` (strings the patch relies
|
offline checker. An entry can also list `expects` (strings the patch relies
|
||||||
on, checked offline only) or be `checkOnly` (anchors not used through the
|
on, checked offline only) or be `checkOnly` (anchors not used through the
|
||||||
finder, checked offline only; with `stylesheet` instead of `module` it is
|
finder, checked offline only; with `stylesheet` instead of `module` it is
|
||||||
@@ -137,7 +138,7 @@ matched against the bundle's CSS).
|
|||||||
`steamFrame.uiPatches.lib.mkPatch` wraps a patch file — a function
|
`steamFrame.uiPatches.lib.mkPatch` wraps a patch file — a function
|
||||||
expression `(find, sigs, opts, hooks) => …` returning a status string — with
|
expression `(find, sigs, opts, hooks) => …` returning a status string — with
|
||||||
the finder library, its signatures, options and the shared hooks (details in
|
the finder library, its signatures, options and the shared hooks (details in
|
||||||
`modules/lib/default.nix`):
|
`modules/steam-ui-patches/lib/default.nix`):
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
steamFrame.uiPatches.patches = [ {
|
steamFrame.uiPatches.patches = [ {
|
||||||
@@ -167,7 +168,7 @@ steamFrame.uiPatches.patches = [ {
|
|||||||
|
|
||||||
### Shared method hooks
|
### Shared method hooks
|
||||||
|
|
||||||
`modules/lib/hooks.js`, argument `hooks`, also `window.__sfuiHooks`: patches
|
`modules/steam-ui-patches/lib/hooks.js`, argument `hooks`, also `window.__sfuiHooks`: patches
|
||||||
intercepting the same method (e.g. the dashboard mailbox's `SendMessage`,
|
intercepting the same method (e.g. the dashboard mailbox's `SendMessage`,
|
||||||
used by [dashboard windows](dashboard-windows.md#how-it-works) and
|
used by [dashboard windows](dashboard-windows.md#how-it-works) and
|
||||||
[window curvature](window-curvature.md#how-it-works)) register named hooks;
|
[window curvature](window-curvature.md#how-it-works)) register named hooks;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
session = ./modules/session.nix;
|
session = ./modules/session.nix;
|
||||||
portal = ./modules/portal.nix;
|
portal = ./modules/portal.nix;
|
||||||
keyboard-layout = ./modules/keyboard-layout.nix;
|
keyboard-layout = ./modules/keyboard-layout.nix;
|
||||||
steam-keyboard-patch = ./modules/steam-keyboard-patch.nix;
|
vr-keyboard-extra-keys = ./modules/vr-keyboard-extra-keys.nix;
|
||||||
vr-keyboard = ./modules/vr-keyboard.nix;
|
vr-keyboard = ./modules/vr-keyboard.nix;
|
||||||
hidden-apps = ./modules/hidden-apps.nix;
|
hidden-apps = ./modules/hidden-apps.nix;
|
||||||
steam-ui-patches = ./modules/steam-ui-patches.nix;
|
steam-ui-patches = ./modules/steam-ui-patches.nix;
|
||||||
@@ -37,10 +37,15 @@
|
|||||||
keyring = ./modules/keyring.nix;
|
keyring = ./modules/keyring.nix;
|
||||||
docker = ./modules/docker.nix;
|
docker = ./modules/docker.nix;
|
||||||
};
|
};
|
||||||
|
# Former attribute names, kept so existing imports keep working (not in
|
||||||
|
# `default`, which imports each module once under its current name).
|
||||||
|
aliases = {
|
||||||
|
steam-keyboard-patch = modules.vr-keyboard-extra-keys; # its name until 2026-09
|
||||||
|
};
|
||||||
systems = [ "aarch64-linux" "x86_64-linux" ];
|
systems = [ "aarch64-linux" "x86_64-linux" ];
|
||||||
forSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
|
forSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
|
||||||
in {
|
in {
|
||||||
homeManagerModules = modules // {
|
homeManagerModules = modules // aliases // {
|
||||||
default = { imports = builtins.attrValues modules; };
|
default = { imports = builtins.attrValues modules; };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,16 +69,7 @@
|
|||||||
cleanup = import ./modules/cleanup/check.nix { inherit pkgs; };
|
cleanup = import ./modules/cleanup/check.nix { inherit pkgs; };
|
||||||
firefox = import ./modules/firefox/check.nix { inherit pkgs; };
|
firefox = import ./modules/firefox/check.nix { inherit pkgs; };
|
||||||
jellyfin = import ./modules/jellyfin/check.nix { inherit pkgs; };
|
jellyfin = import ./modules/jellyfin/check.nix { inherit pkgs; };
|
||||||
vr-keyboard = (import ./modules/vr-keyboard/build.nix {
|
vr-keyboard = import ./modules/vr-keyboard/check.nix { inherit pkgs; };
|
||||||
inherit pkgs;
|
|
||||||
dictionary = {
|
|
||||||
languages = map (l: l // { keepFrequentAbove = 4.0; }) [
|
|
||||||
{ language = "de"; hunspell = "de_DE"; words = 60000; frequencyOffset = 0.0; }
|
|
||||||
{ language = "en"; hunspell = "en_US"; words = 40000; frequencyOffset = -0.3; }
|
|
||||||
];
|
|
||||||
contractions = true; extraWords = [ ]; extraWordsFrequency = 5.0; extraWordFiles = [ ]; excludeWords = [ ];
|
|
||||||
};
|
|
||||||
}).checks;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
# nix flake init -t github:lhns/steam-frame-nix
|
# nix flake init -t github:lhns/steam-frame-nix
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
let
|
let
|
||||||
cfg = config.steamFrame.dashboard.windows;
|
cfg = config.steamFrame.dashboard.windows;
|
||||||
inherit (lib) mkOption types;
|
inherit (lib) mkOption types;
|
||||||
inherit (import ./lib { inherit pkgs; }) mkPatch;
|
inherit (import ./steam-ui-patches/lib { inherit pkgs; }) mkPatch;
|
||||||
|
|
||||||
# Stock grab distance ranges (meters), matched by the patch.
|
# Stock grab distance ranges (meters), matched by the patch.
|
||||||
stockDistance = {
|
stockDistance = {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// dashboard-windows: resize and grab-distance limits of SteamVR dashboard
|
// dashboard-windows: resize and grab-distance limits of SteamVR dashboard
|
||||||
// windows (Steam, app windows, overlays, theater screen, the dashboard).
|
// windows (Steam, app windows, overlays, theater screen, the dashboard).
|
||||||
// Target: SteamVR dashboard (vrwebhelper, DevTools 127.0.0.1:8087, title
|
// Target: SteamVR dashboard (vrwebhelper, DevTools 127.0.0.1:8087, title
|
||||||
// "systemui"). mkPatch patch (see lib/default.nix); opts (null = stock):
|
// "systemui"). mkPatch patch (see steam-ui-patches/lib/default.nix); opts
|
||||||
|
// (null = stock):
|
||||||
// { maxScale: 4.0,
|
// { maxScale: 4.0,
|
||||||
// distance: { world: {min: null, max: 10}, theater: {...}, dashboard: {...} } }
|
// distance: { world: {min: null, max: 10}, theater: {...}, dashboard: {...} } }
|
||||||
//
|
//
|
||||||
@@ -16,8 +17,9 @@
|
|||||||
// dashboard "grab-transform" 0.3 / 4 the dashboard itself
|
// dashboard "grab-transform" 0.3 / 4 the dashboard itself
|
||||||
// (the keyboard's grab-transform, 0.2 / 1, is left alone).
|
// (the keyboard's grab-transform, 0.2 / 1, is left alone).
|
||||||
// They are literals in systemui's bundle, so SendMessage is hooked on the
|
// They are literals in systemui's bundle, so SendMessage is hooked on the
|
||||||
// mailbox prototype (lib/hooks.js, shared with window-curvature) and outgoing
|
// mailbox prototype (steam-ui-patches/lib/hooks.js, shared with
|
||||||
// scene graphs (fresh objects per update) are edited in place. Grab nodes are
|
// window-curvature) and outgoing scene graphs (fresh objects per update) are
|
||||||
|
// edited in place. Grab nodes are
|
||||||
// matched by type plus exact stock values, so if SteamVR changes them the
|
// matched by type plus exact stock values, so if SteamVR changes them the
|
||||||
// distance rewrite becomes a no-op. A scene-graph resend (systemui's debounced
|
// distance rewrite becomes a no-op. A scene-graph resend (systemui's debounced
|
||||||
// scheduler) applies new limits at once.
|
// scheduler) applies new limits at once.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
let
|
let
|
||||||
cfg = config.steamFrame.dashboard.frameControls;
|
cfg = config.steamFrame.dashboard.frameControls;
|
||||||
inherit (lib) mkOption types;
|
inherit (lib) mkOption types;
|
||||||
inherit (import ./lib { inherit pkgs; }) mkPatch;
|
inherit (import ./steam-ui-patches/lib { inherit pkgs; }) mkPatch;
|
||||||
|
|
||||||
# Control names -> SteamVR action icon enums (the patch keys controls by icon).
|
# Control names -> SteamVR action icon enums (the patch keys controls by icon).
|
||||||
names = [ "keyboard" "float" "dashboard" "theater" "dockLeft" "dockRight" "close" "curvature" ];
|
names = [ "keyboard" "float" "dashboard" "theater" "dockLeft" "dockRight" "close" "curvature" ];
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
// opts.floatInTheater gives theater windows the "Float" control back.
|
// opts.floatInTheater gives theater windows the "Float" control back.
|
||||||
//
|
//
|
||||||
// Target: SteamVR dashboard (vrwebhelper, DevTools 127.0.0.1:8087, title
|
// Target: SteamVR dashboard (vrwebhelper, DevTools 127.0.0.1:8087, title
|
||||||
// "systemui"). mkPatch patch (see lib/default.nix); opts: { inBar, inMenu,
|
// "systemui"). mkPatch patch (see steam-ui-patches/lib/default.nix); opts:
|
||||||
// longPressMs, floatInTheater }.
|
// { inBar, inMenu, longPressMs, floatInTheater }.
|
||||||
//
|
//
|
||||||
// Stock: each Frame renders <FrameControlsItem params={type, action_id}>
|
// Stock: each Frame renders <FrameControlsItem params={type, action_id}>
|
||||||
// (2 = action, 1 = spacer); <FrameControls> passes the lists to the MobX
|
// (2 = action, 1 = spacer); <FrameControls> passes the lists to the MobX
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.steamFrame.launcherMenu;
|
cfg = config.steamFrame.launcherMenu;
|
||||||
inherit (import ./lib { inherit pkgs; }) mkPatch;
|
inherit (import ./steam-ui-patches/lib { inherit pkgs; }) mkPatch;
|
||||||
sharedJSContext = { title = "SharedJSContext"; };
|
sharedJSContext = { title = "SharedJSContext"; };
|
||||||
|
|
||||||
iconSuggest = pkgs.writeShellApplication {
|
iconSuggest = pkgs.writeShellApplication {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// grid: shows the programs section of the VR dashboard's "+" menu
|
// grid: shows the programs section of the VR dashboard's "+" menu
|
||||||
// (#VRDashboard_LaunchNonSteamApp) as a grid of tiles (icon, name below).
|
// (#VRDashboard_LaunchNonSteamApp) as a grid of tiles (icon, name below).
|
||||||
// mkPatch patch (see lib/default.nix); opts: { columns, maxRows } (maxRows
|
// mkPatch patch (see steam-ui-patches/lib/default.nix); opts: { columns,
|
||||||
// null: the stock 600 px max height).
|
// maxRows } (maxRows null: the stock 600 px max height).
|
||||||
//
|
//
|
||||||
// Pure restyling in SharedJSContext (the bar popups share its realm): Steam's
|
// Pure restyling in SharedJSContext (the bar popups share its realm): Steam's
|
||||||
// items and handlers stay, so the other launcher-menu patches keep working. A
|
// items and handlers stay, so the other launcher-menu patches keep working. A
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
// maxRows caps the scroller, the inner panel is what scrolls.
|
// maxRows caps the scroller, the inner panel is what scrolls.
|
||||||
// Gamepad navigation follows the computed display: grid. The pinned-desktop
|
// Gamepad navigation follows the computed display: grid. The pinned-desktop
|
||||||
// row (.sfui-pinned-desktop) is made slim and centred.
|
// row (.sfui-pinned-desktop) is made slim and centred.
|
||||||
// Anchors: "launcher-menu-grid" in lib/signatures.json.
|
// Anchors: "launcher-menu-grid" in steam-ui-patches/lib/signatures.json.
|
||||||
// unpatch.js (or a new VERSION/options) calls __sfuiLauncherGrid.stop().
|
// unpatch.js (or a new VERSION/options) calls __sfuiLauncherGrid.stop().
|
||||||
((find, sigs, opts) => {
|
((find, sigs, opts) => {
|
||||||
const NAME = 'launcher-menu-grid';
|
const NAME = 'launcher-menu-grid';
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// launch: what activating a program in the VR dashboard's "+" menu does.
|
// launch: what activating a program in the VR dashboard's "+" menu does.
|
||||||
// mkPatch patch (see lib/default.nix); opts: { closeOnLaunch, launchDebounceSeconds }.
|
// mkPatch patch (see steam-ui-patches/lib/default.nix); opts: { closeOnLaunch,
|
||||||
|
// launchDebounceSeconds }.
|
||||||
//
|
//
|
||||||
// Stock, an item only calls SteamClient.Apps.LaunchNonSteamApp(cmdline) and
|
// Stock, an item only calls SteamClient.Apps.LaunchNonSteamApp(cmdline) and
|
||||||
// the popup stays open until the new window appears, so users click twice.
|
// the popup stays open until the new window appears, so users click twice.
|
||||||
@@ -12,7 +13,7 @@
|
|||||||
// popup handle (closePopup(), as stock does after adding a desktop window),
|
// popup handle (closePopup(), as stock does after adding a desktop window),
|
||||||
// found through React props: a .VRDashboardBarSmallButton whose fiber
|
// found through React props: a .VRDashboardBarSmallButton whose fiber
|
||||||
// ancestors have refBarPopopHandle and, above, allowLaunchProgram
|
// ancestors have refBarPopopHandle and, above, allowLaunchProgram
|
||||||
// ("launcher-menu-launch" in lib/signatures.json).
|
// ("launcher-menu-launch" in steam-ui-patches/lib/signatures.json).
|
||||||
// Covers every activation path (pointer, controller, pinned Desktop copy).
|
// Covers every activation path (pointer, controller, pinned Desktop copy).
|
||||||
// Original kept as __sfuiOrig for unpatch.js; bump VERSION on changes.
|
// Original kept as __sfuiOrig for unpatch.js; bump VERSION on changes.
|
||||||
((find, sigs, opts) => {
|
((find, sigs, opts) => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// pinned-desktop: pins "Desktop" (the nested Plasma session) above or below
|
// pinned-desktop: pins "Desktop" (the nested Plasma session) above or below
|
||||||
// the scrolling list of the VR dashboard's "+" menu.
|
// the scrolling list of the VR dashboard's "+" menu.
|
||||||
// mkPatch patch (see lib/default.nix); opts: { position: "top" | "bottom" }.
|
// mkPatch patch (see steam-ui-patches/lib/default.nix); opts: { position:
|
||||||
|
// "top" | "bottom" }.
|
||||||
//
|
//
|
||||||
// DOM patch in SharedJSContext: a timer attaches a MutationObserver to every
|
// DOM patch in SharedJSContext: a timer attaches a MutationObserver to every
|
||||||
// bar popup document (g_PopupManager). The stock Desktop item (fiber list key
|
// bar popup document (g_PopupManager). The stock Desktop item (fiber list key
|
||||||
@@ -9,7 +10,8 @@
|
|||||||
// region. Clicking the clone clicks the hidden item, so Steam's own handler
|
// region. Clicking the clone clicks the hidden item, so Steam's own handler
|
||||||
// runs. The list container is a flex column with a max-height, so the scroll
|
// runs. The list container is a flex column with a max-height, so the scroll
|
||||||
// region shrinks and the menu keeps its size.
|
// region shrinks and the menu keeps its size.
|
||||||
// Anchors: "launcher-menu-pinned-desktop" in lib/signatures.json.
|
// Anchors: "launcher-menu-pinned-desktop" in
|
||||||
|
// steam-ui-patches/lib/signatures.json.
|
||||||
// unpatch.js (or a new VERSION/position) calls __sfuiPinnedDesktop.stop().
|
// unpatch.js (or a new VERSION/position) calls __sfuiPinnedDesktop.stop().
|
||||||
((find, sigs, opts) => {
|
((find, sigs, opts) => {
|
||||||
const NAME = 'launcher-menu-pinned-desktop';
|
const NAME = 'launcher-menu-pinned-desktop';
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// show-all: the VR dashboard's "+" menu lists all programs without Steam's
|
// show-all: the VR dashboard's "+" menu lists all programs without Steam's
|
||||||
// Developer Mode.
|
// Developer Mode.
|
||||||
// mkPatch patch (see lib/default.nix); no opts; sigs: "launcher-menu-show-all"
|
// mkPatch patch (see steam-ui-patches/lib/default.nix); no opts; sigs:
|
||||||
// (the module exporting the list, plus a check-only anchor for the filter).
|
// "launcher-menu-show-all" (the module exporting the list, plus a check-only
|
||||||
|
// anchor for the filter).
|
||||||
//
|
//
|
||||||
// Steam hides some executables always (steam, vrurlhandler) and, without
|
// Steam hides some executables always (steam, vrurlhandler) and, without
|
||||||
// Developer Mode, a second list (firewall-config, vlc, dolphin, cmake-gui,
|
// Developer Mode, a second list (firewall-config, vlc, dolphin, cmake-gui,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.steamFrame.dashboard.steamCloseButton;
|
cfg = config.steamFrame.dashboard.steamCloseButton;
|
||||||
inherit (import ./lib { inherit pkgs; }) mkPatch;
|
inherit (import ./steam-ui-patches/lib { inherit pkgs; }) mkPatch;
|
||||||
in {
|
in {
|
||||||
imports = [ ./cleanup.nix ./steam-ui-patches.nix ./steamvr-debugger.nix ];
|
imports = [ ./cleanup.nix ./steam-ui-patches.nix ./steamvr-debugger.nix ];
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
# instance reverts removed patches) and is stopped once the list is empty.
|
# instance reverts removed patches) and is stopped once the list is empty.
|
||||||
# Patches with `state = true` get a persistent JSON value (injector.mjs:
|
# Patches with `state = true` get a persistent JSON value (injector.mjs:
|
||||||
# "Persistent state"), kept when the patch goes and removed by
|
# "Persistent state"), kept when the patch goes and removed by
|
||||||
# steam-frame-nix-cleanup --all. Patch calling convention (mkPatch):
|
# steam-frame-nix-cleanup --all. Patch calling convention (mkPatch) and the
|
||||||
# lib/default.nix.
|
# finder library: steam-ui-patches/lib/default.nix.
|
||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.steamFrame.uiPatches;
|
cfg = config.steamFrame.uiPatches;
|
||||||
@@ -97,12 +97,13 @@ in {
|
|||||||
options.steamFrame.uiPatches.lib = mkOption {
|
options.steamFrame.uiPatches.lib = mkOption {
|
||||||
type = types.attrsOf types.raw;
|
type = types.attrsOf types.raw;
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
default = import ./lib { inherit pkgs; };
|
default = import ./steam-ui-patches/lib { inherit pkgs; };
|
||||||
defaultText = lib.literalMD "the helpers of `modules/lib`";
|
defaultText = lib.literalMD "the helpers of `modules/steam-ui-patches/lib`";
|
||||||
description = ''
|
description = ''
|
||||||
Patch helpers from modules/lib/default.nix: `mkPatch { name, src,
|
Patch helpers from modules/steam-ui-patches/lib/default.nix:
|
||||||
signatures ? …, opts ? { }, extraArgs ? [ ] }` (calls `src` as
|
`mkPatch { name, src, signatures ? …, opts ? { }, extraArgs ? [ ] }`
|
||||||
`(find, sigs, opts, hooks, ...extraArgs) => …`; see there), plus
|
(calls `src` as `(find, sigs, opts, hooks, ...extraArgs) => …`; see
|
||||||
|
there), plus
|
||||||
`finders`, `hooks` (paths) and `signatures` (parsed signatures.json).
|
`finders`, `hooks` (paths) and `signatures` (parsed signatures.json).
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,3 +1,5 @@
|
|||||||
|
# steamFrame.keyboard.vr.extraKeys (user service steam-keyboard-patch; patch
|
||||||
|
# name "steam-keyboard-patch" in logs and signatures.json).
|
||||||
# Steam's VR keyboard can only send text (ControllerKeyboardSetKeyState throws
|
# Steam's VR keyboard can only send text (ControllerKeyboardSetKeyState throws
|
||||||
# "Unknown method" in VR), and its layouts are hardcoded.
|
# "Unknown method" in VR), and its layouts are hardcoded.
|
||||||
# - patch.js (injected over CEF DevTools, 127.0.0.1:8080) adds a bottom row
|
# - patch.js (injected over CEF DevTools, 127.0.0.1:8080) adds a bottom row
|
||||||
@@ -5,14 +7,15 @@
|
|||||||
# hands chords, Shift+arrows and characters Steam would turn into "1"
|
# hands chords, Shift+arrows and characters Steam would turn into "1"
|
||||||
# (non-ASCII, AltGr/dead keys) to the helper. AltGr + the key left of
|
# (non-ASCII, AltGr/dead keys) to the helper. AltGr + the key left of
|
||||||
# Backspace = Delete.
|
# Backspace = Delete.
|
||||||
# - helper.mjs keeps it injected, sends those keys with xdotool on :0 (an
|
# - xdotool-helper.mjs keeps it injected (its own injector, not the
|
||||||
|
# steam-ui-patches service), sends those keys with xdotool on :0 (an
|
||||||
# allowlist: no ASCII text, no Enter) and unpatches on stop.
|
# allowlist: no ASCII text, no Enter) and unpatches on stop.
|
||||||
# Tested with Steam client 1790377368 (UI build 11041156).
|
# Tested with Steam client 1790377368 (UI build 11041156).
|
||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
patch = (import ./lib { inherit pkgs; }).mkPatch {
|
patch = (import ./steam-ui-patches/lib { inherit pkgs; }).mkPatch {
|
||||||
name = "steam-keyboard-patch";
|
name = "steam-keyboard-patch";
|
||||||
src = ./steam-keyboard-patch/patch.js;
|
src = ./vr-keyboard-extra-keys/patch.js;
|
||||||
};
|
};
|
||||||
cfg = config.steamFrame.keyboard.vr.extraKeys;
|
cfg = config.steamFrame.keyboard.vr.extraKeys;
|
||||||
in {
|
in {
|
||||||
@@ -35,9 +38,9 @@ in {
|
|||||||
Service = {
|
Service = {
|
||||||
ExecStart = lib.escapeShellArgs [
|
ExecStart = lib.escapeShellArgs [
|
||||||
"${pkgs.nodejs}/bin/node"
|
"${pkgs.nodejs}/bin/node"
|
||||||
"${./steam-keyboard-patch/helper.mjs}"
|
"${./vr-keyboard-extra-keys/xdotool-helper.mjs}"
|
||||||
"${patch}"
|
"${patch}"
|
||||||
"${./steam-keyboard-patch/unpatch.js}"
|
"${./vr-keyboard-extra-keys/unpatch.js}"
|
||||||
"${pkgs.xdotool}/bin/xdotool"
|
"${pkgs.xdotool}/bin/xdotool"
|
||||||
];
|
];
|
||||||
Restart = "always";
|
Restart = "always";
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// Injected into Steam's SharedJSContext (CEF, 127.0.0.1:8080) by helper.mjs.
|
// Injected into Steam's SharedJSContext (CEF, 127.0.0.1:8080) by
|
||||||
|
// xdotool-helper.mjs.
|
||||||
// Extends Steam's VR keyboard for gamescope app windows:
|
// Extends Steam's VR keyboard for gamescope app windows:
|
||||||
// - bottom row: Esc Ctrl Alt [space] AltGr ← ↑ ↓ → Close, stable with Shift
|
// - bottom row: Esc Ctrl Alt [space] AltGr ← ↑ ↓ → Close, stable with Shift
|
||||||
// and AltGr; AltGr + arrows = Pos1/PgUp/PgDn/End
|
// and AltGr; AltGr + arrows = Pos1/PgUp/PgDn/End
|
||||||
@@ -16,11 +17,11 @@
|
|||||||
// - Enter types Return for app windows, even if a Steam search box had focus
|
// - Enter types Return for app windows, even if a Steam search box had focus
|
||||||
// before (Steam then labels it "Search" and closes the keyboard instead)
|
// before (Steam then labels it "Search" and closes the keyboard instead)
|
||||||
// Key output goes through the CDP binding window.__vrkbdKey("<op>:<arg>"),
|
// Key output goes through the CDP binding window.__vrkbdKey("<op>:<arg>"),
|
||||||
// run by helper.mjs with xdotool on :0. Replaced functions keep their
|
// run by xdotool-helper.mjs with xdotool on :0. Replaced functions keep their
|
||||||
// original as __vrkbdOrig; window.__vrkbdRefs etc. are for unpatch.js.
|
// original as __vrkbdOrig; window.__vrkbdRefs etc. are for unpatch.js.
|
||||||
//
|
//
|
||||||
// mkPatch patch (see lib/default.nix); no options. On a signature mismatch
|
// mkPatch patch (see steam-ui-patches/lib/default.nix); no options. On a
|
||||||
// it returns an error and changes nothing. Idempotent.
|
// signature mismatch it returns an error and changes nothing. Idempotent.
|
||||||
((find, sigs) => {
|
((find, sigs) => {
|
||||||
const VERSION = 22;
|
const VERSION = 22;
|
||||||
const send = (msg) => window.__vrkbdKey && window.__vrkbdKey(msg);
|
const send = (msg) => window.__vrkbdKey && window.__vrkbdKey(msg);
|
||||||
@@ -143,7 +144,7 @@
|
|||||||
// All text for gamescope windows ends in ControllerKeyboardSendText, which
|
// All text for gamescope windows ends in ControllerKeyboardSendText, which
|
||||||
// only maps plain ASCII on the base/shift levels (else "1"). Those
|
// only maps plain ASCII on the base/shift levels (else "1"). Those
|
||||||
// characters go to the helper, in order; the rest passes through.
|
// characters go to the helper, in order; the rest passes through.
|
||||||
// (helper.mjs checks the same set.)
|
// (xdotool-helper.mjs checks the same set.)
|
||||||
const viaHelper = (c) => c.codePointAt(0) > 127 || '|@{[]}\\~^`'.includes(c);
|
const viaHelper = (c) => c.codePointAt(0) > 127 || '|@{[]}\\~^`'.includes(c);
|
||||||
wrap(SteamClient.Input, 'ControllerKeyboardSendText', (orig) => function (text, ...rest) {
|
wrap(SteamClient.Input, 'ControllerKeyboardSendText', (orig) => function (text, ...rest) {
|
||||||
if (typeof text !== 'string') return orig.call(this, text, ...rest);
|
if (typeof text !== 'string') return orig.call(this, text, ...rest);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Reverts patch.js in Steam's SharedJSContext (run by helper.mjs on stop).
|
// Reverts patch.js in Steam's SharedJSContext (run by xdotool-helper.mjs on
|
||||||
// Patched functions keep their original as __vrkbdOrig; objects Steam doesn't
|
// stop). Patched functions keep their original as __vrkbdOrig; objects Steam
|
||||||
// expose are in window.__vrkbdRefs/__vrkbdLayouts/__vrkbdProto/__vrkbdInst;
|
// doesn't expose are in window.__vrkbdRefs/__vrkbdLayouts/__vrkbdProto/__vrkbdInst;
|
||||||
// the Delete-repeat listeners of a keyboard window in its __vrkbdDelDetach.
|
// the Delete-repeat listeners of a keyboard window in its __vrkbdDelDetach.
|
||||||
// Safe when not patched.
|
// Safe when not patched.
|
||||||
(() => {
|
(() => {
|
||||||
+6
-4
@@ -1,9 +1,11 @@
|
|||||||
// helper.mjs: injects the patch (patch.js wrapped with the finder library by
|
// xdotool-helper.mjs (user service steam-keyboard-patch): injects the patch
|
||||||
// lib/default.nix mkPatch) into Steam's UI via CEF DevTools and
|
// (patch.js wrapped with the finder library by mkPatch,
|
||||||
// performs the key requests of the patched VR keyboard with xdotool on :0.
|
// steam-ui-patches/lib/default.nix) into Steam's UI via CEF DevTools itself
|
||||||
|
// (not through the steam-ui-patches injector) and performs the key requests
|
||||||
|
// of the patched VR keyboard with xdotool on :0.
|
||||||
// On SIGTERM/SIGINT it reverts the patch (unpatch.js), so stopping the service
|
// On SIGTERM/SIGINT it reverts the patch (unpatch.js), so stopping the service
|
||||||
// restores Steam's stock keyboard without restarting Steam.
|
// restores Steam's stock keyboard without restarting Steam.
|
||||||
// usage: node helper.mjs <patch.js> <unpatch.js> [xdotool]
|
// usage: node xdotool-helper.mjs <patch.js> <unpatch.js> [xdotool]
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import { execFile } from 'node:child_process';
|
import { execFile } from 'node:child_process';
|
||||||
|
|
||||||
+20
-17
@@ -1,22 +1,24 @@
|
|||||||
# Swipe typing, suggestions and a Backspace drag for Steam's VR keyboard.
|
# Swipe typing, suggestions and a Backspace drag for Steam's VR keyboard
|
||||||
# - vr-keyboard/patch.js (Steam UI, 8080): swipe gestures (decoder.js), a
|
# (steamFrame.keyboard.vr; the extra keys are vr-keyboard-extra-keys.nix).
|
||||||
# model of what the keyboard typed (textmodel.js), suggestions (swipe
|
# - vr-keyboard/patch.js (Steam UI, 8080): swipe gestures (swipe-decoder.js),
|
||||||
|
# a model of what the keyboard typed (textmodel.js), suggestions (swipe
|
||||||
# alternatives, corrections and completions, corrector.js; never changing
|
# alternatives, corrections and completions, corrector.js; never changing
|
||||||
# text by themselves), Backspace drag (left: delete with a detent at word
|
# text by themselves), Backspace drag (left: delete with a detent at word
|
||||||
# borders; right: retype), haptic ticks.
|
# borders; right: retype), haptic ticks.
|
||||||
# - The suggestion strip: over the number row ("inside"), or a SteamVR
|
# - The suggestion strip: over the number row ("inside"), or a SteamVR
|
||||||
# dashboard panel above/below the keyboard (panel.js in systemui, 8087),
|
# dashboard panel above/below the keyboard (suggestions-panel/patch.js in
|
||||||
# fed by relay.mjs (user service vr-keyboard-relay).
|
# systemui, 8087), fed by suggestions-panel/relay.mjs (user service
|
||||||
# - Dictionary: built by gen-dict.py from wordfreq frequency lists, filtered
|
# vr-keyboard-relay).
|
||||||
# and cased by Hunspell (nixpkgs' hunspellDicts).
|
# - Dictionary: dictionary.nix (gen-dict.py) from wordfreq frequency lists,
|
||||||
|
# filtered and cased by Hunspell (nixpkgs' hunspellDicts).
|
||||||
# Works alongside keyboard.vr.extraKeys (both hook the same keyboard; this
|
# Works alongside keyboard.vr.extraKeys (both hook the same keyboard; this
|
||||||
# one resets its text model on the extra keys it can't follow). Tests: the
|
# one resets its text model on the extra keys it can't follow). Tests:
|
||||||
# checks below (also flake check `vr-keyboard`).
|
# check.nix (built before the patch; also flake check `vr-keyboard`).
|
||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.steamFrame.keyboard.vr;
|
cfg = config.steamFrame.keyboard.vr;
|
||||||
inherit (lib) mkOption mkEnableOption types;
|
inherit (lib) mkOption mkEnableOption types;
|
||||||
uiLib = import ./lib { inherit pkgs; };
|
uiLib = import ./steam-ui-patches/lib { inherit pkgs; };
|
||||||
|
|
||||||
on = cfg.enable && (cfg.swipe.enable || cfg.autocorrect.enable || cfg.completions.enable || cfg.backspaceDrag.enable);
|
on = cfg.enable && (cfg.swipe.enable || cfg.autocorrect.enable || cfg.completions.enable || cfg.backspaceDrag.enable);
|
||||||
panel = on && cfg.suggestions.position != "inside";
|
panel = on && cfg.suggestions.position != "inside";
|
||||||
@@ -34,9 +36,10 @@ let
|
|||||||
++ [ { language = "en"; hunspell = "en_US"; words = if layoutLanguage != null then 40000 else 60000;
|
++ [ { language = "en"; hunspell = "en_US"; words = if layoutLanguage != null then 40000 else 60000;
|
||||||
frequencyOffset = if layoutLanguage != null then -0.3 else 0.0; } ];
|
frequencyOffset = if layoutLanguage != null then -0.3 else 0.0; } ];
|
||||||
|
|
||||||
built = import ./vr-keyboard/build.nix { inherit pkgs; inherit (cfg) dictionary; };
|
dictionaryJs = import ./vr-keyboard/dictionary.nix { inherit pkgs; inherit (cfg) dictionary; };
|
||||||
|
checks = import ./vr-keyboard/check.nix { inherit pkgs; inherit (cfg) dictionary; };
|
||||||
patch = pkgs.runCommand "vr-keyboard.js" { nativeBuildInputs = [ pkgs.nodejs ]; } ''
|
patch = pkgs.runCommand "vr-keyboard.js" { nativeBuildInputs = [ pkgs.nodejs ]; } ''
|
||||||
: ${built.checks}
|
: ${checks}
|
||||||
cp ${uiLib.mkPatch {
|
cp ${uiLib.mkPatch {
|
||||||
name = "vr-keyboard";
|
name = "vr-keyboard";
|
||||||
src = ./vr-keyboard/patch.js;
|
src = ./vr-keyboard/patch.js;
|
||||||
@@ -52,7 +55,7 @@ let
|
|||||||
inherit (cfg.backspaceDrag) wordDetentPixels;
|
inherit (cfg.backspaceDrag) wordDetentPixels;
|
||||||
inherit (cfg) haptics;
|
inherit (cfg) haptics;
|
||||||
};
|
};
|
||||||
extraArgs = [ ./vr-keyboard/decoder.js ./vr-keyboard/textmodel.js ./vr-keyboard/corrector.js built.dictionary ];
|
extraArgs = [ ./vr-keyboard/swipe-decoder.js ./vr-keyboard/textmodel.js ./vr-keyboard/corrector.js dictionaryJs ];
|
||||||
}} $out
|
}} $out
|
||||||
node --check $out
|
node --check $out
|
||||||
'';
|
'';
|
||||||
@@ -180,7 +183,7 @@ in {
|
|||||||
checks = mkOption {
|
checks = mkOption {
|
||||||
type = types.package;
|
type = types.package;
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
default = built.checks;
|
default = checks;
|
||||||
defaultText = lib.literalMD "the tests, built with the configured dictionary";
|
defaultText = lib.literalMD "the tests, built with the configured dictionary";
|
||||||
description = "The tests (also built with the patch).";
|
description = "The tests (also built with the patch).";
|
||||||
};
|
};
|
||||||
@@ -202,15 +205,15 @@ in {
|
|||||||
name = "vr-keyboard-panel";
|
name = "vr-keyboard-panel";
|
||||||
endpoint = "http://127.0.0.1:8087";
|
endpoint = "http://127.0.0.1:8087";
|
||||||
target.title = "systemui";
|
target.title = "systemui";
|
||||||
patch = pkgs.writeText "vr-keyboard-panel.js" "(${builtins.readFile ./vr-keyboard/panel.js})()";
|
patch = pkgs.writeText "vr-keyboard-panel.js" "(${builtins.readFile ./vr-keyboard/suggestions-panel/patch.js})()";
|
||||||
unpatch = ./vr-keyboard/unpatch-panel.js;
|
unpatch = ./vr-keyboard/suggestions-panel/unpatch.js;
|
||||||
};
|
};
|
||||||
steamFrame.session.services.${if panel then "restart" else "stop"} = [ "vr-keyboard-relay.service" ];
|
steamFrame.session.services.${if panel then "restart" else "stop"} = [ "vr-keyboard-relay.service" ];
|
||||||
}
|
}
|
||||||
(lib.mkIf panel {
|
(lib.mkIf panel {
|
||||||
systemd.user.services.vr-keyboard-relay = {
|
systemd.user.services.vr-keyboard-relay = {
|
||||||
Unit.Description = "Relay of the VR keyboard's suggestion strip (Steam UI <-> SteamVR dashboard)";
|
Unit.Description = "Relay of the VR keyboard's suggestion strip (Steam UI <-> SteamVR dashboard)";
|
||||||
Service = { ExecStart = "${pkgs.nodejs}/bin/node ${./vr-keyboard/relay.mjs}"; Restart = "always"; RestartSec = 5; };
|
Service = { ExecStart = "${pkgs.nodejs}/bin/node ${./vr-keyboard/suggestions-panel/relay.mjs}"; Restart = "always"; RestartSec = 5; };
|
||||||
Install.WantedBy = [ "default.target" ];
|
Install.WantedBy = [ "default.target" ];
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
# Build-time parts of the VR keyboard: the dictionary and the tests. Used by
|
|
||||||
# vr-keyboard.nix and by the flake's checks.<system>.vr-keyboard.
|
|
||||||
{ pkgs, dictionary }:
|
|
||||||
let
|
|
||||||
inherit (pkgs) lib;
|
|
||||||
d = dictionary;
|
|
||||||
words = pkgs.runCommand "vr-keyboard-dictionary.js" { } ''
|
|
||||||
${pkgs.python3.withPackages (ps: [ ps.wordfreq ])}/bin/python3 ${./gen-dict.py} $out ${pkgs.hunspell}/bin/hunspell \
|
|
||||||
${pkgs.writeText "vr-keyboard-dictionary.json" (builtins.toJSON {
|
|
||||||
languages = map (l: {
|
|
||||||
inherit (l) words keepFrequentAbove;
|
|
||||||
lang = l.language;
|
|
||||||
offset = l.frequencyOffset;
|
|
||||||
dict = if l.hunspell == null then null else "${pkgs.hunspellDicts.${l.hunspell}}/share/hunspell/${l.hunspell}";
|
|
||||||
}) d.languages;
|
|
||||||
extra = map (w: [ w d.extraWordsFrequency ]) d.extraWords;
|
|
||||||
extraFiles = map toString d.extraWordFiles;
|
|
||||||
extraZipf = d.extraWordsFrequency;
|
|
||||||
exclude = d.excludeWords;
|
|
||||||
inherit (d) contractions;
|
|
||||||
})}
|
|
||||||
'';
|
|
||||||
# The accuracy thresholds (German keyboard geometry, German and English
|
|
||||||
# words) fail the build only for German + English dictionaries.
|
|
||||||
strict = lib.sort lib.lessThan (map (l: l.language) d.languages) == [ "de" "en" ];
|
|
||||||
in {
|
|
||||||
dictionary = words;
|
|
||||||
checks = pkgs.runCommand "vr-keyboard-checks" { nativeBuildInputs = [ pkgs.nodejs ]; } ''
|
|
||||||
set -o pipefail
|
|
||||||
node ${./tests/textmodel.test.mjs} ${./textmodel.js}
|
|
||||||
node ${./tests/corrector.test.mjs} ${./corrector.js} ${./decoder.js} ${words} ${lib.optionalString (!strict) "|| echo warning: corrector test failed"}
|
|
||||||
node ${./tests/decoder.test.mjs} ${./decoder.js} ${words} 100 1 | tee $out ${lib.optionalString (!strict) "|| true"}
|
|
||||||
node -e '
|
|
||||||
const bad = require("fs").readFileSync(process.argv[1], "utf8").split("\n")
|
|
||||||
.filter((l) => /sigma 0.25/.test(l) && +l.match(/top-3 ([\d.]+)%/)[1] < 85);
|
|
||||||
if (bad.length) { console.error("decoder below 85 % top-3:", bad); process.exit(process.argv[2] === "strict" ? 1 : 0); }' \
|
|
||||||
$out ${if strict then "strict" else "warn"}
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Tests of the VR keyboard (flake check `vr-keyboard`, also built before the
|
||||||
|
# patch by vr-keyboard.nix with the configured dictionary): text model,
|
||||||
|
# corrector and swipe-decoder accuracy (tests/*.test.mjs). The accuracy
|
||||||
|
# thresholds (German keyboard geometry, German and English words) fail the
|
||||||
|
# build only for German + English dictionaries, the default here.
|
||||||
|
{ pkgs
|
||||||
|
, dictionary ? {
|
||||||
|
languages = map (l: l // { keepFrequentAbove = 4.0; }) [
|
||||||
|
{ language = "de"; hunspell = "de_DE"; words = 60000; frequencyOffset = 0.0; }
|
||||||
|
{ language = "en"; hunspell = "en_US"; words = 40000; frequencyOffset = -0.3; }
|
||||||
|
];
|
||||||
|
contractions = true; extraWords = [ ]; extraWordsFrequency = 5.0; extraWordFiles = [ ]; excludeWords = [ ];
|
||||||
|
}
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (pkgs) lib;
|
||||||
|
words = import ./dictionary.nix { inherit pkgs dictionary; };
|
||||||
|
strict = lib.sort lib.lessThan (map (l: l.language) dictionary.languages) == [ "de" "en" ];
|
||||||
|
in
|
||||||
|
pkgs.runCommand "vr-keyboard-checks" { nativeBuildInputs = [ pkgs.nodejs ]; } ''
|
||||||
|
set -o pipefail
|
||||||
|
node ${./tests/textmodel.test.mjs} ${./textmodel.js}
|
||||||
|
node ${./tests/corrector.test.mjs} ${./corrector.js} ${./swipe-decoder.js} ${words} ${lib.optionalString (!strict) "|| echo warning: corrector test failed"}
|
||||||
|
node ${./tests/swipe-decoder.test.mjs} ${./swipe-decoder.js} ${words} 100 1 | tee $out ${lib.optionalString (!strict) "|| true"}
|
||||||
|
node -e '
|
||||||
|
const bad = require("fs").readFileSync(process.argv[1], "utf8").split("\n")
|
||||||
|
.filter((l) => /sigma 0.25/.test(l) && +l.match(/top-3 ([\d.]+)%/)[1] < 85);
|
||||||
|
if (bad.length) { console.error("decoder below 85 % top-3:", bad); process.exit(process.argv[2] === "strict" ? 1 : 0); }' \
|
||||||
|
$out ${if strict then "strict" else "warn"}
|
||||||
|
''
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# The VR keyboard's dictionary (vr-keyboard-dictionary.js, passed to patch.js;
|
||||||
|
# also used by check.nix), built by gen-dict.py from wordfreq frequency lists,
|
||||||
|
# filtered and cased by Hunspell. `dictionary`: the
|
||||||
|
# steamFrame.keyboard.vr.dictionary options.
|
||||||
|
{ pkgs, dictionary }:
|
||||||
|
let
|
||||||
|
d = dictionary;
|
||||||
|
in
|
||||||
|
pkgs.runCommand "vr-keyboard-dictionary.js" { } ''
|
||||||
|
${pkgs.python3.withPackages (ps: [ ps.wordfreq ])}/bin/python3 ${./gen-dict.py} $out ${pkgs.hunspell}/bin/hunspell \
|
||||||
|
${pkgs.writeText "vr-keyboard-dictionary.json" (builtins.toJSON {
|
||||||
|
languages = map (l: {
|
||||||
|
inherit (l) words keepFrequentAbove;
|
||||||
|
lang = l.language;
|
||||||
|
offset = l.frequencyOffset;
|
||||||
|
dict = if l.hunspell == null then null else "${pkgs.hunspellDicts.${l.hunspell}}/share/hunspell/${l.hunspell}";
|
||||||
|
}) d.languages;
|
||||||
|
extra = map (w: [ w d.extraWordsFrequency ]) d.extraWords;
|
||||||
|
extraFiles = map toString d.extraWordFiles;
|
||||||
|
extraZipf = d.extraWordsFrequency;
|
||||||
|
exclude = d.excludeWords;
|
||||||
|
inherit (d) contractions;
|
||||||
|
})}
|
||||||
|
''
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
// Gestures and suggestions for Steam's VR keyboard (vr-keyboard.nix),
|
// Gestures and suggestions for Steam's VR keyboard (vr-keyboard.nix),
|
||||||
// injected into Steam's SharedJSContext (8080). The keyboard is a popup of
|
// injected into Steam's SharedJSContext (8080). The keyboard is a popup of
|
||||||
// that context ("SteamVR - Keyboard"); everything works on its document.
|
// that context ("SteamVR - Keyboard"); everything works on its document.
|
||||||
// mkPatch convention plus four more arguments: decoder.js, textmodel.js,
|
// mkPatch convention plus four more arguments: swipe-decoder.js,
|
||||||
// corrector.js and the dictionary text ("word<TAB>zipf*10\n...").
|
// textmodel.js, corrector.js and the dictionary text ("word<TAB>zipf*10\n...").
|
||||||
//
|
//
|
||||||
// - Swipe: the laser arrives as touch events; Steam types the key a touch
|
// - Swipe: the laser arrives as touch events; Steam types the key a touch
|
||||||
// started on at release. Once a press leaves its first key we drop that
|
// started on at release. Once a press leaves its first key we drop that
|
||||||
@@ -20,9 +20,9 @@
|
|||||||
// replace text only while the model proves the characters they replace
|
// replace text only while the model proves the characters they replace
|
||||||
// intact.
|
// intact.
|
||||||
// - Strip: in the page over the number row ("inside") or as a SteamVR panel
|
// - Strip: in the page over the number row ("inside") or as a SteamVR panel
|
||||||
// above/below the keyboard ("above"/"below", panel.js): state out via the
|
// above/below the keyboard ("above"/"below", suggestions-panel/patch.js):
|
||||||
// CDP binding __sfuiStripOut, picks back via
|
// state out via the CDP binding __sfuiStripOut, picks back via
|
||||||
// __sfuiSwipe.remote.pick(seq, index) (relay.mjs).
|
// __sfuiSwipe.remote.pick(seq, index) (suggestions-panel/relay.mjs).
|
||||||
// All Steam internals are checked first (sigs, instance members); if one is
|
// All Steam internals are checked first (sigs, instance members); if one is
|
||||||
// missing the keyboard stays stock. Only passive listeners; never blocks
|
// missing the keyboard stays stock. Only passive listeners; never blocks
|
||||||
// Steam's events. Debugging: __sfuiSwipeLog, __sfuiSwipePaths
|
// Steam's events. Debugging: __sfuiSwipeLog, __sfuiSwipePaths
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
const own = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
const own = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
||||||
|
|
||||||
// ---- trail and in-page strip -------------------------------------------------
|
// ---- trail and in-page strip -------------------------------------------------
|
||||||
const inPage = O.position === 'inside'; // else a SteamVR panel (panel.js)
|
const inPage = O.position === 'inside'; // else a SteamVR panel (suggestions-panel/)
|
||||||
const style = doc.createElement('style');
|
const style = doc.createElement('style');
|
||||||
style.textContent = `
|
style.textContent = `
|
||||||
#sfui-swipe-trail { position: fixed; left: 0; top: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 10000; }
|
#sfui-swipe-trail { position: fixed; left: 0; top: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 10000; }
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// The suggestion strip as a SteamVR dashboard panel above or below Steam's
|
// The suggestion strip as a SteamVR dashboard panel above or below Steam's
|
||||||
// VR keyboard (suggestions.position "above" / "below"), injected into
|
// VR keyboard (suggestions.position "above" / "below"; patch name
|
||||||
// SteamVR's systemui page (8087). State comes from the keyboard page through relay.mjs:
|
// "vr-keyboard-panel"), injected into SteamVR's systemui page (8087). State
|
||||||
|
// comes from the keyboard page (../patch.js) through relay.mjs:
|
||||||
// __sfuiKbdStrip.show({ seq, items, current, visible, style }); a click calls
|
// __sfuiKbdStrip.show({ seq, items, current, visible, style }); a click calls
|
||||||
// the binding __sfuiStripPick('{"seq":n,"index":i}').
|
// the binding __sfuiStripPick('{"seq":n,"index":i}').
|
||||||
//
|
//
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// relay.mjs: carries the suggestion strip between Steam's keyboard page
|
// relay.mjs (user service vr-keyboard-relay): carries the suggestion strip
|
||||||
// (SharedJSContext, 8080) and SteamVR's systemui page (8087) for
|
// between Steam's keyboard page (../patch.js in SharedJSContext, 8080) and
|
||||||
// suggestions.position "above" / "below". CDP bindings: the keyboard page calls
|
// SteamVR's systemui page (patch.js here, 8087) for suggestions.position
|
||||||
|
// "above" / "below". CDP bindings: the keyboard page calls
|
||||||
// __sfuiStripOut(state json) -> systemui __sfuiKbdStrip.show(state); the
|
// __sfuiStripOut(state json) -> systemui __sfuiKbdStrip.show(state); the
|
||||||
// panel calls __sfuiStripPick({ seq, index } json) -> keyboard page
|
// panel calls __sfuiStripPick({ seq, index } json) -> keyboard page
|
||||||
// __sfuiSwipe.remote.pick(seq, index). Only these two messages, validated.
|
// __sfuiSwipe.remote.pick(seq, index). Only these two messages, validated.
|
||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
// Reverts panel.js in SteamVR's systemui page: removes the strip panel, its
|
// Reverts suggestions-panel/patch.js in SteamVR's systemui page: removes the
|
||||||
// embedded-UV slot and stylesheet. Safe when nothing is patched.
|
// strip panel, its embedded-UV slot and stylesheet. Safe when nothing is patched.
|
||||||
(() => {
|
(() => {
|
||||||
const S = window.__sfuiKbdStrip;
|
const S = window.__sfuiKbdStrip;
|
||||||
if (!S) return 'not patched';
|
if (!S) return 'not patched';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// decoder.js: swipe path -> words. Evaluates to { VERSION, parseDict, layout,
|
// swipe-decoder.js: swipe path -> words. Evaluates to { VERSION, parseDict,
|
||||||
// decode, ... }. SHARK2-style template matching (Kristensson & Zhai 2004):
|
// layout, decode, ... }. SHARK2-style template matching (Kristensson & Zhai 2004):
|
||||||
// each word's ideal path runs through its key centres; candidates starting
|
// each word's ideal path runs through its key centres; candidates starting
|
||||||
// and ending near the path's ends, with every key near the path, are scored
|
// and ending near the path's ends, with every key near the path, are scored
|
||||||
// by point distances of the resampled paths (proportional and DTW), how
|
// by point distances of the resampled paths (proportional and DTW), how
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
const VERSION = 3;
|
const VERSION = 3;
|
||||||
const N = 32; // resample points
|
const N = 32; // resample points
|
||||||
const SKIP = new Set(["'", '-', '\u2019']);
|
const SKIP = new Set(["'", '-', '\u2019']);
|
||||||
// Cost weights and pruning limits (tuned with tests/decoder.test.mjs; distances in key widths).
|
// Cost weights and pruning limits (tuned with tests/swipe-decoder.test.mjs; distances in key widths).
|
||||||
const W = {
|
const W = {
|
||||||
loc: 4.4, // mean point distance, proportional alignment
|
loc: 4.4, // mean point distance, proportional alignment
|
||||||
dtw: 2.0, // mean point distance, dynamic time warping
|
dtw: 2.0, // mean point distance, dynamic time warping
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// Offline check of corrector.js on the built dictionary.
|
// Offline check of corrector.js on the built dictionary.
|
||||||
// usage: node corrector.test.mjs <corrector.js> <decoder.js> <dictionary.js>
|
// usage: node corrector.test.mjs <corrector.js> <swipe-decoder.js> <dictionary.js>
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -1,6 +1,7 @@
|
|||||||
// Offline check of decoder.js with synthetic swipes on Steam's German VR
|
// Offline check of swipe-decoder.js with synthetic swipes on Steam's German VR
|
||||||
// keyboard (key centres measured in the keyboard popup, 854x280 CSS px).
|
// keyboard (key centres measured in the keyboard popup, 854x280 CSS px).
|
||||||
// usage: node decoder.test.mjs <decoder.js> <dictionary.js> [words-per-sample] [seed]
|
// usage: node swipe-decoder.test.mjs <swipe-decoder.js> <dictionary.js>
|
||||||
|
// [words-per-sample] [seed]
|
||||||
// Each word's path goes through its key centres with random offsets (up to
|
// Each word's path goes through its key centres with random offsets (up to
|
||||||
// about half a key), cuts corners (spline) and jitters. Prints top-1/top-3
|
// about half a key), cuts corners (spline) and jitters. Prints top-1/top-3
|
||||||
// accuracy for frequent German and English words and a list of fixed words.
|
// accuracy for frequent German and English words and a list of fixed words.
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
let
|
let
|
||||||
cfg = config.steamFrame.dashboard.windowCurvature;
|
cfg = config.steamFrame.dashboard.windowCurvature;
|
||||||
inherit (lib) mkOption types;
|
inherit (lib) mkOption types;
|
||||||
inherit (import ./lib { inherit pkgs; }) mkPatch;
|
inherit (import ./steam-ui-patches/lib { inherit pkgs; }) mkPatch;
|
||||||
|
|
||||||
path = [ "steamFrame" "dashboard" "windowCurvature" ];
|
path = [ "steamFrame" "dashboard" "windowCurvature" ];
|
||||||
rename = from: to: lib.mkRenamedOptionModule (path ++ [ from ]) (path ++ [ to ]);
|
rename = from: to: lib.mkRenamedOptionModule (path ++ [ from ]) (path ++ [ to ]);
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
// curve); dragging up/down with the laser sets the curvature live.
|
// curve); dragging up/down with the laser sets the curvature live.
|
||||||
//
|
//
|
||||||
// Target: SteamVR dashboard (vrwebhelper, DevTools 127.0.0.1:8087, title
|
// Target: SteamVR dashboard (vrwebhelper, DevTools 127.0.0.1:8087, title
|
||||||
// "systemui"). mkPatch patch (see lib/default.nix); opts: { initial, max,
|
// "systemui"). mkPatch patch (see steam-ui-patches/lib/default.nix); opts:
|
||||||
// step, detentPixels, detentPoints, dragThresholdPixels, dragPixelsPerUnit,
|
// { initial, max, step, detentPixels, detentPoints, dragThresholdPixels,
|
||||||
// barDragPixelsPerUnit, haptics }.
|
// dragPixelsPerUnit, barDragPixelsPerUnit, haptics }.
|
||||||
//
|
//
|
||||||
// Stock curvature (frame.curvature, systemui's `curvature` component):
|
// Stock curvature (frame.curvature, systemui's `curvature` component):
|
||||||
// shouldCurve = m_bCurveOverride (set by ToggleCurvature(), cleared on dock
|
// shouldCurve = m_bCurveOverride (set by ToggleCurvature(), cleared on dock
|
||||||
@@ -18,10 +18,10 @@
|
|||||||
// 1.8 m) : 1000; its panels reference it as "curvature-origin-id" and
|
// 1.8 m) : 1000; its panels reference it as "curvature-origin-id" and
|
||||||
// vrcompositor bends them onto a cylinder around it (curvature = 1/radius).
|
// vrcompositor bends them onto a cylinder around it (curvature = 1/radius).
|
||||||
// Those MobX properties are non-configurable, so this patch hooks the mailbox
|
// Those MobX properties are non-configurable, so this patch hooks the mailbox
|
||||||
// SendMessage (lib/hooks.js, shared with dashboard-windows) and rewrites the
|
// SendMessage (steam-ui-patches/lib/hooks.js, shared with dashboard-windows)
|
||||||
// origin's z in outgoing scene graphs to stock / value (1 = stock, 2 = half
|
// and rewrites the origin's z in outgoing scene graphs to stock / value
|
||||||
// the radius, 0 = flat = stock toggle off). On/off stays the stock state, so
|
// (1 = stock, 2 = half the radius, 0 = flat = stock toggle off). On/off stays
|
||||||
// stock toggle and wheel always agree.
|
// the stock state, so stock toggle and wheel always agree.
|
||||||
//
|
//
|
||||||
// Values per window (key: first overlay key) in
|
// Values per window (key: first overlay key) in
|
||||||
// window.__sfuiWindowCurvatureState (schema 1: { values, log }); kept across
|
// window.__sfuiWindowCurvatureState (schema 1: { values, log }); kept across
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
// check-signatures.mjs: checks offline (no Steam, no browser) that every
|
// check-signatures.mjs: checks offline (no Steam, no browser) that every
|
||||||
// signature in modules/lib/signatures.json still matches the installed Steam /
|
// signature in modules/steam-ui-patches/lib/signatures.json still matches the
|
||||||
// SteamVR web UI bundles: module and export signatures exactly once,
|
// installed Steam / SteamVR web UI bundles: module and export signatures
|
||||||
// "expects" strings still present (warnings), "stylesheet" signatures exactly
|
// exactly once, "expects" strings still present (warnings), "stylesheet"
|
||||||
// one file of the bundle's "styles" directory. Run it after a Steam update:
|
// signatures exactly one file of the bundle's "styles" directory. Run it
|
||||||
|
// after a Steam update:
|
||||||
//
|
//
|
||||||
// nix shell nixpkgs#nodejs -c node scripts/check-signatures.mjs
|
// nix shell nixpkgs#nodejs -c node scripts/check-signatures.mjs
|
||||||
//
|
//
|
||||||
@@ -20,7 +21,8 @@
|
|||||||
// Modules come from webpack-modules.mjs (factory sources as the page's
|
// Modules come from webpack-modules.mjs (factory sources as the page's
|
||||||
// Function.prototype.toString sees them). For export signatures the matched
|
// Function.prototype.toString sees them). For export signatures the matched
|
||||||
// factory runs in a throwaway VM context where imports and unknown globals
|
// factory runs in a throwaway VM context where imports and unknown globals
|
||||||
// are inert stubs, and is matched with modules/lib/finders.js.
|
// are inert stubs, and is matched with
|
||||||
|
// modules/steam-ui-patches/lib/finders.js.
|
||||||
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
||||||
import { dirname, join, resolve } from 'node:path';
|
import { dirname, join, resolve } from 'node:path';
|
||||||
import { homedir } from 'node:os';
|
import { homedir } from 'node:os';
|
||||||
@@ -29,7 +31,7 @@ import vm from 'node:vm';
|
|||||||
import { loadBundles, pageFiles } from './webpack-modules.mjs';
|
import { loadBundles, pageFiles } from './webpack-modules.mjs';
|
||||||
|
|
||||||
const here = dirname(fileURLToPath(import.meta.url));
|
const here = dirname(fileURLToPath(import.meta.url));
|
||||||
const libDir = join(here, '..', 'modules', 'lib');
|
const libDir = join(here, '..', 'modules', 'steam-ui-patches', 'lib');
|
||||||
|
|
||||||
// ---- arguments -----------------------------------------------------------------
|
// ---- arguments -----------------------------------------------------------------
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// Replays recorded swipes (window.__sfuiSwipePaths of Steam's SharedJSContext,
|
// Replays recorded swipes (window.__sfuiSwipePaths of Steam's SharedJSContext,
|
||||||
// saved as JSON) through the decoder: top candidates and the cost terms of
|
// saved as JSON) through the decoder: top candidates and the cost terms of
|
||||||
// the expected word. The dictionary is the built vr-keyboard-dictionary.js.
|
// the expected word. The dictionary is the built vr-keyboard-dictionary.js.
|
||||||
// usage: node scripts/vr-keyboard-replay.mjs modules/vr-keyboard/decoder.js <dictionary.js> <paths.json> [word]
|
// usage: node scripts/vr-keyboard-replay.mjs modules/vr-keyboard/swipe-decoder.js
|
||||||
|
// <dictionary.js> <paths.json> [word]
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
const [, , decoderPath, dictPath, pathsPath, expected] = process.argv;
|
const [, , decoderPath, dictPath, pathsPath, expected] = process.argv;
|
||||||
const D = (0, eval)(readFileSync(decoderPath, 'utf8'));
|
const D = (0, eval)(readFileSync(decoderPath, 'utf8'));
|
||||||
|
|||||||
Reference in new issue
Block a user