mirror of
https://github.com/theupriser/steamify-cachyos.git
synced 2026-10-03 17:41:58 +02:00
fix: Fix setup bugs, add SteamOS desktop look and LED driver, split into modules
- Fix session switching: sync bridge only edits [Autologin], sync service no longer hits systemd's start limit, re-runs don't duplicate settings - Make the Return to Gaming Mode shortcut work without a password prompt - Steam desktop autostart only runs in Plasma, not inside gamescope - Vapor theme: fix install path, wallpaper, icon and color scheme; take over Valve's SteamOS defaults, GTK theme, panel layout and dark mode; always fetch the newest steamdeck-kde-presets with checksum verification - Steam Machine: install kernel headers and build/load the leds-valve driver reliably; ask before installing OpenRGB - Split the script into lib/ modules per responsibility - Rewrite README for end users with a technical details section; add AGENTS.md
This commit is contained in:
1 parent
1999b6ece5
commit
4ad6c2c04e
11 files changed
+1148
-732
No files matched your search
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
# Output helpers, prompts and small shared utilities.
|
||||
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own.
|
||||
|
||||
c_reset="\033[0m"; c_bold="\033[1m"; c_green="\033[32m"; c_yellow="\033[33m"; c_red="\033[31m"; c_cyan="\033[36m"
|
||||
|
||||
info() { echo -e "${c_cyan}[INFO]${c_reset} $*"; }
|
||||
ok() { echo -e "${c_green}[OK]${c_reset} $*"; }
|
||||
warn() { echo -e "${c_yellow}[WARN]${c_reset} $*"; }
|
||||
err() { echo -e "${c_red}[ERROR] $*${c_reset}" >&2; }
|
||||
ask_yn() {
|
||||
local prompt="$1" default="${2:-y}" reply
|
||||
local hint="[Y/n]"; [[ "$default" == "n" ]] && hint="[y/N]"
|
||||
read -rp "$(echo -e "${c_bold}${prompt}${c_reset} ${hint} ")" reply
|
||||
reply="${reply:-$default}"
|
||||
[[ "$reply" =~ ^[Yy]$ ]]
|
||||
}
|
||||
|
||||
require_root_helper() {
|
||||
# Re-exec any privileged step through sudo rather than requiring the
|
||||
# whole script to run as root, so $HOME/whoami stay correct for the
|
||||
# target user detection below.
|
||||
if [[ $EUID -eq 0 && -z "${SUDO_USER:-}" ]]; then
|
||||
err "Please run this script as your normal user (it will call sudo itself), not directly as root."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
backup_file() {
|
||||
local f="$1"
|
||||
if [[ -f "$f" && ! -f "${f}.bak-gamescope-wizard" ]]; then
|
||||
sudo cp -a "$f" "${f}.bak-gamescope-wizard"
|
||||
info "Backed up $f -> ${f}.bak-gamescope-wizard"
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
# "Return to Gaming Mode" desktop shortcut and the sudoers rule it needs.
|
||||
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own.
|
||||
|
||||
create_desktop_shortcut() {
|
||||
# Determine the home directory of the target user
|
||||
local user_home
|
||||
user_home="$(getent passwd "$TARGET_USER" | cut -d: -f6)"
|
||||
|
||||
# Locate the correct Desktop directory
|
||||
local desktop_dir
|
||||
if [[ -f "$user_home/.config/user-dirs.dirs" ]]; then
|
||||
desktop_dir=$(grep '^XDG_DESKTOP_DIR=' "$user_home/.config/user-dirs.dirs" | cut -d '"' -f 2)
|
||||
desktop_dir="${desktop_dir/\$HOME/$user_home}"
|
||||
fi
|
||||
desktop_dir="${desktop_dir:-$user_home/Desktop}"
|
||||
|
||||
mkdir -p "$desktop_dir"
|
||||
|
||||
# Let the shortcut restart the login manager without a password prompt
|
||||
# (it runs with Terminal=false, so sudo can't ask). Scoped to exactly
|
||||
# the two commands the shortcut needs, for this user only.
|
||||
local sudoers_tmp
|
||||
sudoers_tmp="$(mktemp)"
|
||||
cat > "$sudoers_tmp" << EOF
|
||||
$TARGET_USER ALL=(root) NOPASSWD: /usr/bin/systemctl start sync-steamos-session.service, /usr/bin/systemctl restart plasmalogin
|
||||
EOF
|
||||
if sudo visudo -cf "$sudoers_tmp" >/dev/null; then
|
||||
sudo install -m 0440 -o root -g root "$sudoers_tmp" /etc/sudoers.d/gamescope-session-switch
|
||||
ok "Installed /etc/sudoers.d/gamescope-session-switch for the shortcut."
|
||||
else
|
||||
warn "Generated sudoers rule failed validation; the shortcut will not be able to restart plasmalogin."
|
||||
fi
|
||||
rm -f "$sudoers_tmp"
|
||||
|
||||
# SteamOS itself uses Valve's "gaming-return" icon (Steam logo with a
|
||||
# return arrow; the Deck-style arrow is only used on Steam Deck
|
||||
# hardware). It comes with the Vapor theme; without it, use Steam's icon.
|
||||
local shortcut_icon="steam"
|
||||
[[ -f "$user_home/.local/share/icons/hicolor/scalable/actions/gaming-return.svg" ]] &&
|
||||
shortcut_icon="gaming-return"
|
||||
|
||||
# 2. Generate the .desktop shortcut with instant session switcher strings
|
||||
local shortcut_path="$desktop_dir/Return to Gaming Mode.desktop"
|
||||
info "Creating 'Return to Gaming Mode' desktop shortcut at: $shortcut_path"
|
||||
|
||||
cat << EOF > "$shortcut_path"
|
||||
[Desktop Entry]
|
||||
Name=Return to Gaming Mode
|
||||
Comment=Switch session back to Gamescope
|
||||
Exec=sh -c 'steamos-session-select gamescope && sudo -n /usr/bin/systemctl start sync-steamos-session.service && sudo -n /usr/bin/systemctl restart plasmalogin'
|
||||
Icon=$shortcut_icon
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=System;
|
||||
EOF
|
||||
|
||||
# Fix permissions for the target user and KDE Plasma desktop ecosystem
|
||||
chmod +x "$shortcut_path"
|
||||
|
||||
ok "Desktop shortcut created successfully."
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
# Valve Fremont (Steam Machine) front LED bar driver.
|
||||
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own.
|
||||
|
||||
detect_valve_fremont() {
|
||||
# Valve Steam Machine (Fremont) boards. The leds-valve driver's own DMI
|
||||
# table also matches the OEM/F7F strings some early units report, so
|
||||
# accept both -- otherwise we'd skip hardware the driver supports.
|
||||
local vendor product
|
||||
vendor="$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null || true)"
|
||||
product="$(cat /sys/class/dmi/id/product_name 2>/dev/null || true)"
|
||||
[[ "$vendor" == "Valve" && "$product" == "Fremont" ]] ||
|
||||
[[ "$vendor" == "OEM" && "$product" == "F7F" ]]
|
||||
}
|
||||
|
||||
install_kernel_headers() {
|
||||
# DKMS can only build leds-valve against kernels whose headers are
|
||||
# installed. CachyOS ships several kernels (linux-cachyos, -lts, -bore,
|
||||
# ...); install the matching -headers package for every one present.
|
||||
local -a kernels headers=()
|
||||
mapfile -t kernels < <(pacman -Qqo /usr/lib/modules/*/pkgbase 2>/dev/null | sort -u)
|
||||
local k
|
||||
for k in "${kernels[@]}"; do
|
||||
pacman -Si "${k}-headers" >/dev/null 2>&1 && headers+=("${k}-headers")
|
||||
done
|
||||
if [[ ${#headers[@]} -eq 0 ]]; then
|
||||
warn "Couldn't work out which kernel headers package you need."
|
||||
warn "Install the -headers package for your kernel (e.g. linux-cachyos-headers) yourself."
|
||||
return 1
|
||||
fi
|
||||
info "Making sure kernel headers are installed: ${headers[*]}"
|
||||
sudo pacman -S --needed --noconfirm "${headers[@]}"
|
||||
}
|
||||
|
||||
install_valve_led_driver() {
|
||||
# Installs the leds-valve DKMS driver from the AUR so the front LED
|
||||
# bar is exposed under /sys/class/leds/valve-leds[N].
|
||||
local aur_helper
|
||||
aur_helper="$(find_aur_helper)"
|
||||
if [[ -z "$aur_helper" ]]; then
|
||||
warn "No AUR helper available - can't install leds-valve-dkms-git."
|
||||
warn "Install one yourself, then run: yay -S leds-valve-dkms-git"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local -a flags
|
||||
read -ra flags <<< "$(aur_noninteractive_flags "$aur_helper")"
|
||||
|
||||
# Headers first: without them the package installs fine but DKMS
|
||||
# silently builds nothing, so modprobe later finds no module.
|
||||
install_kernel_headers || return 1
|
||||
|
||||
if ! pacman -Qi leds-valve-dkms-git >/dev/null 2>&1; then
|
||||
info "Installing leds-valve-dkms-git from the AUR via $aur_helper (non-interactive)..."
|
||||
if ! "$aur_helper" -S "${flags[@]}" leds-valve-dkms-git; then
|
||||
err "Installing leds-valve-dkms-git failed."
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
ok "leds-valve-dkms-git already installed."
|
||||
fi
|
||||
|
||||
# Build for the running kernel explicitly. The upstream Makefile uses
|
||||
# `uname -r` rather than DKMS's target kernel, so building for the
|
||||
# running kernel is the one case that reliably works.
|
||||
local kver
|
||||
kver="$(uname -r)"
|
||||
if ! dkms status -k "$kver" leds-valve-dkms 2>/dev/null | grep -q installed; then
|
||||
info "Building leds-valve for running kernel $kver..."
|
||||
if ! sudo dkms install leds-valve-dkms/0.1 -k "$kver"; then
|
||||
err "DKMS build failed. See the build log:"
|
||||
err " /var/lib/dkms/leds-valve-dkms/0.1/build/make.log"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Load now, and on every boot.
|
||||
echo leds-valve | sudo tee /etc/modules-load.d/leds-valve.conf >/dev/null
|
||||
|
||||
info "Loading the leds-valve kernel module..."
|
||||
if sudo modprobe leds-valve; then
|
||||
ok "Module loaded."
|
||||
else
|
||||
warn "modprobe leds-valve failed. Kernel messages:"
|
||||
sudo dmesg | grep -i 'valve' | tail -n 10
|
||||
fi
|
||||
|
||||
if compgen -G "/sys/class/leds/valve-leds*" >/dev/null; then
|
||||
ok "LED bar nodes detected: $(cd /sys/class/leds && echo valve-leds*)"
|
||||
else
|
||||
warn "No valve-leds nodes under /sys/class/leds/ yet. Reboot, then check with:"
|
||||
warn " ls /sys/class/leds/ | grep valve ; sudo dmesg | grep -i valve"
|
||||
fi
|
||||
|
||||
echo
|
||||
info "Steam only pushes download progress to the LED bar in real SteamOS Game Mode."
|
||||
if ask_yn "Also install the experimental OpenRGB build (openrgb-git), which can drive the LED bar?" n; then
|
||||
"$aur_helper" -S "${flags[@]}" openrgb-git &&
|
||||
ok "openrgb-git installed. Launch OpenRGB to detect and configure the front panel."
|
||||
fi
|
||||
}
|
||||
|
||||
setup_led_driver() {
|
||||
# Step 8c: only on Fremont hardware, and only if the user wants it.
|
||||
if detect_valve_fremont; then
|
||||
info "Detected Valve Fremont hardware (Steam Machine)."
|
||||
if ask_yn "Set up the front-panel LED bar driver (leds-valve) so it isn't stuck dark/breathing?" y; then
|
||||
ensure_aur_helper || warn "Couldn't set up an AUR helper automatically. You can install one yourself later and run: yay -S leds-valve-dkms-git"
|
||||
install_valve_led_driver || warn "LED driver setup ran into a problem - see errors above. Your gamescope boot setup above is unaffected."
|
||||
fi
|
||||
echo
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
#!/bin/bash
|
||||
# plasma-login-manager autologin into gamescope, and the session sync bridge.
|
||||
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own.
|
||||
|
||||
BASE_CONF="/etc/plasmalogin.conf"
|
||||
|
||||
check_display_manager() {
|
||||
# Step 1: warn when the login manager is not plasma-login-manager.
|
||||
local DM
|
||||
DM="$(systemctl show -p Id --value display-manager 2>/dev/null | sed 's/\.service$//')"
|
||||
info "Detected display manager: ${DM:-none}"
|
||||
|
||||
if [[ "$DM" != "plasmalogin" ]]; then
|
||||
warn "This wizard was built and tested against plasma-login-manager (plasmalogin)."
|
||||
warn "Detected display manager is '${DM:-unknown}'. SDDM setups don't need"
|
||||
warn "most of these workarounds (SDDM's own Autologin works out of the box);"
|
||||
warn "this script will still try, but review the output carefully."
|
||||
ask_yn "Continue anyway?" n || exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
configure_autologin() {
|
||||
# Steps 3-5: plasmalogin.conf.d, the base [Autologin] section, stray fragments.
|
||||
# 3. Ensure plasmalogin.conf.d exists (fixes Switch-to-Desktop crash)
|
||||
info "Ensuring /etc/plasmalogin.conf.d exists (fixes the 'Switching to Desktop' hang bug)"
|
||||
sudo mkdir -p /etc/plasmalogin.conf.d
|
||||
ok "Directory present."
|
||||
echo
|
||||
|
||||
# 4. Fix the base /etc/plasmalogin.conf
|
||||
info "Configuring $BASE_CONF (Session=gamescope-session.desktop, User=$TARGET_USER, Relogin=true)"
|
||||
backup_file "$BASE_CONF"
|
||||
|
||||
sudo touch "$BASE_CONF"
|
||||
|
||||
sudo awk '
|
||||
BEGIN { in_autologin=0 }
|
||||
/^\[Autologin\]/ { in_autologin=1; next }
|
||||
/^\[/ { in_autologin=0 }
|
||||
!in_autologin { print }
|
||||
' "$BASE_CONF" | sudo tee "${BASE_CONF}.tmp" > /dev/null || { err "Failed to rewrite $BASE_CONF."; exit 1; }
|
||||
|
||||
sudo mv "${BASE_CONF}.tmp" "$BASE_CONF"
|
||||
|
||||
sudo tee -a "$BASE_CONF" > /dev/null << EOF
|
||||
[Autologin]
|
||||
Session=gamescope-session.desktop
|
||||
User=$TARGET_USER
|
||||
Relogin=true
|
||||
EOF
|
||||
|
||||
ok "Base autologin config written:"
|
||||
sudo sed -n '/^\[Autologin\]/,$p' "$BASE_CONF"
|
||||
echo
|
||||
|
||||
# 5. Clean up any stray conf.d autologin fragments from manual attempts
|
||||
info "Removing any leftover manual conf.d autologin overrides (base config now handles it)"
|
||||
sudo rm -f /etc/plasmalogin.conf.d/zzz-steamos-autologin*.conf
|
||||
ok "Cleaned."
|
||||
echo
|
||||
}
|
||||
|
||||
install_session_sync() {
|
||||
# Steps 6-8: sync bridge conf.d -> base config, its systemd units, first run.
|
||||
# 6. Install the sync bridge: conf.d -> base config
|
||||
local SYNC_SCRIPT="/usr/local/bin/sync-steamos-session.sh"
|
||||
info "Installing session sync bridge at $SYNC_SCRIPT"
|
||||
|
||||
sudo tee "$SYNC_SCRIPT" > /dev/null << 'EOF'
|
||||
#!/bin/bash
|
||||
SRC="/etc/plasmalogin.conf.d/zz-steamos-autologin.conf"
|
||||
DEST="/etc/plasmalogin.conf"
|
||||
|
||||
[[ -f "$SRC" ]] || exit 0
|
||||
SESSION=$(grep -oP '^Session=\K.*' "$SRC")
|
||||
[[ -z "$SESSION" ]] && exit 0
|
||||
|
||||
# Only touch Session= inside [Autologin]; other sections may have their own.
|
||||
if sed -n '/^\[Autologin\]/,/^\[/p' "$DEST" | grep -q '^Session='; then
|
||||
sed -i "/^\[Autologin\]/,/^\[/ s|^Session=.*|Session=$SESSION|" "$DEST"
|
||||
elif grep -q '^\[Autologin\]' "$DEST"; then
|
||||
sed -i "/^\[Autologin\]/a Session=$SESSION" "$DEST"
|
||||
else
|
||||
printf '\n[Autologin]\nSession=%s\n' "$SESSION" >> "$DEST"
|
||||
fi
|
||||
EOF
|
||||
|
||||
sudo chmod +x "$SYNC_SCRIPT"
|
||||
ok "Sync script installed."
|
||||
echo
|
||||
|
||||
# 7. systemd path watcher + service
|
||||
info "Installing systemd path watcher so session switches take effect immediately"
|
||||
|
||||
sudo tee /etc/systemd/system/sync-steamos-session.path > /dev/null << 'EOF'
|
||||
[Unit]
|
||||
Description=Watch for steamos session changes
|
||||
|
||||
[Path]
|
||||
PathModified=/etc/plasmalogin.conf.d/zz-steamos-autologin.conf
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
sudo tee /etc/systemd/system/sync-steamos-session.service > /dev/null << 'EOF'
|
||||
[Unit]
|
||||
Description=Sync steamos session selection into plasmalogin.conf
|
||||
# Session switches can come in bursts (switch + autologin reset); never
|
||||
# let systemd's start rate limit silently stop the bridge.
|
||||
StartLimitIntervalSec=0
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/bin/sync-steamos-session.sh
|
||||
EOF
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now sync-steamos-session.path
|
||||
ok "Path watcher enabled."
|
||||
echo
|
||||
|
||||
# 8. Run it once now so current state is in sync
|
||||
info "Running the sync once now to align current state"
|
||||
sudo systemctl start sync-steamos-session.service
|
||||
ok "Done. Current base config:"
|
||||
sudo cat "$BASE_CONF"
|
||||
echo
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#!/bin/bash
|
||||
# Required packages and AUR helper handling.
|
||||
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own.
|
||||
|
||||
aur_noninteractive_flags() {
|
||||
# Fully non-interactive install flags for the given AUR helper: no
|
||||
# PKGBUILD diff/edit/cleanbuild prompts, no provider-selection prompts
|
||||
# (e.g. picking kernel headers for DKMS), and makepkg itself silenced.
|
||||
case "$1" in
|
||||
yay)
|
||||
echo --needed --noconfirm --answerclean None --answerdiff None \
|
||||
--answeredit None --mflags "--noconfirm"
|
||||
;;
|
||||
paru)
|
||||
echo --needed --noconfirm --skipreview --mflags "--noconfirm"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
find_aur_helper() {
|
||||
if command -v yay >/dev/null 2>&1; then
|
||||
echo yay
|
||||
elif command -v paru >/dev/null 2>&1; then
|
||||
echo paru
|
||||
fi
|
||||
}
|
||||
|
||||
bootstrap_yay() {
|
||||
# Builds and installs yay from the AUR using only makepkg + base-devel
|
||||
# (both from the official repos), so we have an AUR helper available
|
||||
# without assuming the user already set one up.
|
||||
info "Installing base-devel and git (needed to build an AUR helper)..."
|
||||
sudo pacman -S --needed --noconfirm base-devel git || return 1
|
||||
|
||||
local tmp_dir
|
||||
tmp_dir="$(mktemp -d)"
|
||||
info "Cloning yay-bin from the AUR..."
|
||||
if ! git clone --depth 1 https://aur.archlinux.org/yay-bin.git "$tmp_dir/yay-bin"; then
|
||||
err "Failed to clone yay-bin from the AUR."
|
||||
rm -rf "$tmp_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
info "Building and installing yay (this runs makepkg as your user, not root)..."
|
||||
if (cd "$tmp_dir/yay-bin" && makepkg -si --noconfirm); then
|
||||
ok "yay installed."
|
||||
rm -rf "$tmp_dir"
|
||||
return 0
|
||||
else
|
||||
err "Building yay failed. See errors above."
|
||||
rm -rf "$tmp_dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
install_required_packages() {
|
||||
# Step 2: gamescope/Steam and the desktop helpers the rest of the setup uses.
|
||||
local -a MISSING
|
||||
info "Checking required packages: gamescope-session-cachyos, steam, mangohud, xterm, ttf-liberation, wqy-zenhei, plasma-keyboard"
|
||||
mapfile -t MISSING < <(pacman -T gamescope-session-cachyos steam mangohud xterm ttf-liberation wqy-zenhei plasma-keyboard)
|
||||
|
||||
if [[ ${#MISSING[@]} -gt 0 ]]; then
|
||||
info "Missing packages: ${MISSING[*]}"
|
||||
if ask_yn "Install them now?"; then
|
||||
sudo pacman -S --needed --noconfirm "${MISSING[@]}" || { err "Package installation failed."; exit 1; }
|
||||
else
|
||||
err "Cannot continue without these packages."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
ok "All required packages already installed."
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_aur_helper() {
|
||||
# yay or paru, bootstrapping yay from the AUR when neither is installed.
|
||||
if [[ -n "$(find_aur_helper)" ]]; then
|
||||
ok "AUR helper already present."
|
||||
else
|
||||
info "No AUR helper (yay/paru) found. Installing yay..."
|
||||
bootstrap_yay
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
# Steam in the Plasma desktop session: gamepad UI args, virtual keyboard, autostart.
|
||||
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own.
|
||||
|
||||
setup_steam_desktop() {
|
||||
# Step 8b: always runs.
|
||||
info "Applying non-optional SteamOS environment & keyboard fixes..."
|
||||
local steam_env_dir gs_env systemd_user_dir
|
||||
steam_env_dir="$HOME/.config/environment.d"
|
||||
mkdir -p "$steam_env_dir"
|
||||
|
||||
# 1. Force Gamescope Steam to boot into Deck mode and load Deck glyphs
|
||||
echo "STEAM_GAMEPADUI_ARGS=\"-gamepadui -steamos3\"" > "$steam_env_dir/99-gamescope-steam-glyphs.conf"
|
||||
gs_env="$HOME/.config/gamescope-session/environment"
|
||||
if [[ -d "$HOME/.config/gamescope-session" ]]; then
|
||||
touch "$gs_env"
|
||||
sed -i '/^STEAM_GAMEPADUI_ARGS=/d' "$gs_env"
|
||||
echo "STEAM_GAMEPADUI_ARGS=\"-gamepadui -steamos3\"" >> "$gs_env"
|
||||
fi
|
||||
|
||||
# 2. Prevent KDE Wayland from blocking the virtual keyboard overlay
|
||||
echo "KWIN_IM_SHOW_ALWAYS=1" > "$steam_env_dir/99-kde-virtual-keyboard.conf"
|
||||
|
||||
# 3. Setup robust background systemd service for Steam autostart on Desktop Mode
|
||||
systemd_user_dir="$HOME/.config/systemd/user"
|
||||
mkdir -p "$systemd_user_dir"
|
||||
|
||||
cat << 'EOF' > "$systemd_user_dir/steam-desktop-autostart.service"
|
||||
[Unit]
|
||||
Description=Steam Background Autostart for Virtual Keyboard
|
||||
After=graphical-session.target
|
||||
PartOf=graphical-session.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# Only in the Plasma desktop session; gamescope-session starts its own Steam.
|
||||
ExecCondition=/bin/sh -c '[ "$XDG_CURRENT_DESKTOP" = KDE ]'
|
||||
ExecStart=/usr/bin/steam -silent
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
EOF
|
||||
|
||||
# Clean out old .desktop shortcut so they don't fight
|
||||
rm -f "$HOME/.config/autostart/steam.desktop"
|
||||
|
||||
# Reload user systemd context and activate the background loop
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now steam-desktop-autostart.service
|
||||
|
||||
ok "Steam UI parameters and keyboard autostart deployed successfully."
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
#!/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 with kwriteconfig6, 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 -a 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+=("--group" "${BASH_REMATCH[1]}")
|
||||
rest="${BASH_REMATCH[2]}"
|
||||
done
|
||||
continue
|
||||
fi
|
||||
(( skip )) && continue
|
||||
[[ "$line" == *=* ]] || continue
|
||||
key="${line%%=*}"; value="${line#*=}"
|
||||
kwriteconfig6 --file "$target" "${groups[@]}" --key "$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 screen locking, 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
|
||||
for f in kdeglobals kwinrc kwinrulesrc kscreenlockerrc 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
|
||||
kwriteconfig6 --file konsolerc --group "Desktop Entry" --key DefaultProfile Vapor.profile
|
||||
mkdir -p ~/.config/gtk-3.0 ~/.config/gtk-4.0
|
||||
kwriteconfig6 --file ~/.config/gtk-3.0/settings.ini --group Settings --key gtk-theme-name Vapor
|
||||
kwriteconfig6 --file ~/.config/gtk-4.0/settings.ini --group Settings --key 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.
|
||||
kwriteconfig6 --file ~/.config/gtk-3.0/settings.ini --group Settings --key gtk-application-prefer-dark-theme true
|
||||
kwriteconfig6 --file ~/.config/gtk-4.0/settings.ini --group Settings --key gtk-application-prefer-dark-theme true
|
||||
if command -v gsettings >/dev/null 2>&1; then
|
||||
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"
|
||||
|
||||
# Launcher icon
|
||||
# Same launcher icon SteamOS sets (Vapor Deck plasmoid setup script).
|
||||
local icon_name="distributor-logo-steamdeck"
|
||||
local conf_file="$HOME/.config/plasma-org.kde.plasma.desktop-appletsrc"
|
||||
|
||||
if ! find ~/.local/share/icons /usr/share/icons -iname "${icon_name}.*" 2>/dev/null | grep -q .; then
|
||||
warn "Could not find '${icon_name}' installed anywhere under ~/.local/share/icons or /usr/share/icons."
|
||||
warn "The launcher icon will be set anyway, but it may show as a broken icon until the file is installed."
|
||||
fi
|
||||
|
||||
if [[ -f "$conf_file" ]]; then
|
||||
info "Looking for the Application Launcher applet (Kickoff/Kicker) to set the custom icon..."
|
||||
local matches=()
|
||||
local current_section=""
|
||||
local current_containment=""
|
||||
local current_applet=""
|
||||
|
||||
while IFS= read -r line; do
|
||||
if [[ "$line" =~ ^\[Containments\]\[([0-9]+)\]\[Applets\]\[([0-9]+)\]$ ]]; then
|
||||
current_containment="${BASH_REMATCH[1]}"
|
||||
current_applet="${BASH_REMATCH[2]}"
|
||||
current_section="applet_root"
|
||||
continue
|
||||
fi
|
||||
if [[ "$line" =~ ^\[ ]]; then
|
||||
current_section=""
|
||||
continue
|
||||
fi
|
||||
if [[ "$current_section" == "applet_root" && "$line" =~ ^plugin=(org\.kde\.plasma\.(kickoff|kicker|simplemenu|homerun|application-menu))$ ]]; then
|
||||
matches+=( "${current_containment}:${current_applet}:${BASH_REMATCH[1]}" )
|
||||
current_section=""
|
||||
fi
|
||||
done < "$conf_file"
|
||||
|
||||
if [[ ${#matches[@]} -gt 0 ]]; then
|
||||
for m in "${matches[@]}"; do
|
||||
IFS=':' read -r containment applet plugin <<< "$m"
|
||||
info "Setting launcher icon on Containment $containment / Applet $applet ($plugin)..."
|
||||
kwriteconfig6 \
|
||||
--file "$conf_file" \
|
||||
--group Containments --group "$containment" \
|
||||
--group Applets --group "$applet" \
|
||||
--group Configuration --group General \
|
||||
--key icon "$icon_name"
|
||||
ok "Icon set to '$icon_name'."
|
||||
done
|
||||
else
|
||||
warn "No compatible Application Launcher applet found in panel config. Skipping icon assignment."
|
||||
fi
|
||||
else
|
||||
warn "$conf_file not found. Skipping menu icon configuration."
|
||||
fi
|
||||
|
||||
# SteamOS scales system 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.
|
||||
if [[ -f "$conf_file" ]]; then
|
||||
local tray
|
||||
for tray in $(awk '
|
||||
/^\[Containments\]\[[0-9]+\]\[Applets\]\[[0-9]+\]$/ { split($0, p, /[][]+/); sect = p[3] ":" p[5] }
|
||||
/^\[/ && !/\]\[Applets\]\[[0-9]+\]$/ { sect = "" }
|
||||
/^plugin=org\.kde\.plasma\.systemtray$/ && sect != "" { print sect }
|
||||
' "$conf_file"); do
|
||||
kwriteconfig6 --file "$conf_file" --group Containments --group "${tray%%:*}" \
|
||||
--group Applets --group "${tray#*:}" --group General \
|
||||
--key scaleIconsToFit true
|
||||
done
|
||||
fi
|
||||
|
||||
# SteamOS shows the date below the time in the panel clock.
|
||||
if [[ -f "$conf_file" ]]; then
|
||||
local clock
|
||||
for clock in $(awk '
|
||||
/^\[Containments\]\[[0-9]+\]\[Applets\]\[[0-9]+\]$/ { split($0, p, /[][]+/); sect = p[3] ":" p[5] }
|
||||
/^\[/ && !/\]\[Applets\]\[[0-9]+\]$/ { sect = "" }
|
||||
/^plugin=org\.kde\.plasma\.digitalclock$/ && sect != "" { print sect }
|
||||
' "$conf_file"); do
|
||||
kwriteconfig6 --file "$conf_file" --group Containments --group "${clock%%:*}" \
|
||||
--group Applets --group "${clock#*:}" --group Configuration --group Appearance \
|
||||
--key dateDisplayFormat BelowTime
|
||||
done
|
||||
fi
|
||||
|
||||
# Wallpaper
|
||||
# Valve ships the wallpapers as flat JPGs in usr/share/wallpapers.
|
||||
local wallpaper_path="$HOME/.local/share/wallpapers/Steam Deck Logo Default.jpg"
|
||||
|
||||
if [[ -f "$wallpaper_path" ]]; then
|
||||
info "Applying official Steam Deck Vapor wallpaper via D-Bus script..."
|
||||
local dbus_cmd="
|
||||
var allDesktops = desktops();
|
||||
for (var i = 0; i < allDesktops.length; i++) {
|
||||
var d = allDesktops[i];
|
||||
d.currentConfigGroup = ['Wallpaper', 'org.kde.image', 'General'];
|
||||
d.writeConfig('Image', 'file://${wallpaper_path}');
|
||||
}
|
||||
"
|
||||
if ! pgrep -u "$USER" -x plasmashell >/dev/null; then
|
||||
:
|
||||
elif command -v qdbus6 >/dev/null 2>&1; then
|
||||
qdbus6 org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript "$dbus_cmd" >/dev/null 2>&1 || true
|
||||
else
|
||||
qdbus org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript "$dbus_cmd" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
if [[ -f "$conf_file" ]]; then
|
||||
local desktop_sections=$(grep -E '^\[Containments\]\[[0-9]+\]$' "$conf_file" || true)
|
||||
while read -r section; do
|
||||
if [[ -n "$section" ]]; then
|
||||
local clean_sec=$(echo "$section" | tr -d '[]')
|
||||
kwriteconfig6 --file "$conf_file" --group "$clean_sec" --group Wallpaper --group org.kde.image --group General --key Image "file://${wallpaper_path}"
|
||||
fi
|
||||
done <<< "$desktop_sections"
|
||||
fi
|
||||
ok "Wallpaper path pushed to session config."
|
||||
else
|
||||
warn "Could not find extracted Vapor wallpaper file. Skipping."
|
||||
fi
|
||||
|
||||
# Activate the Vapor global theme and color scheme, then restart
|
||||
# plasmashell once so it picks everything up (restarting first races
|
||||
# with the settings being written). The theme's own defaults use
|
||||
# breeze-dark icons; there is no "Vapor" icon theme.
|
||||
kwriteconfig6 --file kdeglobals --group Icons --key Theme "breeze-dark"
|
||||
kwriteconfig6 --file kdeglobals --group General --key ColorScheme "Vapor"
|
||||
kwriteconfig6 --file plasmarc --group Theme --key name "Vapor"
|
||||
|
||||
# Same panel change for the next login, in case Plasma isn't running now.
|
||||
local panel
|
||||
for panel in $(grep -oP '^\[PlasmaViews\]\[Panel \K[0-9]+(?=\]$)' ~/.config/plasmashellrc 2>/dev/null); do
|
||||
kwriteconfig6 --file plasmashellrc --group PlasmaViews --group "Panel $panel" --key floating 0
|
||||
kwriteconfig6 --file plasmashellrc --group PlasmaViews --group "Panel $panel" --group Defaults --key 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 pgrep -u "$USER" -x plasmashell >/dev/null; 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.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript \
|
||||
'panels().forEach(function (p) {
|
||||
p.widgets("org.kde.plasma.systemtray").forEach(function (w) { w.currentConfigGroup = ["General"]; w.writeConfig("scaleIconsToFit", true); });
|
||||
p.widgets("org.kde.plasma.digitalclock").forEach(function (w) { w.currentConfigGroup = ["Appearance"]; w.writeConfig("dateDisplayFormat", "BelowTime"); });
|
||||
})' \
|
||||
>/dev/null 2>&1 || true
|
||||
# SteamOS uses a solid, full-width bottom panel at Plasma's stock
|
||||
# 44px height (CachyOS ships a floating 30px one).
|
||||
qdbus6 org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript \
|
||||
'panels().forEach(function (p) { p.floating = false; p.lengthMode = "fill"; p.height = 44; })' \
|
||||
>/dev/null 2>&1 || true
|
||||
sleep 2 # let plasmashell flush the scripted changes before replacing it
|
||||
rm -rf ~/.cache/plasmashell* ~/.cache/org.kde.dirmodel-qml.kcache
|
||||
qdbus6 org.kde.KWin /KWin org.kde.KWin.reconfigure >/dev/null 2>&1 || true
|
||||
# Restart through systemd so plasmashell keeps the session's environment
|
||||
# (platform theme etc.); a plain --replace from this shell may not.
|
||||
if systemctl --user cat plasma-plasmashell.service >/dev/null 2>&1; then
|
||||
# Also takes over from a stray instance started by an earlier --replace.
|
||||
systemctl --user stop plasma-plasmashell.service
|
||||
pkill -u "$USER" -x plasmashell && sleep 2
|
||||
systemctl --user start plasma-plasmashell.service
|
||||
else
|
||||
setsid plasmashell --replace >/dev/null 2>&1 &
|
||||
fi
|
||||
else
|
||||
info "No running Plasma session; the theme applies at your next desktop login."
|
||||
fi
|
||||
|
||||
ok "Vapor theme assets installed. Apply it under System Settings > Appearance"
|
||||
ok "> Global Theme > Vapor (Steam Deck), next time you're in a Plasma session."
|
||||
}
|
||||
|
||||
setup_vapor_theme() {
|
||||
# Step 9 (optional): ask, make sure curl/zstd exist, then install.
|
||||
echo
|
||||
if ask_yn "Also install Valve's official Vapor (Steam Deck) KDE theme for your desktop session?" n; then
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
warn "curl not found, installing it first..."
|
||||
sudo pacman -S --needed --noconfirm curl
|
||||
fi
|
||||
if ! command -v unzstd >/dev/null 2>&1; then
|
||||
warn "zstd not found, installing it first..."
|
||||
sudo pacman -S --needed --noconfirm zstd
|
||||
fi
|
||||
install_vapor_theme || warn "Vapor theme install ran into a problem - see errors above. Your gamescope boot setup above is unaffected."
|
||||
fi
|
||||
}
|
||||
Reference in new issue
Block a user