Files
steamify-cachyos/lib/vapor-theme.sh
T
theupriser b91fa4bc83 fix: Replace the question flow with a menu that turns components on and off
- Menu detects what is on and applies only the changes: SteamOS
  conversion, SteamOS theme, Steam Deck/Machine icons, single user mode,
  and Steam Machine support (only on Fremont hardware)
- Everything is reversible: KDE settings go through an undo journal
  (lib/state.sh), system files are restored from their backups
- Steam Machine support: LED driver, udev rule giving the user the LED
  files, steamos-manager for Steam's hardware settings; OpenRGB dropped
- Edit Plasma files only while plasmashell is stopped so it can't
  overwrite them
- README and AGENTS.md describe the menu, the component model and the
  test environment
2026-09-23 15:16:46 +02:00

270 lines
14 KiB
Bash

#!/bin/bash
# Valve's SteamOS Vapor desktop theme and SteamOS desktop defaults.
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own.
merge_kde_config() {
# Merge a KDE-style ini file (e.g. one of Valve's /etc/xdg defaults) into
# the user's own config (journaled, see lib/state.sh), so SteamOS defaults win over
# the distro's without touching package-owned files in /etc/xdg.
# Nested groups ("[A][B]") are supported; immutable "[$i]" groups and
# comments are skipped.
local src="$1" target="$2" line key value
local groups=""
local skip=0
while IFS= read -r line || [[ -n "$line" ]]; do
line="${line%$'\r'}"
[[ -z "$line" || "$line" == \#* ]] && continue
if [[ "$line" == \[* ]]; then
skip=0; groups=""
[[ "$line" == *'[$'* ]] && { skip=1; continue; }
local rest="$line"
while [[ "$rest" =~ ^\[([^]]*)\](.*)$ ]]; do
groups+="${groups:+|}${BASH_REMATCH[1]}"
rest="${BASH_REMATCH[2]}"
done
continue
fi
(( skip )) && continue
[[ "$line" == *=* ]] || continue
key="${line%%=*}"; value="${line#*=}"
kset theme "$target" "$groups" "$key" "$value"
done < "$src"
}
install_vapor_theme() {
# Installs Valve's official SteamOS "Vapor" Plasma theme (color scheme,
# Plasma look-and-feel package, wallpapers, icons) straight from Valve's
# own SteamOS package mirror, into the current user's ~/.local/share.
# This only touches the invoking user's home directory, never system
# files, so it's independent from everything else this script does.
local mirror="https://steamdeck-packages.steamos.cloud/archlinux-mirror"
local repo file_name sha256 tmp_dir
tmp_dir="$(mktemp -d)"
# Take the package from the newest SteamOS release repo (jupiter-3.9,
# jupiter-3.10, ...), read from its pacman database: that gives the
# exact file name and checksum. Fall back to a known-good version.
info "Looking up the current SteamOS Vapor presets..."
repo="$(curl -fsL "$mirror/" | grep -oE 'jupiter-[0-9]+\.[0-9]+/' | tr -d / | sort -V | tail -n 1)"
if [[ -n "$repo" ]] && curl -fsL "$mirror/$repo/os/x86_64/$repo.db" -o "$tmp_dir/repo.db"; then
local desc
desc="$(tar -tf "$tmp_dir/repo.db" 2>/dev/null | grep -E '^steamdeck-kde-presets-[0-9][^/]*/desc$' | head -n 1)"
if [[ -n "$desc" ]]; then
file_name="$(tar -xOf "$tmp_dir/repo.db" "$desc" | awk '/^%FILENAME%$/ { getline; print }')"
sha256="$(tar -xOf "$tmp_dir/repo.db" "$desc" | awk '/^%SHA256SUM%$/ { getline; print }')"
fi
fi
if [[ -z "${file_name:-}" ]]; then
warn "Couldn't read Valve's package index; using the known-good version."
repo="jupiter-3.9"
file_name="steamdeck-kde-presets-3.9.4-2-any.pkg.tar.zst"
sha256=""
fi
info "Downloading $file_name ($repo)..."
if ! curl -fL "$mirror/$repo/os/x86_64/$file_name" -o "${tmp_dir}/presets.tar.zst"; then
err "Download failed. Valve may have moved/renamed this package;"
err "check $mirror for the current version."
rm -rf "$tmp_dir"
return 1
fi
if [[ -n "$sha256" ]] && ! echo "$sha256 ${tmp_dir}/presets.tar.zst" | sha256sum -c --quiet -; then
err "Checksum mismatch for $file_name; not installing it."
rm -rf "$tmp_dir"
return 1
fi
info "Extracting package contents..."
if ! tar -I unzstd -xf "${tmp_dir}/presets.tar.zst" -C "$tmp_dir"; then
err "Extraction failed (is 'zstd' installed? try: sudo pacman -S --needed zstd)"
rm -rf "$tmp_dir"
return 1
fi
info "Creating local configuration directories..."
mkdir -p ~/.local/share/color-schemes \
~/.local/share/plasma/desktoptheme \
~/.local/share/plasma/look-and-feel \
~/.local/share/wallpapers \
~/.local/share/icons/hicolor
info "Installing Steam Deck theme assets..."
[[ -d "${tmp_dir}/usr/share/color-schemes" ]] && cp -r "${tmp_dir}/usr/share/color-schemes/"* ~/.local/share/color-schemes/
[[ -d "${tmp_dir}/usr/share/plasma/desktoptheme/Vapor" ]] && cp -r "${tmp_dir}/usr/share/plasma/desktoptheme/Vapor" ~/.local/share/plasma/desktoptheme/
[[ -d "${tmp_dir}/usr/share/plasma/look-and-feel/com.valve.vapor.desktop" ]] && cp -r "${tmp_dir}/usr/share/plasma/look-and-feel/com.valve.vapor.desktop" ~/.local/share/plasma/look-and-feel/
[[ -d "${tmp_dir}/usr/share/wallpapers" ]] && cp -r "${tmp_dir}/usr/share/wallpapers/"* ~/.local/share/wallpapers/
[[ -d "${tmp_dir}/usr/share/icons/hicolor" ]] && cp -r "${tmp_dir}/usr/share/icons/hicolor/"* ~/.local/share/icons/hicolor/
# The rest of what SteamOS ships: GTK theme (Steam, Firefox, ...), Konsole
# profile and user avatars -- the same files the AUR package installs.
mkdir -p ~/.local/share/themes ~/.local/share/konsole ~/.local/share/plasma/avatars
[[ -d "${tmp_dir}/usr/share/themes/Vapor" ]] && cp -r "${tmp_dir}/usr/share/themes/Vapor" ~/.local/share/themes/
[[ -d "${tmp_dir}/usr/share/konsole" ]] && cp -r "${tmp_dir}/usr/share/konsole/"* ~/.local/share/konsole/
[[ -d "${tmp_dir}/usr/share/plasma/avatars" ]] && cp -r "${tmp_dir}/usr/share/plasma/avatars/"* ~/.local/share/plasma/avatars/
# Valve's SteamOS desktop defaults (fonts, Steam keyboard window rule,
# no welcome screen, light file indexing, GTK theme).
# Left out on purpose: autostart entries (Steam is started by our user
# unit), sddm/profile.d, and files hardcoding /home/deck or SteamOS tools.
info "Applying SteamOS desktop defaults..."
local f
# kscreenlockerrc is left to the single-user question.
for f in kdeglobals kwinrc kwinrulesrc kded5rc baloofilerc kcminputrc; do
[[ -f "${tmp_dir}/etc/xdg/$f" ]] && merge_kde_config "${tmp_dir}/etc/xdg/$f" "$f"
done
[[ -f "${tmp_dir}/etc/xdg/kded5rc" ]] && merge_kde_config "${tmp_dir}/etc/xdg/kded5rc" kded6rc
kset theme konsolerc "Desktop Entry" DefaultProfile Vapor.profile
mkdir -p ~/.config/gtk-3.0 ~/.config/gtk-4.0
kset theme ~/.config/gtk-3.0/settings.ini Settings gtk-theme-name Vapor
kset theme ~/.config/gtk-4.0/settings.ini Settings gtk-theme-name Vapor
# Vapor is a dark theme: tell GTK, libadwaita and portal-aware apps
# (Firefox, Steam's web views, Flatpaks) to use their dark variants too.
kset theme ~/.config/gtk-3.0/settings.ini Settings gtk-application-prefer-dark-theme true
kset theme ~/.config/gtk-4.0/settings.ini Settings gtk-application-prefer-dark-theme true
if command -v gsettings >/dev/null 2>&1; then
[[ -n "$(state_get theme gsettings_scheme)" ]] ||
state_set theme gsettings_scheme "$(gsettings get org.gnome.desktop.interface color-scheme 2>/dev/null)"
[[ -n "$(state_get theme gsettings_gtk)" ]] ||
state_set theme gsettings_gtk "$(gsettings get org.gnome.desktop.interface gtk-theme 2>/dev/null)"
gsettings set org.gnome.desktop.interface color-scheme prefer-dark 2>/dev/null || true
gsettings set org.gnome.desktop.interface gtk-theme Vapor 2>/dev/null || true
fi
[[ -f "${tmp_dir}/etc/xdg/gtk-2.0/gtkrc" ]] && cp "${tmp_dir}/etc/xdg/gtk-2.0/gtkrc" ~/.gtkrc-2.0
info "Cleaning up temporary files..."
rm -rf "$tmp_dir"
# Remember what to go back to when the theme is turned off again.
[[ -n "$(state_get theme lookandfeel)" ]] ||
state_set theme lookandfeel "$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage \
--default "$(kreadconfig6 --file /etc/xdg/kdeglobals --group KDE --key LookAndFeelPackage --default org.kde.breeze.desktop)")"
[[ -n "$(state_get theme colorscheme)" ]] ||
state_set theme colorscheme "$(kreadconfig6 --file kdeglobals --group General --key ColorScheme --default BreezeLight)"
local appletsrc="plasma-org.kde.plasma.desktop-appletsrc" applet
# Same launcher icon SteamOS sets (Vapor Deck plasmoid setup script).
for applet in $(plasma_applets org.kde.plasma.kickoff); do
kset theme "$appletsrc" "Containments|${applet%%:*}|Applets|${applet#*:}|Configuration|General" icon distributor-logo-steamdeck
done
# SteamOS scales tray icons to the panel height. Valve's setup script
# targets Plasma 5's separate tray containment; in Plasma 6 the setting
# lives on the systemtray applet itself.
for applet in $(plasma_applets org.kde.plasma.systemtray); do
kset theme "$appletsrc" "Containments|${applet%%:*}|Applets|${applet#*:}|General" scaleIconsToFit true
done
# SteamOS shows the date below the time.
for applet in $(plasma_applets org.kde.plasma.digitalclock); do
kset theme "$appletsrc" "Containments|${applet%%:*}|Applets|${applet#*:}|Configuration|Appearance" dateDisplayFormat BelowTime
done
# Wallpaper: Valve ships them as flat JPGs in usr/share/wallpapers.
local wallpaper="$HOME/.local/share/wallpapers/Steam Deck Logo Default.jpg" containment
if [[ -f "$wallpaper" ]]; then
for containment in $(grep -oP '^\[Containments\]\[\K[0-9]+(?=\]$)' ~/.config/"$appletsrc" 2>/dev/null); do
grep -q "^\[Containments\]\[$containment\]\[Wallpaper\]" ~/.config/"$appletsrc" || continue
kset theme "$appletsrc" "Containments|$containment|Wallpaper|org.kde.image|General" Image "file://$wallpaper"
done
fi
# Vapor color scheme and Plasma style; the theme's own defaults use
# breeze-dark icons (there is no "Vapor" icon theme).
kset theme kdeglobals KDE LookAndFeelPackage com.valve.vapor.desktop
kset theme kdeglobals Icons Theme breeze-dark
kset theme kdeglobals General ColorScheme Vapor
kset theme plasmarc Theme name Vapor
# SteamOS uses a solid, full-width bottom panel at Plasma's stock 44px
# height (CachyOS ships a floating 30px one).
local panel
for panel in $(grep -oP '^\[PlasmaViews\]\[Panel \K[0-9]+(?=\]$)' ~/.config/plasmashellrc 2>/dev/null); do
kset theme plasmashellrc "PlasmaViews|Panel $panel" floating 0
kset theme plasmashellrc "PlasmaViews|Panel $panel|Defaults" thickness 44
done
gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor 2>/dev/null || true
kbuildsycoca6 --noincremental 2>/dev/null || true
# Live changes need a running Plasma session; otherwise they are picked
# up from the config files above at the next Plasma login.
if [[ "${PLASMASHELL_WAS_RUNNING:-false}" == true ]]; then
info "Applying the theme to the running Plasma session..."
lookandfeeltool -a com.valve.vapor.desktop >/dev/null 2>&1 || true
# lookandfeeltool leaves the distro's color scheme active; apply it explicitly.
plasma-apply-colorscheme Vapor >/dev/null 2>&1 || true
qdbus6 org.kde.kded6 /modules/gtkconfig org.kde.gtkconfig.setGtkTheme Vapor >/dev/null 2>&1 || true
qdbus6 org.kde.KWin /KWin org.kde.KWin.reconfigure >/dev/null 2>&1 || true
else
info "No running Plasma session; the theme applies at your next desktop login."
fi
ok "SteamOS desktop look installed."
}
theme_status() {
[[ "$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage)" == com.valve.vapor.desktop ]]
}
theme_enable() {
command -v curl >/dev/null 2>&1 || sudo pacman -S --needed --noconfirm curl
command -v unzstd >/dev/null 2>&1 || sudo pacman -S --needed --noconfirm zstd
# Stop plasmashell first: the panel and wallpaper edits go to files it
# would otherwise overwrite with its in-memory config on exit.
stop_plasmashell_for_edit
install_vapor_theme
local rc=$?
restart_plasmashell_if_stopped
(( rc == 0 )) || warn "Vapor theme install ran into a problem - see errors above."
return $rc
}
theme_disable() {
info "Restoring the previous desktop look..."
local lookandfeel colorscheme
lookandfeel="$(state_get theme lookandfeel org.kde.breeze.desktop)"
colorscheme="$(state_get theme colorscheme BreezeLight)"
stop_plasmashell_for_edit
if has_journal theme; then
krevert theme
else
# Installed by a version of this script without the undo journal:
# undo the main settings it made.
kwriteconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage --delete
kwriteconfig6 --file kdeglobals --group General --key ColorScheme --delete
kwriteconfig6 --file kdeglobals --group Icons --key Theme --delete
kwriteconfig6 --file plasmarc --group Theme --key name --delete
local f
for f in ~/.config/gtk-3.0/settings.ini ~/.config/gtk-4.0/settings.ini; do
[[ -f "$f" ]] || continue
kwriteconfig6 --file "$f" --group Settings --key gtk-theme-name --delete
kwriteconfig6 --file "$f" --group Settings --key gtk-application-prefer-dark-theme --delete
done
kwriteconfig6 --file konsolerc --group "Desktop Entry" --key DefaultProfile --delete
fi
if command -v gsettings >/dev/null 2>&1; then
local v
v="$(state_get theme gsettings_scheme)"
if [[ -n "$v" ]]; then gsettings set org.gnome.desktop.interface color-scheme "$v" 2>/dev/null
else gsettings reset org.gnome.desktop.interface color-scheme 2>/dev/null; fi
v="$(state_get theme gsettings_gtk)"
if [[ -n "$v" ]]; then gsettings set org.gnome.desktop.interface gtk-theme "$v" 2>/dev/null
else gsettings reset org.gnome.desktop.interface gtk-theme 2>/dev/null; fi
fi
rm -rf ~/.local/share/plasma/look-and-feel/com.valve.vapor.desktop \
~/.local/share/plasma/desktoptheme/Vapor \
~/.local/share/themes/Vapor \
~/.local/share/color-schemes/Vapor.colors ~/.local/share/color-schemes/VGUI.colors \
~/.local/share/konsole/Vapor.profile ~/.local/share/konsole/Vapor.colorscheme \
~/.local/share/wallpapers/Steam\ Deck\ Logo*.jpg ~/.local/share/wallpapers/Troll.jpg
rm -f ~/.gtkrc-2.0
kbuildsycoca6 --noincremental 2>/dev/null || true
if [[ -n "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ]]; then
lookandfeeltool -a "$lookandfeel" >/dev/null 2>&1 || true
plasma-apply-colorscheme "$colorscheme" >/dev/null 2>&1 || true
fi
restart_plasmashell_if_stopped
state_clear theme
ok "Previous desktop look restored."
}