mirror of
https://github.com/lhns/steam-frame-nix.git
synced 2026-10-06 03:00:13 +02:00
launcher-menu: icon fallbacks as Home Manager links
The icon fallbacks were links an activation script made (and tracked in ~/.local/state/steam-frame-nix/icon-fallbacks) after scanning the desktop entries. They are Home Manager links now: ~/.local/share/icons/hicolor/scalable/apps/<name>.svg -> the largest Breeze app SVG, for utilities-terminal and preferences-system (SteamOS' Konsole and KDE System Settings) plus iconFallbacks.extra; a name Breeze doesn't have fails the build. enable = false: none. - The scan is read-only now (steam-frame-icon-fallbacks --suggest): each switch prints icons Steam can't find that Breeze has (for extra) and fallbacks hicolor has anyway. - hicolor's mtime is bumped when the set of links changed between generations, so a running Steam rescans (GTK checks theme dir mtimes). - The script's old links at those paths are removed before checkLinkTargets (they would be collisions); any others and the manifest go through steam-frame-nix-cleanup. The assertion for the old list form stays until ~2026-12.
This commit is contained in:
1 parent
b7b32d0aa9
commit
a0111813b3
2 files changed
+87
-86
No files matched your search
+53
-21
@@ -10,21 +10,39 @@
|
|||||||
# Tested with Steam client 1790377368.
|
# Tested with Steam client 1790377368.
|
||||||
#
|
#
|
||||||
# iconFallbacks (not a patch): Steam resolves Icon= names only in hicolor (and
|
# iconFallbacks (not a patch): Steam resolves Icon= names only in hicolor (and
|
||||||
# pixmaps), so Breeze-only icons (Konsole, KDE System Settings) are missing;
|
# pixmaps), so Breeze-only icons (Konsole, KDE System Settings) are missing.
|
||||||
# icon-fallbacks.sh links them from nixpkgs' Breeze into
|
# Home Manager links a fixed list of them from nixpkgs' Breeze into
|
||||||
# ~/.local/share/icons/hicolor on switch. It was a list of names until 2026-09;
|
# ~/.local/share/icons/hicolor/scalable/apps (a missing name fails the
|
||||||
# a list now fails with a pointer to enable/extra.
|
# build); on switch, icon-fallbacks.sh --suggest only prints hints (names to
|
||||||
|
# add, names no longer needed), and hicolor's mtime is bumped when the list
|
||||||
|
# changed so a running Steam rescans. Until 2026-09 a script made the links
|
||||||
|
# on switch (removed by steam-frame-nix-cleanup, or before checkLinkTargets
|
||||||
|
# where Home Manager takes over). iconFallbacks was a list of names until
|
||||||
|
# 2026-09; a list fails with a pointer to enable/extra (until ~2026-12).
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.steamFrame.launcherMenu;
|
cfg = config.steamFrame.launcherMenu;
|
||||||
inherit (import ./lib { inherit pkgs; }) mkPatch;
|
inherit (import ./lib { inherit pkgs; }) mkPatch;
|
||||||
sharedJSContext = { title = "SharedJSContext"; };
|
sharedJSContext = { title = "SharedJSContext"; };
|
||||||
|
|
||||||
iconFallbacks = pkgs.writeShellApplication {
|
iconSuggest = pkgs.writeShellApplication {
|
||||||
name = "steam-frame-icon-fallbacks";
|
name = "steam-frame-icon-fallbacks";
|
||||||
runtimeInputs = [ pkgs.coreutils pkgs.findutils pkgs.gawk ];
|
runtimeInputs = [ pkgs.coreutils pkgs.findutils pkgs.gawk ];
|
||||||
text = builtins.readFile ./launcher-menu/icon-fallbacks.sh;
|
text = builtins.readFile ./launcher-menu/icon-fallbacks.sh;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
breezeApps = "${pkgs.kdePackages.breeze-icons}/share/icons/breeze/apps";
|
||||||
|
defaultIcons = [ "utilities-terminal" "preferences-system" ];
|
||||||
|
icons = lib.optionals cfg.iconFallbacks.enable (lib.unique (defaultIcons ++ cfg.iconFallbacks.extra));
|
||||||
|
# The largest Breeze app icon (all SVG); fails the build if there is none.
|
||||||
|
breezeIcon = name: pkgs.runCommand "icon-fallback-${name}.svg" { } ''
|
||||||
|
for size in 64 48 32 24 22 16; do
|
||||||
|
if [ -e ${breezeApps}/$size/${name}.svg ]; then ln -s ${breezeApps}/$size/${name}.svg $out; exit 0; fi
|
||||||
|
done
|
||||||
|
echo "steamFrame.launcherMenu.iconFallbacks: Breeze has no app icon \"${name}\" (${breezeApps}/<size>/${name}.svg)" >&2
|
||||||
|
exit 1
|
||||||
|
'';
|
||||||
|
iconDir = "${lib.removePrefix "${config.home.homeDirectory}/" config.xdg.dataHome}/icons/hicolor";
|
||||||
in {
|
in {
|
||||||
imports = [
|
imports = [
|
||||||
./cleanup.nix
|
./cleanup.nix
|
||||||
@@ -119,12 +137,12 @@ in {
|
|||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
description = ''
|
description = ''
|
||||||
On switch, for each Icon= of the desktop entries Steam sees
|
Link nixpkgs' Breeze SVGs of utilities-terminal and
|
||||||
that hicolor lacks but nixpkgs' Breeze has (SteamOS:
|
preferences-system (SteamOS' Konsole and KDE System
|
||||||
utilities-terminal, preferences-system), link the Breeze SVG
|
Settings) and of `extra` into
|
||||||
into ~/.local/share/icons/hicolor/scalable/apps. Only its own
|
~/.local/share/icons/hicolor/scalable/apps (Home Manager
|
||||||
links (listed in ~/.local/state/steam-frame-nix/icon-fallbacks)
|
links). Each switch prints hints: icons Steam can't find
|
||||||
are touched; stale ones are removed; false removes all.
|
that Breeze has, and fallbacks hicolor has anyway.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
extra = lib.mkOption {
|
extra = lib.mkOption {
|
||||||
@@ -132,8 +150,8 @@ in {
|
|||||||
default = [ ];
|
default = [ ];
|
||||||
example = [ "system-file-manager" ];
|
example = [ "system-file-manager" ];
|
||||||
description = ''
|
description = ''
|
||||||
Extra icon names to provide (if hicolor lacks them); names
|
Further Breeze app icon names to provide; a name Breeze
|
||||||
Breeze doesn't have are reported and skipped.
|
doesn't have fails the build.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
legacyList = lib.mkOption {
|
legacyList = lib.mkOption {
|
||||||
@@ -158,15 +176,29 @@ in {
|
|||||||
'';
|
'';
|
||||||
} ];
|
} ];
|
||||||
|
|
||||||
# After installPackages so the new profile counts; without BREEZE_APPS the
|
config.xdg.dataFile = lib.listToAttrs (map (name: {
|
||||||
# script removes its links.
|
name = "icons/hicolor/scalable/apps/${name}.svg";
|
||||||
|
value.source = breezeIcon name;
|
||||||
|
}) icons);
|
||||||
|
|
||||||
|
# The script's links of 2026-09 where Home Manager links now.
|
||||||
|
config.steamFrame.cleanup.migrateLinks = map (name: {
|
||||||
|
path = "${iconDir}/scalable/apps/${name}.svg";
|
||||||
|
target = "/nix/store/*-breeze-icons-*/share/icons/breeze/apps/*";
|
||||||
|
}) icons;
|
||||||
|
|
||||||
|
# GTK (Steam) rescans an icon theme only when a theme dir's mtime changes:
|
||||||
|
# bump hicolor's when the links changed. After installPackages so the new
|
||||||
|
# profile counts for the hints.
|
||||||
config.home.activation.steamFrameIconFallbacks =
|
config.home.activation.steamFrameIconFallbacks =
|
||||||
lib.hm.dag.entryAfter [ "writeBoundary" "installPackages" ] (
|
lib.hm.dag.entryAfter [ "writeBoundary" "linkGeneration" "installPackages" ] (''
|
||||||
if cfg.iconFallbacks.enable then ''
|
apps=${lib.escapeShellArg "${iconDir}/scalable/apps"}
|
||||||
run env BREEZE_APPS=${pkgs.kdePackages.breeze-icons}/share/icons/breeze/apps \
|
if [[ -d $HOME/${lib.escapeShellArg iconDir} ]] &&
|
||||||
${lib.getExe iconFallbacks} ${lib.escapeShellArgs cfg.iconFallbacks.extra}
|
[[ "$(ls "''${oldGenPath:-/nonexistent}/home-files/$apps" 2>/dev/null)" != "$(ls "$newGenPath/home-files/$apps" 2>/dev/null)" ]]; then
|
||||||
'' else ''
|
run touch "$HOME"/${lib.escapeShellArg iconDir}
|
||||||
run ${lib.getExe iconFallbacks}
|
fi
|
||||||
|
'' + lib.optionalString cfg.iconFallbacks.enable ''
|
||||||
|
BREEZE_APPS=${breezeApps} ${lib.getExe iconSuggest} --suggest ${lib.escapeShellArgs icons} || true
|
||||||
'');
|
'');
|
||||||
|
|
||||||
config.steamFrame.uiPatches.patches =
|
config.steamFrame.uiPatches.patches =
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
# shellcheck shell=bash
|
# shellcheck shell=bash
|
||||||
# Hicolor fallbacks for desktop entry icons that only Breeze has.
|
# Hints for launcherMenu.iconFallbacks; read-only.
|
||||||
# Usage: steam-frame-icon-fallbacks [EXTRA_NAME...]
|
# Usage: BREEZE_APPS=<breeze-icons>/share/icons/breeze/apps \
|
||||||
# BREEZE_APPS=<breeze-icons>/share/icons/breeze/apps: link each Icon= name
|
# steam-frame-icon-fallbacks --suggest [CONFIGURED_NAME...]
|
||||||
# (of the entries Steam sees, plus EXTRA_NAMEs) that no hicolor dir has
|
# Prints
|
||||||
# but Breeze does as $XDG_DATA_HOME/icons/hicolor/scalable/apps/<name>.svg.
|
# - Icon= names of the desktop entries Steam sees that no hicolor dir (nor
|
||||||
# BREEZE_APPS unset: remove the links made before.
|
# pixmaps) has but Breeze does, and that aren't configured: candidates for
|
||||||
# Only links listed in the manifest and still pointing into breeze-icons are
|
# iconFallbacks.extra;
|
||||||
# ever removed.
|
# - configured names that hicolor has anyway (apart from our links): no
|
||||||
|
# longer needed.
|
||||||
|
|
||||||
|
[[ ${1:-} == --suggest ]] || { echo "usage: steam-frame-icon-fallbacks --suggest [NAME...]" >&2; exit 2; }
|
||||||
|
shift
|
||||||
|
: "${BREEZE_APPS:?BREEZE_APPS is not set}"
|
||||||
|
|
||||||
data_home=${XDG_DATA_HOME:-$HOME/.local/share}
|
data_home=${XDG_DATA_HOME:-$HOME/.local/share}
|
||||||
dest=$data_home/icons/hicolor/scalable/apps
|
|
||||||
manifest=${XDG_STATE_HOME:-$HOME/.local/state}/steam-frame-nix/icon-fallbacks
|
|
||||||
# Steam's data dirs (XDG_DATA_HOME, then its XDG_DATA_DIRS on SteamOS), not
|
# Steam's data dirs (XDG_DATA_HOME, then its XDG_DATA_DIRS on SteamOS), not
|
||||||
# the caller's: switches usually run from the nested desktop session.
|
# the caller's: switches usually run from the nested desktop session.
|
||||||
data_dirs=(
|
data_dirs=(
|
||||||
@@ -24,29 +27,26 @@ data_dirs=(
|
|||||||
)
|
)
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
|
|
||||||
ours() { [[ -L $1 && $(readlink "$1") == /nix/store/*-breeze-icons-*/* ]]; }
|
# Our fallbacks: links that end up in a Breeze store path.
|
||||||
|
ours() { [[ -L $1 && $(readlink -f "$1") == /nix/store/*-breeze-icons-*/* ]]; }
|
||||||
|
|
||||||
# Does Steam already find <name> (hicolor in any data dir, or pixmaps)?
|
# Where Steam finds <name> (hicolor in any data dir, or pixmaps), ours aside.
|
||||||
# Our own links don't count.
|
|
||||||
resolves() {
|
resolves() {
|
||||||
local d f
|
local d f
|
||||||
for d in "$HOME/.icons" "${data_dirs[@]/%//icons}"; do
|
for d in "$HOME/.icons" "${data_dirs[@]/%//icons}"; do
|
||||||
for f in "$d"/hicolor/*/*/"$1".{png,svg,xpm}; do
|
for f in "$d"/hicolor/*/*/"$1".{png,svg,xpm}; do
|
||||||
ours "$f" || return 0
|
ours "$f" || { echo "$f"; return 0; }
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
for d in "${data_dirs[@]}"; do
|
for d in "${data_dirs[@]}"; do
|
||||||
for f in "$d"/pixmaps/"$1".{png,svg,xpm}; do [[ ! -e $f ]] || return 0; done
|
for f in "$d"/pixmaps/"$1".{png,svg,xpm}; do [[ ! -e $f ]] || { echo "$f"; return 0; }; done
|
||||||
done
|
done
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Largest Breeze app icon for <name> (Breeze's app icons are all SVG).
|
in_breeze() {
|
||||||
breeze() {
|
|
||||||
local size
|
local size
|
||||||
for size in 64 48 32 24 22 16; do
|
for size in 64 48 32 24 22 16; do [[ -e $BREEZE_APPS/$size/$1.svg ]] && return 0; done
|
||||||
if [[ -e $BREEZE_APPS/$size/$1.svg ]]; then echo "$BREEZE_APPS/$size/$1.svg"; return; fi
|
|
||||||
done
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,11 +64,11 @@ entry_icon() {
|
|||||||
' "$1"
|
' "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Wanted: extra names, and the icons of the entries Steam sees (the first
|
declare -A configured=() seen=() missing=()
|
||||||
# entry with a given desktop-file ID wins, like in the menu).
|
for name in "$@"; do configured[$name]=1; done
|
||||||
declare -A wanted=() seen=()
|
|
||||||
if [[ -n ${BREEZE_APPS:-} ]]; then
|
# The entries Steam sees (the first with a given desktop-file ID wins, like
|
||||||
for name in "$@"; do wanted[$name]=extra; done
|
# in the menu).
|
||||||
for d in "${data_dirs[@]}"; do
|
for d in "${data_dirs[@]}"; do
|
||||||
[[ -d $d/applications ]] || continue
|
[[ -d $d/applications ]] || continue
|
||||||
while IFS= read -r -d '' f; do
|
while IFS= read -r -d '' f; do
|
||||||
@@ -76,48 +76,17 @@ if [[ -n ${BREEZE_APPS:-} ]]; then
|
|||||||
[[ -z ${seen[$id]:-} ]] || continue
|
[[ -z ${seen[$id]:-} ]] || continue
|
||||||
seen[$id]=1
|
seen[$id]=1
|
||||||
icon=$(entry_icon "$f")
|
icon=$(entry_icon "$f")
|
||||||
[[ -z $icon ]] || wanted[$icon]=${wanted[$icon]:-$id}
|
[[ -n $icon && -z ${configured[$icon]:-} && -z ${missing[$icon]:-} ]] || continue
|
||||||
|
if ! resolves "$icon" >/dev/null && in_breeze "$icon"; then missing[$icon]=${id%.desktop}; fi
|
||||||
done < <(find -L "$d/applications" -name '*.desktop' -print0 2>/dev/null)
|
done < <(find -L "$d/applications" -name '*.desktop' -print0 2>/dev/null)
|
||||||
done
|
done
|
||||||
fi
|
|
||||||
|
|
||||||
changed=
|
for icon in "${!missing[@]}"; do
|
||||||
linked=()
|
echo "icon fallbacks: \"$icon\" (${missing[$icon]}) shows without icon in Steam's \"+\" menu; Breeze has it: add it to steamFrame.launcherMenu.iconFallbacks.extra"
|
||||||
for name in "${!wanted[@]}"; do
|
|
||||||
resolves "$name" && continue
|
|
||||||
if ! src=$(breeze "$name"); then
|
|
||||||
[[ ${wanted[$name]} != extra ]] || echo "icon fallbacks: Breeze has no app icon \"$name\" (extra)" >&2
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
link=$dest/$name.svg
|
|
||||||
if [[ -e $link || -L $link ]] && ! ours "$link"; then continue; fi # not ours
|
|
||||||
linked+=("$name")
|
|
||||||
[[ $(readlink "$link") == "$src" ]] && continue
|
|
||||||
mkdir -p "$dest"
|
|
||||||
ln -sfn "$src" "$link"
|
|
||||||
echo "icon fallbacks: linked $name (${wanted[$name]})"
|
|
||||||
changed=1
|
|
||||||
done
|
done
|
||||||
|
for name in "${!configured[@]}"; do
|
||||||
# Remove links from earlier runs that are no longer wanted.
|
if f=$(resolves "$name"); then
|
||||||
if [[ -f $manifest ]]; then
|
echo "icon fallbacks: \"$name\" is in hicolor anyway ($f); the fallback is no longer needed"
|
||||||
while IFS= read -r name; do
|
|
||||||
[[ -n $name && " ${linked[*]} " != *" $name "* ]] || continue
|
|
||||||
if ours "$dest/$name.svg"; then
|
|
||||||
rm -f "$dest/$name.svg"
|
|
||||||
echo "icon fallbacks: removed $name"
|
|
||||||
changed=1
|
|
||||||
fi
|
fi
|
||||||
done < "$manifest"
|
done
|
||||||
fi
|
exit 0
|
||||||
|
|
||||||
if (( ${#linked[@]} )); then
|
|
||||||
mkdir -p "${manifest%/*}"
|
|
||||||
printf '%s\n' "${linked[@]}" | sort > "$manifest"
|
|
||||||
else
|
|
||||||
rm -f "$manifest"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# GTK rescans an icon theme only when a theme dir's mtime changes; bump it
|
|
||||||
# so a running Steam sees the change.
|
|
||||||
if [[ -n $changed && -d $data_home/icons/hicolor ]]; then touch "$data_home/icons/hicolor"; fi
|
|
||||||
Reference in new issue
Block a user