Files
lhns--steam-frame-nix/modules/steam-ui-patches/lib/default.nix
T
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

58 lines
2.7 KiB
Nix

# Shared helpers of the runtime Steam UI patches:
# - finders.js: signature-based lookup of webpack modules/exports and fibers;
# - signatures.json: per-patch signatures, also read by
# scripts/check-signatures.mjs (run it after a Steam update);
# - hooks.js: one shared wrapper per hooked method (e.g. SendMessage);
# - mkPatch: builds the expression the injector evaluates.
#
# Patch convention: a patch file is a function expression
# (find, sigs, opts, hooks) => <status string or Promise of one>
# (trailing parameters may be omitted), called as
# (<patch>)(<finders.js>, <signatures>, <opts>, <hooks.js>)
# with find = lib/finders.js, sigs = its module signatures, opts = the JSON
# options from its module, hooks = lib/hooks.js. The injector re-evaluates it
# on every new JS context and periodically, so it must be idempotent: keep
# state on a window.__sfui* global (or on the wrapped function), return
# "unchanged" when already applied (not logged) and tear down/re-apply when
# its VERSION or options differ. Bump VERSION whenever the patch code changes.
# A matching unpatch.js (plain expression) reverts it when the service stops.
# State that must outlive the page (SteamVR restarts, reboots) goes through
# window.__sfuiStore (patch registered with state = true; injector.mjs).
# Patches needing none of the arguments may be plain expressions without
# mkPatch (e.g. launcher-menu/order).
{ pkgs }:
let
sigFile = builtins.fromJSON (builtins.readFile ./signatures.json);
in {
signatures = sigFile;
finders = ./finders.js;
hooks = ./hooks.js;
# name: file name (and default signatures entry); src: the patch file;
# signatures: `sigs` (default: signatures.json entry `name`, else none);
# opts: JSON-serialisable options; extraArgs: files or derivations with JS
# expressions passed as further arguments after hooks (built with
# runCommand, so they may be build outputs, e.g. generated data).
mkPatch = { name, src, signatures ? sigFile.patches.${name}.modules or { }, opts ? { }, extraArgs ? [ ] }:
if extraArgs == [ ] then
pkgs.writeText "${name}.js" ''
(${builtins.readFile src}
)(${builtins.readFile ./finders.js}
, ${builtins.toJSON signatures}
, ${builtins.toJSON opts}
, ${builtins.readFile ./hooks.js}
)
''
else
pkgs.runCommand "${name}.js" { } ''
{
echo '('; cat ${src}; echo ')('; cat ${./finders.js}
echo ','; echo ${pkgs.lib.escapeShellArg (builtins.toJSON signatures)}
echo ','; echo ${pkgs.lib.escapeShellArg (builtins.toJSON opts)}
echo ','; cat ${./hooks.js}
for f in ${pkgs.lib.escapeShellArgs (map (f: "${f}") extraArgs)}; do echo ','; cat "$f"; done
echo ')'
} > $out
'';
}