diff --git a/install.sh b/install.sh index c05a1c6..369e458 100755 --- a/install.sh +++ b/install.sh @@ -86,6 +86,10 @@ Commands: Show Nix, Home Manager and user service state, and what 'cleanup --all' would remove. + steamvr-debugger-arm + Internal (run before each SteamVR start by steamFrame.steamvrDebugger): + set VRWebHelper.DebuggerEnabled until SteamVR stops. + Options: --yes, -y Don't ask; answer yes to every question. --help, -h Show this help. @@ -577,6 +581,53 @@ debugger_remove_hook() { return 0 } +# Before each SteamVR start (the steamvr-webhelper-debugger oneshot of +# steamFrame.steamvrDebugger): remember the key's value in +# steamvr-debugger.armed, set it to true and make sure the runtime drop-in +# puts it back when SteamVR stops. A key that is already true (without +# .armed) is the user's own and never touched. +cmd_debugger_arm() { + local cur tmp + need_not_root + command -v jq >/dev/null 2>&1 || die "jq not found" + if [[ -f $DEBUGGER_MARKER_V1 ]]; then # marker of older versions: key was absent + mkdir -p "$SFN_STATE" + [[ -f $DEBUGGER_ARMED ]] || printf 'absent\n' >"$DEBUGGER_ARMED" + rm -f -- "$DEBUGGER_MARKER_V1" + info "migrated $DEBUGGER_MARKER_V1 to $DEBUGGER_ARMED" + fi + if [[ ! -f $DEBUGGER_ARMED ]]; then + if [[ -f $VRSETTINGS ]]; then + cur="$(jq -r 'if (.VRWebHelper | type) == "object" and (.VRWebHelper | has("DebuggerEnabled")) + then .VRWebHelper.DebuggerEnabled | tostring else "absent" end' "$VRSETTINGS")" \ + || die "can't read $VRSETTINGS" + else + cur=nofile + fi + case $cur in + true) info "VRWebHelper.DebuggerEnabled is already true (not set by steam-frame-nix): left as is"; return 0 ;; + false|absent|nofile) ;; + *) warn "VRWebHelper.DebuggerEnabled is $cur; left as is"; return 0 ;; + esac + mkdir -p "$SFN_STATE" + printf '%s\n' "$cur" >"$DEBUGGER_ARMED" + fi + if [[ ! -f $VRSETTINGS ]]; then + mkdir -p "${VRSETTINGS%/*}" + printf '{}\n' >"$VRSETTINGS" + fi + if [[ $(jq '.VRWebHelper.DebuggerEnabled' "$VRSETTINGS") != true ]]; then + tmp="$(mktemp "$VRSETTINGS.XXXXXX")" + jq --indent 3 '.VRWebHelper.DebuggerEnabled = true' "$VRSETTINGS" >"$tmp" || { rm -f "$tmp"; die "can't write $VRSETTINGS"; } + chmod --reference="$VRSETTINGS" "$tmp" + mv -f "$tmp" "$VRSETTINGS" + info "VRWebHelper.DebuggerEnabled set to true until SteamVR stops (before: $(<"$DEBUGGER_ARMED"))" + else + info "VRWebHelper.DebuggerEnabled already true (set by steam-frame-nix)" + fi + debugger_ensure_hook || true +} + clean_debugger() { # keep local keep=$1 prior='' state cur out if [[ -f $DEBUGGER_ARMED ]]; then @@ -1085,6 +1136,7 @@ main() { uninstall) cmd_uninstall "$@" ;; status) cmd_status "$@" ;; cleanup) cmd_cleanup "$@" ;; + steamvr-debugger-arm) cmd_debugger_arm "$@" ;; -h|--help|help) usage ;; '') usage >&2; exit 2 ;; *) printf 'unknown command: %s\n\n' "$cmd" >&2; usage >&2; exit 2 ;; diff --git a/modules/cleanup/check.nix b/modules/cleanup/check.nix index d470227..534ce9a 100644 --- a/modules/cleanup/check.nix +++ b/modules/cleanup/check.nix @@ -194,5 +194,37 @@ pkgs.runCommand "cleanup-check" { nativeBuildInputs = [ cleanup pkgs.jq ]; } '' has "$res" "left alone: VRWebHelper.DebuggerEnabled = true" [ "$(jq -c . $V)" = '{"VRWebHelper":{"DebuggerEnabled":true}}' ] || fail "user's key changed" echo "D ok" + + # --- E: steamvr-debugger-arm (before each SteamVR start) --- + arm() { bash ${../../install.sh} steamvr-debugger-arm; } + fresh e + printf '{\n "steamvr" : {}\n}\n' > $V + touch $S/steamvr-debugger # old marker: migrated + echo activating > $STUB/state + res=$(arm); echo "$res" + [ "$(cat $S/steamvr-debugger.armed)" = absent ] || fail "armed" + gone $S/steamvr-debugger + [ "$(jq -c .VRWebHelper $V)" = '{"DebuggerEnabled":true}' ] || fail "arm: $(cat $V)" + there $root/run/steam-frame-nix/steamvr-debugger-restore + [ "$(grep -c daemon-reload $STUB/log)" = 1 ] || fail "arm reload" + before=$(snap); res=$(arm) + [ "$(snap)" = "$before" ] || fail "second arm changed files" + [ "$(grep -c daemon-reload $STUB/log)" = 1 ] || fail "second arm reloaded" + bash $root/run/steam-frame-nix/steamvr-debugger-restore # SteamVR stops + [ "$(jq -c . $V)" = '{"steamvr":{}}' ] || fail "arm restore: $(cat $V)" + gone $S/steamvr-debugger.armed + res=$(arm) # restart: armed again + [ "$(cat $S/steamvr-debugger.armed)" = absent ] || fail "re-arm" + bash $root/run/steam-frame-nix/steamvr-debugger-restore + # the user's own true: never armed, never restored + printf '{"VRWebHelper":{"DebuggerEnabled":true}}\n' > $V + res=$(arm); has "$res" "not set by steam-frame-nix" + gone $S/steamvr-debugger.armed + # no file yet: created, and removed again if nothing else was added + rm $V; res=$(arm) + [ "$(cat $S/steamvr-debugger.armed)" = nofile ] || fail "nofile" + bash $root/run/steam-frame-nix/steamvr-debugger-restore + gone $V $S/steamvr-debugger.armed + echo "E ok" touch $out '' diff --git a/modules/cleanup/package.nix b/modules/cleanup/package.nix index 88c4156..3b4ece7 100644 --- a/modules/cleanup/package.nix +++ b/modules/cleanup/package.nix @@ -1,10 +1,12 @@ # steam-frame-nix-cleanup: `install.sh cleanup` (see there) with its tools # from nixpkgs. `nix run github:lhns/steam-frame-nix#cleanup -- --all`. -{ writeShellApplication, bash, coreutils, findutils, jq, gnugrep, gnused, gawk, procps }: +# Other install.sh commands the modules run: name + command. +{ writeShellApplication, bash, coreutils, findutils, jq, gnugrep, gnused, gawk, procps +, name ? "steam-frame-nix-cleanup", command ? "cleanup" }: writeShellApplication { - name = "steam-frame-nix-cleanup"; + inherit name; runtimeInputs = [ bash coreutils findutils jq gnugrep gnused gawk procps ]; text = '' - exec bash ${../../install.sh} cleanup "$@" + exec bash ${../../install.sh} ${command} "$@" ''; } diff --git a/modules/steamvr-debugger.nix b/modules/steamvr-debugger.nix index a483a64..2aea4a7 100644 --- a/modules/steamvr-debugger.nix +++ b/modules/steamvr-debugger.nix @@ -2,55 +2,35 @@ # 127.0.0.1:8087, for dashboard patches. On automatically (mkDefault) when a # patch targets port 8087. # -# The port opens only with VRWebHelper/DebuggerEnabled (requires a SteamVR -# restart). steamvr.vrsettings is rewritten by SteamVR from memory, so it -# can't be a store link or be edited while SteamVR runs: a oneshot merges just -# that key with jq before each steamvr.service start (drop-in Wants/After; -# ExecStartPre= would be too late, vrserver starts in the unit's own chain). -# Disabled, it resets the key only if it set it (marker in -# $XDG_STATE_HOME/steam-frame-nix); a manual setting is kept. -# Developer Mode forwards the port to 0.0.0.0:8088 (README, "DevTools on the -# LAN"); our patches use 127.0.0.1 only. +# The port opens only with VRWebHelper/DebuggerEnabled in +# ~/.config/openvr/config/steamvr.vrsettings, which SteamVR rewrites from +# memory, so it can't be a store link or be edited while SteamVR runs. The key +# is set only while SteamVR runs: +# - before each start, the steamvr-webhelper-debugger oneshot (drop-in +# Wants/After on steamvr.service; ExecStartPre= would be too late, vrserver +# starts in the unit's own chain) runs `install.sh steamvr-debugger-arm`: +# it keeps the key's value in $XDG_STATE_HOME/steam-frame-nix/ +# steamvr-debugger.armed (a key already true is the user's own and left +# alone), sets the key, and writes a runtime drop-in +# ($XDG_RUNTIME_DIR/systemd/user/steamvr.service.d) with a /usr/bin-only +# restore script (daemon-reload only when it is new, i.e. once per boot); +# - when SteamVR stops, that ExecStopPost= puts the value back and removes +# .armed. The runtime pieces don't depend on Nix, so this also works after +# a rollback or uninstall; they are gone at reboot, and .armed (the only +# trace after a power loss) is resolved by the next start or +# steam-frame-nix-cleanup. +# Off: no unit and no drop-in; cleanup restores the key once SteamVR is +# stopped. Developer Mode forwards the port to 0.0.0.0:8088 (README, +# "DevTools on the LAN"); our patches use 127.0.0.1 only. { config, lib, pkgs, ... }: let cfg = config.steamFrame.steamvrDebugger; usesDebugger = p: builtins.match "[a-z]+://[^/]*:8087(/.*)?" p.endpoint != null; - merge = pkgs.writeShellApplication { + arm = pkgs.callPackage ./cleanup/package.nix { name = "steamvr-webhelper-debugger"; - runtimeInputs = [ pkgs.jq pkgs.coreutils ]; - text = '' - f="$HOME/.config/openvr/config/steamvr.vrsettings" - marker="''${XDG_STATE_HOME:-$HOME/.local/state}/steam-frame-nix/steamvr-debugger" - ${if cfg.enable then '' - want=true - '' else '' - if [ ! -e "$marker" ]; then - echo "not managed (VRWebHelper.DebuggerEnabled left as is)" - exit 0 - fi - want=false - ''} - mkdir -p "$(dirname "$f")" - [ -s "$f" ] || printf '{}\n' > "$f" - if [ "$(jq '.VRWebHelper.DebuggerEnabled' "$f")" = "$want" ]; then - echo "VRWebHelper.DebuggerEnabled already $want" - else - tmp="$(mktemp "$f.XXXXXX")" - trap 'rm -f "$tmp"' EXIT - jq --indent 3 --argjson v "$want" '.VRWebHelper.DebuggerEnabled = $v' "$f" > "$tmp" - chmod --reference="$f" "$tmp" - mv "$tmp" "$f" - echo "VRWebHelper.DebuggerEnabled set to $want (effective from this SteamVR start)" - fi - if [ "$want" = true ]; then - mkdir -p "$(dirname "$marker")" - touch "$marker" - else - rm -f "$marker" - fi - ''; + command = "steamvr-debugger-arm"; }; in { imports = [ ./cleanup.nix ./steam-ui-patches.nix ]; @@ -65,33 +45,39 @@ in { description = '' SteamVR's web helper debugger (dashboard DevTools on 127.0.0.1:8087, VRWebHelper/DebuggerEnabled in steamvr.vrsettings), needed by SteamVR - dashboard patches, which turn it on automatically. Takes effect after one SteamVR - restart; off resets the key at the next start. Developer Mode also - forwards the port to the LAN (0.0.0.0:8088). + dashboard patches, which turn it on automatically. Set only while + SteamVR runs (from its next start; the first time needs one SteamVR + restart) and put back to its previous value when SteamVR stops. + Developer Mode also forwards the port to the LAN (0.0.0.0:8088). ''; }; - config = { - steamFrame.steamvrDebugger.enable = - lib.mkDefault (lib.any usesDebugger config.steamFrame.uiPatches.patches); - steamFrame.cleanup.keep = lib.optional cfg.enable "debugger"; + config = lib.mkMerge [ + { + steamFrame.steamvrDebugger.enable = + lib.mkDefault (lib.any usesDebugger config.steamFrame.uiPatches.patches); + } + (lib.mkIf cfg.enable { + steamFrame.cleanup.keep = [ "debugger" ]; - systemd.user.services.steamvr-webhelper-debugger = { - Unit = { - Description = "Set SteamVR VRWebHelper/DebuggerEnabled (steam-frame-nix)"; - Before = [ "steamvr.service" ]; + systemd.user.services.steamvr-webhelper-debugger = { + Unit = { + Description = "Set SteamVR VRWebHelper/DebuggerEnabled until SteamVR stops (steam-frame-nix)"; + Before = [ "steamvr.service" ]; + }; + Service = { + Type = "oneshot"; + Environment = [ "STEAM_FRAME_NIX_RUNTIME_DIR=%t" ]; + ExecStart = lib.getExe arm; + }; }; - Service = { - Type = "oneshot"; - ExecStart = "${merge}/bin/steamvr-webhelper-debugger"; - }; - }; - # Wants=, not Requires=: SteamVR still starts if the merge fails. - xdg.configFile."systemd/user/steamvr.service.d/webhelper-debugger.conf".text = '' - [Unit] - Wants=steamvr-webhelper-debugger.service - After=steamvr-webhelper-debugger.service - ''; - }; + # Wants=, not Requires=: SteamVR still starts if this fails. + xdg.configFile."systemd/user/steamvr.service.d/webhelper-debugger.conf".text = '' + [Unit] + Wants=steamvr-webhelper-debugger.service + After=steamvr-webhelper-debugger.service + ''; + }) + ]; }