mirror of
https://github.com/lhns/steam-frame-nix.git
synced 2026-10-06 00:00:16 +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
+97
-96
No files matched your search
+54
-22
@@ -10,21 +10,39 @@
|
||||
# Tested with Steam client 1790377368.
|
||||
#
|
||||
# iconFallbacks (not a patch): Steam resolves Icon= names only in hicolor (and
|
||||
# pixmaps), so Breeze-only icons (Konsole, KDE System Settings) are missing;
|
||||
# icon-fallbacks.sh links them from nixpkgs' Breeze into
|
||||
# ~/.local/share/icons/hicolor on switch. It was a list of names until 2026-09;
|
||||
# a list now fails with a pointer to enable/extra.
|
||||
# pixmaps), so Breeze-only icons (Konsole, KDE System Settings) are missing.
|
||||
# Home Manager links a fixed list of them from nixpkgs' Breeze into
|
||||
# ~/.local/share/icons/hicolor/scalable/apps (a missing name fails the
|
||||
# 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, ... }:
|
||||
let
|
||||
cfg = config.steamFrame.launcherMenu;
|
||||
inherit (import ./lib { inherit pkgs; }) mkPatch;
|
||||
sharedJSContext = { title = "SharedJSContext"; };
|
||||
|
||||
iconFallbacks = pkgs.writeShellApplication {
|
||||
iconSuggest = pkgs.writeShellApplication {
|
||||
name = "steam-frame-icon-fallbacks";
|
||||
runtimeInputs = [ pkgs.coreutils pkgs.findutils pkgs.gawk ];
|
||||
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 {
|
||||
imports = [
|
||||
./cleanup.nix
|
||||
@@ -119,12 +137,12 @@ in {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
On switch, for each Icon= of the desktop entries Steam sees
|
||||
that hicolor lacks but nixpkgs' Breeze has (SteamOS:
|
||||
utilities-terminal, preferences-system), link the Breeze SVG
|
||||
into ~/.local/share/icons/hicolor/scalable/apps. Only its own
|
||||
links (listed in ~/.local/state/steam-frame-nix/icon-fallbacks)
|
||||
are touched; stale ones are removed; false removes all.
|
||||
Link nixpkgs' Breeze SVGs of utilities-terminal and
|
||||
preferences-system (SteamOS' Konsole and KDE System
|
||||
Settings) and of `extra` into
|
||||
~/.local/share/icons/hicolor/scalable/apps (Home Manager
|
||||
links). Each switch prints hints: icons Steam can't find
|
||||
that Breeze has, and fallbacks hicolor has anyway.
|
||||
'';
|
||||
};
|
||||
extra = lib.mkOption {
|
||||
@@ -132,8 +150,8 @@ in {
|
||||
default = [ ];
|
||||
example = [ "system-file-manager" ];
|
||||
description = ''
|
||||
Extra icon names to provide (if hicolor lacks them); names
|
||||
Breeze doesn't have are reported and skipped.
|
||||
Further Breeze app icon names to provide; a name Breeze
|
||||
doesn't have fails the build.
|
||||
'';
|
||||
};
|
||||
legacyList = lib.mkOption {
|
||||
@@ -158,16 +176,30 @@ in {
|
||||
'';
|
||||
} ];
|
||||
|
||||
# After installPackages so the new profile counts; without BREEZE_APPS the
|
||||
# script removes its links.
|
||||
config.xdg.dataFile = lib.listToAttrs (map (name: {
|
||||
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 =
|
||||
lib.hm.dag.entryAfter [ "writeBoundary" "installPackages" ] (
|
||||
if cfg.iconFallbacks.enable then ''
|
||||
run env BREEZE_APPS=${pkgs.kdePackages.breeze-icons}/share/icons/breeze/apps \
|
||||
${lib.getExe iconFallbacks} ${lib.escapeShellArgs cfg.iconFallbacks.extra}
|
||||
'' else ''
|
||||
run ${lib.getExe iconFallbacks}
|
||||
'');
|
||||
lib.hm.dag.entryAfter [ "writeBoundary" "linkGeneration" "installPackages" ] (''
|
||||
apps=${lib.escapeShellArg "${iconDir}/scalable/apps"}
|
||||
if [[ -d $HOME/${lib.escapeShellArg iconDir} ]] &&
|
||||
[[ "$(ls "''${oldGenPath:-/nonexistent}/home-files/$apps" 2>/dev/null)" != "$(ls "$newGenPath/home-files/$apps" 2>/dev/null)" ]]; then
|
||||
run touch "$HOME"/${lib.escapeShellArg iconDir}
|
||||
fi
|
||||
'' + lib.optionalString cfg.iconFallbacks.enable ''
|
||||
BREEZE_APPS=${breezeApps} ${lib.getExe iconSuggest} --suggest ${lib.escapeShellArgs icons} || true
|
||||
'');
|
||||
|
||||
config.steamFrame.uiPatches.patches =
|
||||
lib.optional cfg.sort {
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
# shellcheck shell=bash
|
||||
# Hicolor fallbacks for desktop entry icons that only Breeze has.
|
||||
# Usage: steam-frame-icon-fallbacks [EXTRA_NAME...]
|
||||
# BREEZE_APPS=<breeze-icons>/share/icons/breeze/apps: link each Icon= name
|
||||
# (of the entries Steam sees, plus EXTRA_NAMEs) that no hicolor dir has
|
||||
# but Breeze does as $XDG_DATA_HOME/icons/hicolor/scalable/apps/<name>.svg.
|
||||
# BREEZE_APPS unset: remove the links made before.
|
||||
# Only links listed in the manifest and still pointing into breeze-icons are
|
||||
# ever removed.
|
||||
# Hints for launcherMenu.iconFallbacks; read-only.
|
||||
# Usage: BREEZE_APPS=<breeze-icons>/share/icons/breeze/apps \
|
||||
# steam-frame-icon-fallbacks --suggest [CONFIGURED_NAME...]
|
||||
# Prints
|
||||
# - Icon= names of the desktop entries Steam sees that no hicolor dir (nor
|
||||
# pixmaps) has but Breeze does, and that aren't configured: candidates for
|
||||
# iconFallbacks.extra;
|
||||
# - 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}
|
||||
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
|
||||
# the caller's: switches usually run from the nested desktop session.
|
||||
data_dirs=(
|
||||
@@ -24,29 +27,26 @@ data_dirs=(
|
||||
)
|
||||
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)?
|
||||
# Our own links don't count.
|
||||
# Where Steam finds <name> (hicolor in any data dir, or pixmaps), ours aside.
|
||||
resolves() {
|
||||
local d f
|
||||
for d in "$HOME/.icons" "${data_dirs[@]/%//icons}"; do
|
||||
for f in "$d"/hicolor/*/*/"$1".{png,svg,xpm}; do
|
||||
ours "$f" || return 0
|
||||
ours "$f" || { echo "$f"; return 0; }
|
||||
done
|
||||
done
|
||||
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
|
||||
return 1
|
||||
}
|
||||
|
||||
# Largest Breeze app icon for <name> (Breeze's app icons are all SVG).
|
||||
breeze() {
|
||||
in_breeze() {
|
||||
local size
|
||||
for size in 64 48 32 24 22 16; do
|
||||
if [[ -e $BREEZE_APPS/$size/$1.svg ]]; then echo "$BREEZE_APPS/$size/$1.svg"; return; fi
|
||||
done
|
||||
for size in 64 48 32 24 22 16; do [[ -e $BREEZE_APPS/$size/$1.svg ]] && return 0; done
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -64,60 +64,29 @@ entry_icon() {
|
||||
' "$1"
|
||||
}
|
||||
|
||||
# Wanted: extra names, and the icons of the entries Steam sees (the first
|
||||
# entry with a given desktop-file ID wins, like in the menu).
|
||||
declare -A wanted=() seen=()
|
||||
if [[ -n ${BREEZE_APPS:-} ]]; then
|
||||
for name in "$@"; do wanted[$name]=extra; done
|
||||
for d in "${data_dirs[@]}"; do
|
||||
[[ -d $d/applications ]] || continue
|
||||
while IFS= read -r -d '' f; do
|
||||
id=${f#"$d/applications/"}; id=${id//\//-}
|
||||
[[ -z ${seen[$id]:-} ]] || continue
|
||||
seen[$id]=1
|
||||
icon=$(entry_icon "$f")
|
||||
[[ -z $icon ]] || wanted[$icon]=${wanted[$icon]:-$id}
|
||||
done < <(find -L "$d/applications" -name '*.desktop' -print0 2>/dev/null)
|
||||
done
|
||||
fi
|
||||
declare -A configured=() seen=() missing=()
|
||||
for name in "$@"; do configured[$name]=1; done
|
||||
|
||||
changed=
|
||||
linked=()
|
||||
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
|
||||
# The entries Steam sees (the first with a given desktop-file ID wins, like
|
||||
# in the menu).
|
||||
for d in "${data_dirs[@]}"; do
|
||||
[[ -d $d/applications ]] || continue
|
||||
while IFS= read -r -d '' f; do
|
||||
id=${f#"$d/applications/"}; id=${id//\//-}
|
||||
[[ -z ${seen[$id]:-} ]] || continue
|
||||
seen[$id]=1
|
||||
icon=$(entry_icon "$f")
|
||||
[[ -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
|
||||
|
||||
# Remove links from earlier runs that are no longer wanted.
|
||||
if [[ -f $manifest ]]; then
|
||||
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
|
||||
done < "$manifest"
|
||||
fi
|
||||
|
||||
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
|
||||
for icon in "${!missing[@]}"; do
|
||||
echo "icon fallbacks: \"$icon\" (${missing[$icon]}) shows without icon in Steam's \"+\" menu; Breeze has it: add it to steamFrame.launcherMenu.iconFallbacks.extra"
|
||||
done
|
||||
for name in "${!configured[@]}"; do
|
||||
if f=$(resolves "$name"); then
|
||||
echo "icon fallbacks: \"$name\" is in hicolor anyway ($f); the fallback is no longer needed"
|
||||
fi
|
||||
done
|
||||
exit 0
|
||||
Reference in new issue
Block a user