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.
This commit is contained in:
Pierre Kisters committed 2026-09-30 22:54:13 +02:00
1 parent b2babb3859
commit 26641d76a0
11 files changed
+301 -6

No files matched your search

+13
View File
@@ -57,6 +57,8 @@ pkgs.runCommand "cleanup-check" { nativeBuildInputs = [ cleanup pkgs.jq ]; } ''
echo '[Desktop Entry]' > $root/run/steam-frame-nix/applications/im.riot.Riot.desktop
echo x > $root/run/steam-frame-nix/applications/.im.riot.Riot.desktop.sum
: > $root/run/steam-frame-nix/applications/.lock
ln -s $HOME/.local/share/Steam/userdata/1/760/remote/250820/screenshots \
$root/run/steam-frame-nix/screenshots # steamFrame.screenshots
echo old > $root/run/systemd/user/steamvr.service.d/50-steam-frame-nix-debugger.conf
echo keep > $root/run/systemd/user/steamvr.service.d/other.conf
ln -s /nix/store/00000000000000000000000000000000-breeze-icons-6.30.0/share/icons/breeze/apps/64/utilities-terminal.svg $I/utilities-terminal.svg
@@ -191,6 +193,17 @@ pkgs.runCommand "cleanup-check" { nativeBuildInputs = [ cleanup pkgs.jq ]; } ''
exec 8<&- # Firefox closed
res=$(steam-frame-nix-cleanup --orphans)
gone $F/desktop/user.js
# screenshots link: kept while used, a foreign link left alone
mkdir -p $root/run/steam-frame-nix
ln -s /x/userdata/1/760/remote/250820/screenshots $root/run/steam-frame-nix/screenshots
res=$(steam-frame-nix-cleanup --orphans --keep screenshots)
there $root/run/steam-frame-nix/screenshots
res=$(steam-frame-nix-cleanup --orphans)
gone $root/run/steam-frame-nix/screenshots
ln -s /elsewhere $root/run/steam-frame-nix/screenshots
res=$(steam-frame-nix-cleanup --orphans)
has "$res" "left alone: $root/run/steam-frame-nix/screenshots"
rm -r $root/run/steam-frame-nix
! steam-frame-nix-cleanup --all --keep debugger 2>/dev/null || fail "--all --keep accepted"
! steam-frame-nix-cleanup --orphans --keep bogus 2>/dev/null || fail "unknown --keep accepted"
echo "C ok"
+73
View File
@@ -0,0 +1,73 @@
# ~/Pictures/<name>: a link to Steam's folder of SteamVR screenshots
# (docs/screenshots.md). With steamUserId a plain Home Manager link; without,
# it points to <session.runtimeDir>/steam-frame-nix/screenshots (tmpfs),
# which steam-frame-nix-screenshots (screenshots/link.sh) points to the
# current account's folder: on switch, at login and when loginusers.vdf or
# userdata changes (path unit).
{ config, lib, pkgs, ... }:
let
cfg = config.steamFrame.screenshots;
session = config.steamFrame.session;
steam = "${config.home.homeDirectory}/.local/share/Steam";
detect = cfg.enable && cfg.steamUserId == null;
script = import ./screenshots/package.nix { inherit lib pkgs steam; inherit (session) runtimeDir; };
exe = lib.getExe script;
in {
imports = [ ./cleanup.nix ];
options.steamFrame.screenshots = {
enable = lib.mkEnableOption "a link to the SteamVR screenshots in ~/Pictures";
name = lib.mkOption {
type = lib.types.str;
default = "SteamVR Screenshots";
description = "Name of the link in ~/Pictures.";
};
steamUserId = lib.mkOption {
type = lib.types.nullOr (lib.types.strMatching "[1-9][0-9]*");
default = null;
example = "80511808";
description = ''
Steam account ID: the folder name in ~/.local/share/Steam/userdata.
`null`: the account last logged in, found at runtime.
'';
};
};
config = lib.mkIf cfg.enable (lib.mkMerge [
{
home.file."Pictures/${cfg.name}".source = config.lib.file.mkOutOfStoreSymlink
(if detect then "${session.runtimeDir}/steam-frame-nix/screenshots"
else "${steam}/userdata/${cfg.steamUserId}/760/remote/250820/screenshots");
}
(lib.mkIf detect {
steamFrame.cleanup.keep = [ "screenshots" ];
home.packages = [ script ];
systemd.user.services.steam-frame-nix-screenshots = {
Unit.Description = "steam-frame-nix: link to the SteamVR screenshots of the current Steam account";
Service = {
Type = "oneshot";
ExecStart = exe;
};
Install.WantedBy = [ "default.target" ];
};
systemd.user.paths.steam-frame-nix-screenshots = {
Unit.Description = "steam-frame-nix: update the screenshots link when the Steam account changes";
Path = {
PathChanged = [ "${steam}/config/loginusers.vdf" "${steam}/userdata" ];
Unit = "steam-frame-nix-screenshots.service";
};
Install.WantedBy = [ "default.target" ];
};
steamFrame.session.services.start = [ "steam-frame-nix-screenshots.path" ];
home.activation.steamFrameScreenshots = lib.hm.dag.entryAfter [ "linkGeneration" "steamFrameNixCleanup" ] ''
if [[ -v DRY_RUN ]]; then
echo "would run: ${exe}"
else
${exe}
fi
'';
})
]);
}
+58
View File
@@ -0,0 +1,58 @@
# Checks of steam-frame-nix-screenshots (link.sh) on fake Steam and runtime
# dirs: account from loginusers.vdf (MostRecent, else latest timestamp),
# else the only userdata folder, else no link (an old one removed); an
# unchanged target isn't rewritten; no outer runtime dir is a no-op.
{ pkgs }:
let
script = import ./package.nix {
inherit (pkgs) lib; inherit pkgs;
runtimeDir = "/nonexistent"; steam = "/nonexistent";
};
in
pkgs.runCommand "screenshots-check" { nativeBuildInputs = [ script ]; } ''
set -euo pipefail
fail() { echo "FAIL: $*" >&2; exit 1; }
export STEAM_FRAME_NIX_RUNTIME_DIR=$PWD/run STEAM_FRAME_NIX_STEAM_DIR=$PWD/steam
S=$STEAM_FRAME_NIX_STEAM_DIR L=$STEAM_FRAME_NIX_RUNTIME_DIR/steam-frame-nix/screenshots
target() { echo "$S/userdata/$1/760/remote/250820/screenshots"; }
expect() { [ "$(readlink $L)" = "$(target $1)" ] || fail "$2: $(readlink $L || echo no link)"; }
steam-frame-nix-screenshots 2>&1 | grep -q "doesn't exist" || fail "no runtime dir"
[ ! -e run ] || fail "runtime dir created"
mkdir -p run $S/config $S/userdata/0 $S/userdata/80511808
# the only account folder ("0" is not an account)
steam-frame-nix-screenshots; expect 80511808 "userdata"
# MostRecent wins over a later timestamp
cat > $S/config/loginusers.vdf <<'VDF'
"users"
{
"76561198040777536"
{
"AccountName" "a"
"MostRecent" "1"
"timestamp" "100"
}
"76561197960265729"
{
"AccountName" "b"
"MostRecent" "0"
"timestamp" "200"
}
}
VDF
steam-frame-nix-screenshots; expect 80511808 "MostRecent"
# unchanged: not rewritten
res=$(steam-frame-nix-screenshots 2>&1); [ -z "$res" ] || fail "rewrote: $res"
# no MostRecent (current Steam): latest timestamp
sed -i '/MostRecent/d' $S/config/loginusers.vdf
steam-frame-nix-screenshots; expect 1 "timestamp"
# nothing to go by: link removed
rm $S/config/loginusers.vdf; mkdir $S/userdata/42
steam-frame-nix-screenshots 2>&1 | grep -q "no Steam account" || fail "no warning"
[ ! -L $L ] || fail "link kept"
touch $out
''
+54
View File
@@ -0,0 +1,54 @@
# steam-frame-nix-screenshots (the module prepends runtime, steam): points
# <runtime>/steam-frame-nix/screenshots (tmpfs), where ~/Pictures/<name>
# links, to the SteamVR screenshot folder of the Steam account last logged
# in:
# 1. the user marked MostRecent "1" in config/loginusers.vdf, else the one
# with the latest "timestamp" (SteamID64 - 76561197960265728 = account
# ID, the folder in userdata);
# 2. else the only account folder in userdata;
# 3. else no link (warning).
# The link is replaced atomically and only when its target changes.
link=$runtime/steam-frame-nix/screenshots
log() { echo "steam-frame-nix-screenshots: $*" >&2; }
if [[ ! -d $runtime ]]; then
log "$runtime doesn't exist (outer session not running?); skipped"
exit 0
fi
id=''
if [[ -f $steam/config/loginusers.vdf ]]; then
id64=$(awk '
{ gsub(/"/, " ") }
$1 == "{" { depth++; next }
$1 == "}" { depth--; next }
depth == 1 && $1 ~ /^[0-9]+$/ && NF == 1 { user = $1; next }
depth == 2 && tolower($1) == "mostrecent" && $2 == "1" { recent = user }
depth == 2 && tolower($1) == "timestamp" && $2 + 0 > best { best = $2 + 0; latest = user }
END { print recent != "" ? recent : latest }
' "$steam/config/loginusers.vdf")
if [[ $id64 =~ ^[0-9]{17}$ ]] && (( id64 > 76561197960265728 )); then
id=$(( id64 - 76561197960265728 ))
fi
fi
if [[ -z $id ]]; then
dirs=()
for d in "$steam"/userdata/*/; do
d=${d%/}; d=${d##*/}
[[ $d =~ ^[1-9][0-9]*$ ]] && dirs+=("$d")
done
(( ${#dirs[@]} == 1 )) && id=${dirs[0]}
fi
if [[ -z $id ]]; then
log "no Steam account found in $steam (log in to Steam, or set steamFrame.screenshots.steamUserId); no link"
[[ -L $link ]] && rm -f "$link"
exit 0
fi
target=$steam/userdata/$id/760/remote/250820/screenshots
[[ -L $link && $(readlink "$link") == "$target" ]] && exit 0
mkdir -p "${link%/*}"
ln -sfn "$target" "$link.tmp.$$"
mv -fT "$link.tmp.$$" "$link"
log "$link -> $target"
+11
View File
@@ -0,0 +1,11 @@
# steam-frame-nix-screenshots: link.sh with the outer runtime dir and the
# Steam directory (both overridable by environment, for the check).
{ lib, pkgs, runtimeDir, steam }:
pkgs.writeShellApplication {
name = "steam-frame-nix-screenshots";
runtimeInputs = [ pkgs.coreutils pkgs.gawk ];
text = ''
runtime=''${STEAM_FRAME_NIX_RUNTIME_DIR:-${lib.escapeShellArg runtimeDir}}
steam=''${STEAM_FRAME_NIX_STEAM_DIR:-${lib.escapeShellArg steam}}
'' + builtins.readFile ./link.sh;
}