mirror of
https://github.com/lhns/steam-frame-nix.git
synced 2026-10-06 04:04:16 +02:00
launcher-menu: detect missing icon fallbacks on switch (iconFallbacks.enable/extra)
This commit is contained in:
1 parent
cfa9ca7e15
commit
8d7094916b
4 files changed
+229
-45
No files matched your search
@@ -195,7 +195,8 @@ menu list every desktop entry, including terminals such as Konsole.
|
||||
| `steamFrame.launcherMenu.grid.enable` | bool | `false` | Show the "+" menu's programs as a grid of tiles (large icon, name below) instead of a list. |
|
||||
| `steamFrame.launcherMenu.grid.columns` | int, 1-8 | `4` | Tiles per row (popup is 300 px wide: 3 ≈ 92 px, 4 ≈ 68 px, 5 ≈ 53 px tiles). |
|
||||
| `steamFrame.launcherMenu.grid.maxRows` | null or positive int | `null` | Rows visible at once, the rest scrolls. `null`: fill up to the menu's max height (600 px). |
|
||||
| `steamFrame.launcherMenu.iconFallbacks` | list of str | `[ "utilities-terminal" "preferences-system" ]` | Icon names installed as hicolor copies of Breeze app icons, so the "+" menu shows them (Konsole, KDE System Settings). `[ ]`: none. Takes effect after a Steam restart. |
|
||||
| `steamFrame.launcherMenu.iconFallbacks.enable` | bool | `true` | On every switch, link hicolor fallbacks (Breeze app icons) for desktop entries whose icon Steam can't find, so the "+" menu shows them (SteamOS: Konsole, KDE System Settings). `false` removes the links. See [Icon fallbacks](#icon-fallbacks-launchermenuiconfallbacks). |
|
||||
| `steamFrame.launcherMenu.iconFallbacks.extra` | list of str | `[ ]` | Further icon names to provide even if no desktop entry the scan sees uses them. |
|
||||
| `steamFrame.dashboard.windowMaxScale` | null or number | `null` | Largest resize-handle scale of SteamVR dashboard windows, relative to their default size. `null`: stock (2). See [Dashboard windows](#dashboard-windows-dashboard). |
|
||||
| `steamFrame.dashboard.windowDistance.{world,theater,dashboard}.{min,max}` | null or number (m) | `null` | How close / far grabbed windows can be pulled in / pushed back. `null`: stock (world 0.25-5, theater 1-6, dashboard 0.3-4 m). |
|
||||
| `steamFrame.dashboard.steamCloseButton.enable` | bool | `false` | Close (X) button on the dashboard's Steam window: switches to the previous window, or leaves just the dashboard bar. See [Steam close button](#steam-close-button-dashboardsteamclosebuttonenable). |
|
||||
@@ -551,19 +552,30 @@ desktop's icon theme. Konsole (`Icon=utilities-terminal`) and KDE System
|
||||
Settings (`Icon=preferences-system`), which SteamOS ships, have their icons
|
||||
only in Breeze, so they show up without an icon in the "+" menu.
|
||||
|
||||
**Fix:** for each name in `iconFallbacks` (default: those two), the largest
|
||||
Breeze app icon from nixpkgs' `kdePackages.breeze-icons` is installed as
|
||||
`~/.nix-profile/share/icons/hicolor/scalable/apps/<name>.svg`
|
||||
(`~/.nix-profile/share` is on Steam's `XDG_DATA_DIRS`); desktop entries are
|
||||
left alone. Add names for other programs that lack an icon (the `Icon=`
|
||||
value of their desktop entry), e.g.
|
||||
`iconFallbacks = [ "utilities-terminal" "preferences-system" "system-file-manager" ];`;
|
||||
`[ ]` installs nothing. A name Breeze has no app icon for fails the build
|
||||
with an error naming it.
|
||||
**Fix:** on every `home-manager switch`, a small activation script looks
|
||||
through the desktop entries Steam sees (`applications/` in
|
||||
`~/.local/share` and Steam's `XDG_DATA_DIRS`; an entry shadowed by one with
|
||||
the same desktop-file ID, and `Hidden`/`NoDisplay` entries, are skipped).
|
||||
For each `Icon=` name that no hicolor theme dir (or `pixmaps`) has but
|
||||
nixpkgs' `kdePackages.breeze-icons` app icons do, it links the largest
|
||||
Breeze SVG as `~/.local/share/icons/hicolor/scalable/apps/<name>.svg`.
|
||||
Desktop entries are left alone. `iconFallbacks.extra` adds names to
|
||||
provide even if no scanned entry uses them (a name Breeze lacks is
|
||||
reported and skipped). The links made are listed in
|
||||
`~/.local/state/steam-frame-nix/icon-fallbacks`; links no longer needed are
|
||||
removed, and `iconFallbacks.enable = false` removes them all. Only those
|
||||
links (still pointing into a breeze-icons store path) are ever removed;
|
||||
other files in the icon dir are never touched. Programs installed later get
|
||||
their fallback on the next switch.
|
||||
|
||||
**Caveat:** Steam caches the icon lookup (GTK icon cache) for its lifetime,
|
||||
and files in the Nix store all have the same 1970 mtime, so new icons show
|
||||
up only after Steam is restarted (e.g. a reboot), not on the next switch.
|
||||
`iconFallbacks` used to be a list of icon names; setting a list now fails
|
||||
evaluation with a message: use `iconFallbacks.extra` for additional names,
|
||||
`iconFallbacks.enable = false` for what `[ ]` meant.
|
||||
|
||||
A running Steam picks up the change without a restart: it caches the icon
|
||||
theme (GTK) and rescans it only when a theme directory's mtime changes, so
|
||||
the script bumps the mtime of `~/.local/share/icons/hicolor` whenever it
|
||||
adds or removes links.
|
||||
|
||||
### Dashboard windows (`dashboard.*`)
|
||||
|
||||
|
||||
+76
-32
@@ -23,32 +23,23 @@
|
||||
# iconFallbacks is not a patch: Steam's scan of host programs resolves a
|
||||
# desktop entry's Icon= name only in the hicolor icon theme (and pixmaps),
|
||||
# so programs whose icon exists only in the desktop's Breeze theme (SteamOS'
|
||||
# Konsole and KDE System Settings) have no icon in the menu. Hicolor copies
|
||||
# from nixpkgs' Breeze are installed into the profile (~/.nix-profile/share
|
||||
# is on Steam's XDG_DATA_DIRS).
|
||||
# Konsole and KDE System Settings) have no icon in the menu. On every switch,
|
||||
# launcher-menu/icon-fallbacks.sh looks through the desktop entries Steam
|
||||
# sees and links the missing icons that nixpkgs' Breeze has into
|
||||
# ~/.local/share/icons/hicolor (see the script). Until 2026-09 iconFallbacks
|
||||
# was a list of names; setting a list now fails with a message pointing to
|
||||
# enable/extra.
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.steamFrame.launcherMenu;
|
||||
inherit (import ./lib { inherit pkgs; }) mkPatch;
|
||||
sharedJSContext = { title = "SharedJSContext"; };
|
||||
|
||||
# Largest Breeze app icon (all are SVG) as hicolor/scalable/apps/<name>.svg.
|
||||
breezeApps = "${pkgs.kdePackages.breeze-icons}/share/icons/breeze/apps";
|
||||
iconFallbacks = pkgs.runCommand "launcher-menu-icon-fallbacks" { } ''
|
||||
dir=$out/share/icons/hicolor/scalable/apps
|
||||
mkdir -p $dir
|
||||
for name in ${lib.escapeShellArgs cfg.iconFallbacks}; do
|
||||
src=
|
||||
for size in 64 48 32 24 22 16; do
|
||||
if [ -e "${breezeApps}/$size/$name.svg" ]; then src="${breezeApps}/$size/$name.svg"; break; fi
|
||||
done
|
||||
if [ -z "$src" ]; then
|
||||
echo "steamFrame.launcherMenu.iconFallbacks: no Breeze app icon \"$name\" (${breezeApps}/<size>/$name.svg)" >&2
|
||||
exit 1
|
||||
fi
|
||||
cp -L "$src" "$dir/$name.svg"
|
||||
done
|
||||
'';
|
||||
iconFallbacks = pkgs.writeShellApplication {
|
||||
name = "steam-frame-icon-fallbacks";
|
||||
runtimeInputs = [ pkgs.coreutils pkgs.findutils pkgs.gawk ];
|
||||
text = builtins.readFile ./launcher-menu/icon-fallbacks.sh;
|
||||
};
|
||||
in {
|
||||
imports = [ ./steam-ui-patches.nix ];
|
||||
|
||||
@@ -122,23 +113,76 @@ in {
|
||||
};
|
||||
};
|
||||
iconFallbacks = lib.mkOption {
|
||||
type = lib.types.listOf (lib.types.strMatching "[A-Za-z0-9._+-]+");
|
||||
default = [ "utilities-terminal" "preferences-system" ];
|
||||
example = [ "utilities-terminal" "preferences-system" "system-file-manager" ];
|
||||
default = { };
|
||||
description = ''
|
||||
Icon names (Icon= of desktop entries) installed as hicolor icons,
|
||||
copied from nixpkgs' Breeze app icons, so the "+" menu shows them:
|
||||
Steam resolves program icons only in the hicolor theme, not in the
|
||||
desktop's Breeze theme. The default covers the entries SteamOS ships
|
||||
whose icons exist only in Breeze: Konsole (utilities-terminal) and
|
||||
KDE System Settings (preferences-system). A name Breeze has no app
|
||||
icon for fails the build, naming it. [] installs nothing. Steam
|
||||
picks up new icons only after a restart.
|
||||
Hicolor fallbacks for program icons only the desktop's Breeze theme
|
||||
has, so the "+" menu shows them: Steam resolves program icons only in
|
||||
the hicolor theme.
|
||||
'';
|
||||
# The option used to be a list of icon names: keep a list from being
|
||||
# silently misread and fail with a message instead (see the assertion).
|
||||
type = lib.types.coercedTo (lib.types.listOf lib.types.str)
|
||||
(names: { legacyList = names; })
|
||||
(lib.types.submodule {
|
||||
options = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
On every switch, look through the desktop entries Steam sees
|
||||
(XDG data dirs, shadowed and Hidden/NoDisplay entries skipped)
|
||||
and, for each Icon= name no hicolor theme dir (or pixmaps)
|
||||
has but nixpkgs' Breeze app icons do (SteamOS: Konsole's
|
||||
utilities-terminal, KDE System Settings' preferences-system),
|
||||
link the Breeze SVG as
|
||||
~/.local/share/icons/hicolor/scalable/apps/<name>.svg. Links
|
||||
no longer needed are removed; only links made by this option
|
||||
(listed in ~/.local/state/steam-frame-nix/icon-fallbacks) are
|
||||
ever touched. false removes them all.
|
||||
'';
|
||||
};
|
||||
extra = lib.mkOption {
|
||||
type = lib.types.listOf (lib.types.strMatching "[A-Za-z0-9._+-]+");
|
||||
default = [ ];
|
||||
example = [ "system-file-manager" ];
|
||||
description = ''
|
||||
Icon names to provide even if no desktop entry the scan sees
|
||||
uses them (still only if hicolor lacks them). A name Breeze
|
||||
has no app icon for is reported on switch and skipped.
|
||||
'';
|
||||
};
|
||||
legacyList = lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.listOf lib.types.str);
|
||||
default = null;
|
||||
internal = true;
|
||||
visible = false;
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
config.home.packages = lib.optional (cfg.iconFallbacks != [ ]) iconFallbacks;
|
||||
config.assertions = [ {
|
||||
assertion = cfg.iconFallbacks.legacyList == null;
|
||||
message = ''
|
||||
steamFrame.launcherMenu.iconFallbacks is no longer a list of icon names
|
||||
(set to ${builtins.toJSON cfg.iconFallbacks.legacyList}). Use
|
||||
iconFallbacks.enable = true; # default: utilities-terminal, preferences-system
|
||||
iconFallbacks.extra = [ ... ]; # further names
|
||||
or iconFallbacks.enable = false; for none (what [ ] used to mean).
|
||||
'';
|
||||
} ];
|
||||
|
||||
# After installPackages, so the new profile's desktop entries and icons count.
|
||||
# Disabled, the script removes its links (no BREEZE_APPS).
|
||||
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}
|
||||
'');
|
||||
|
||||
config.steamFrame.uiPatches.patches =
|
||||
lib.optional cfg.sort {
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
# 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 the Icon= names
|
||||
# of the desktop entries Steam sees (plus EXTRA_NAMEs) that no hicolor
|
||||
# theme dir has but Breeze does, as
|
||||
# $XDG_DATA_HOME/icons/hicolor/scalable/apps/<name>.svg -> Breeze's SVG.
|
||||
# BREEZE_APPS unset: remove all links made before (disabled).
|
||||
# Links made are listed in a manifest; only those, and only while they still
|
||||
# point into a breeze-icons store path, are ever removed. Nothing else in the
|
||||
# icon dir is touched.
|
||||
|
||||
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=(
|
||||
"$data_home"
|
||||
"$HOME/.local/share/flatpak/exports/share"
|
||||
/var/lib/flatpak/exports/share
|
||||
/usr/local/share
|
||||
/usr/share
|
||||
"$HOME/.nix-profile/share"
|
||||
/nix/var/nix/profiles/default/share
|
||||
)
|
||||
shopt -s nullglob
|
||||
|
||||
ours() { [[ -L $1 && $(readlink "$1") == /nix/store/*-breeze-icons-*/* ]]; }
|
||||
|
||||
# Does Steam already find <name> (hicolor in any data dir, or pixmaps)?
|
||||
# Our own links don't count.
|
||||
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
|
||||
done
|
||||
done
|
||||
for d in "${data_dirs[@]}"; do
|
||||
for f in "$d"/pixmaps/"$1".{png,svg,xpm}; do [[ ! -e $f ]] || return 0; done
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Largest Breeze app icon for <name> (Breeze's app icons are all SVG).
|
||||
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
|
||||
return 1
|
||||
}
|
||||
|
||||
# Icon= of a desktop entry, unless it is Hidden/NoDisplay or not a plain icon
|
||||
# name (e.g. an absolute path).
|
||||
entry_icon() {
|
||||
awk -F= '
|
||||
/^\[/ { group = ($0 ~ /^\[Desktop Entry\][[:space:]]*$/); next }
|
||||
!group { next }
|
||||
{ key = $1; sub(/[[:space:]]+$/, "", key); val = substr($0, index($0, "=") + 1)
|
||||
gsub(/^[[:space:]]+|[[:space:]\r]+$/, "", val) }
|
||||
(key == "Hidden" || key == "NoDisplay") && val == "true" { skip = 1 }
|
||||
key == "Icon" { icon = val }
|
||||
END { if (!skip && icon ~ /^[A-Za-z0-9._+-]+$/) print 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
|
||||
|
||||
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
|
||||
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
|
||||
@@ -26,6 +26,9 @@
|
||||
# closeOnLaunch = true;
|
||||
# launchDebounce = 10;
|
||||
# grid = { enable = true; columns = 4; maxRows = 4; };
|
||||
# # Icon fallbacks for programs without an icon in the menu are on by
|
||||
# # default; names to provide in addition to what the scan finds:
|
||||
# # iconFallbacks.extra = [ "system-file-manager" ];
|
||||
# };
|
||||
# # SteamVR dashboard windows: resizable up to 4x (stock 2x), pushed
|
||||
# # back up to 10 m in the world / 12 m in theater mode (stock 5 / 6 m):
|
||||
|
||||
Reference in new issue
Block a user