From 4ad6c2c04e1cdca7d4b735343e62254c6fe10099 Mon Sep 17 00:00:00 2001 From: Rick Peters Date: Wed, 23 Sep 2026 14:19:59 +0200 Subject: [PATCH] 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 --- AGENTS.md | 97 +++++ README.md | 351 +++++++++++------ icons/steamdeck-gaming-return.svg | 7 - lib/common.sh | 35 ++ lib/desktop-shortcut.sh | 62 +++ lib/led-driver.sh | 113 ++++++ lib/login-manager.sh | 129 ++++++ lib/packages.sh | 83 ++++ lib/steam-desktop.sh | 54 +++ lib/vapor-theme.sh | 323 +++++++++++++++ setup-gamescope-boot.sh | 626 ++---------------------------- 11 files changed, 1148 insertions(+), 732 deletions(-) create mode 100644 AGENTS.md delete mode 100644 icons/steamdeck-gaming-return.svg create mode 100644 lib/common.sh create mode 100644 lib/desktop-shortcut.sh create mode 100644 lib/led-driver.sh create mode 100644 lib/login-manager.sh create mode 100644 lib/packages.sh create mode 100644 lib/steam-desktop.sh create mode 100644 lib/vapor-theme.sh diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..8972698 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,97 @@ +# AGENTS.md + +Guidance for AI coding agents working on this repository. See `README.md` +for what the script does from a user's point of view. + +## Project overview + +A bash wizard that makes CachyOS (KDE Plasma 6 + `plasma-login-manager`) +boot into a SteamOS-style gamescope session, with working switching between +gamescope and the Plasma desktop. Primary target: the Valve Steam Machine +(DMI `Valve`/`Fremont`) running CachyOS Desktop edition. + +## Layout + +- `setup-gamescope-boot.sh` - the only entry point. Pre-flight checks, + sources `lib/*.sh`, then calls the steps in order. No step logic here. +- `lib/*.sh` - one file per responsibility, each defining functions only + (no top-level side effects). See the table in `README.md`. New + functionality goes in the module it belongs to, or a new module that is + added to the `source` loop in `setup-gamescope-boot.sh`. + +## Conventions + +- Bash, `set -uo pipefail` (no `-e`): check exit codes of steps that matter + explicitly (`|| { err ...; exit 1; }` or `|| return 1`). +- Use the helpers from `lib/common.sh` (`info`, `ok`, `warn`, `err`, + `ask_yn`, `backup_file`) for all output and prompts. +- Runs as the normal user; use `sudo` per command, never require root. + Per-user files go in `$HOME` of the invoking user (the script refuses to + configure another user). +- **Idempotent**: every step must be safe to re-run without duplicating + lines or settings. Back up system files with `backup_file` before the + first edit. +- Don't overwrite package-owned files in `/etc/xdg` or `/usr`. KDE settings + go into the user's config via `kwriteconfig6` (or `merge_kde_config` for + whole Valve ini files). +- Package installs after a user already confirmed use `--noconfirm`: a + second pacman prompt consumes the next scripted answer. +- Comments explain *why* (the CachyOS/Plasma quirk being worked around), + not what the next line does. + +## Non-obvious behaviour to preserve + +- `steam-set-session` only writes `/etc/plasmalogin.conf.d/zz-steamos-autologin.conf`; + the base `/etc/plasmalogin.conf` wins, hence the sync bridge. The sync + service needs `StartLimitIntervalSec=0`, or bursts of session switches + get it rate-limited and all later switches silently stop working. +- The sync script must only edit `Session=` inside `[Autologin]`. +- The Steam desktop autostart unit is guarded with + `ExecCondition=... XDG_CURRENT_DESKTOP = KDE`, so it doesn't start a + second Steam inside gamescope. +- Vapor theme: install Valve's files as shipped. Don't rewrite Valve's + `metadata.json`; the icon theme is `breeze-dark` (there is no "Vapor" + icon theme); wallpapers are flat JPGs in `usr/share/wallpapers`. + `lookandfeeltool` does not switch the color scheme, so + `plasma-apply-colorscheme Vapor` is applied explicitly. +- Plasma 6 has no separate systemtray containment: tray settings live on + the systemtray applet itself (Valve's setup script targets Plasma 5). +- Restart plasmashell via `systemctl --user` (`plasma-plasmashell.service`), + not `plasmashell --replace` from the script's shell: a shell without the + session environment yields a light-themed desktop (context menus, apps it + launches). +- Live Plasma changes (`qdbus6 ... evaluateScript`, `lookandfeeltool`) only + run when `plasmashell` is running; config-file fallbacks cover the rest. +- LED driver: `leds-valve-dkms-git`'s Makefile builds against `uname -r`, + so the script builds explicitly for the running kernel and installs + headers for every installed kernel first. The module creates + `/sys/class/leds/valve-leds*`. +- `Relogin=true` means a gamescope that fails to start is relaunched in a + tight loop; keep that in mind when changing session handling. + +## Checking changes + +There is no test suite. At minimum: + +```bash +for f in setup-gamescope-boot.sh lib/*.sh; do bash -n "$f"; done +shellcheck -S warning setup-gamescope-boot.sh lib/*.sh # if available +``` + +Behaviour is best verified in a CachyOS VM (QEMU/KVM, KDE Plasma install, +`plasma-login-manager`) with SSH access, reset from a disk snapshot between +runs. Notes from doing this: + +- Run the script non-interactively by piping answers, e.g. + `printf "\ny\nn\n" | ./setup-gamescope-boot.sh` (user, theme, reboot). + The prompt order depends on what's already installed. +- Over SSH, export the session environment before running it so the live + Plasma steps work: + `XDG_RUNTIME_DIR=/run/user/$UID WAYLAND_DISPLAY=wayland-0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$UID/bus`. +- `spectacle -b -n -f -o shot.png` takes a screenshot to check the look. + Apps you launch over SSH lack the session's Qt platform theme and look + light; launch them with `systemd-run --user` to judge colors correctly. +- Gamescope usually won't render in a VM (no suitable Vulkan), so test the + session logic with `plasma.desktop` as the autologin session. The + gamescope boot itself and the LED driver can only be verified on real + hardware. diff --git a/README.md b/README.md index 647fb1a..2f52008 100644 --- a/README.md +++ b/README.md @@ -1,172 +1,273 @@ # CachyOS Gamescope Boot Wizard -A setup script that makes a CachyOS desktop install (running KDE Plasma + -`plasma-login-manager`) always boot into a **Steam Deck-style gamescope -session** - the same "always boots into Big Picture, switch to desktop and -back on demand" experience SteamOS gives you on a real Deck or the Valve +Turn a CachyOS desktop PC into a SteamOS-style console: it boots straight +into Steam's Big Picture (gamescope), and you can switch to the KDE Plasma +desktop and back whenever you like - just like on a Steam Deck or Valve's Steam Machine. -It exists because, as of the CachyOS March 2026 release (which switched the -default login manager from SDDM to `plasma-login-manager`), the stock -`gamescope-session-cachyos` tooling has a few gaps that stop this from -working out of the box. This script patches around them. +Out of the box, CachyOS doesn't quite manage this: the switch to the desktop +can hang, and the PC doesn't reliably boot back into gaming mode. This +script fixes that and sets everything up for you. -## What's actually broken (and what this fixes) +## What you get -CachyOS's `gamescope-session-cachyos` package ships `steamos-session-select`, -which is meant to work exactly like it does on real SteamOS. On a -`plasma-login-manager` system, three things get in the way: - -1. **No `User=` written to the autologin config.** - `steam-set-session` (the script `steamos-session-select` calls under the - hood) only ever writes a `Session=` key to the autologin config file. On - SDDM this is fine, because SDDM's `User=` is set once by the installer and - never touched again. `plasma-login-manager` needs *both* `Session=` and - `User=` present to autologin at all - without `User=`, it just falls back - to showing the greeter every boot. - -2. **Missing `/etc/plasmalogin.conf.d` directory.** - If that directory doesn't exist, `steam-set-session` fails outright when - it tries to write to it. In practice this shows up as Steam's **"Switch to - Desktop"** hanging forever on a "Switching to Desktop" message with no way - back except a hard reset. - -3. **The base `/etc/plasmalogin.conf` wins over conf.d, and CachyOS ships it - hardcoded to `Session=plasma`.** - Every CachyOS tool that changes sessions (`steamos-session-select`, Steam's - power menu, and the `cachyos-gamescope-autologin.service` that's supposed - to reset you back to gamescope after a desktop session) only ever writes - to `/etc/plasmalogin.conf.d/zz-steamos-autologin.conf`. But the *base* - `/etc/plasmalogin.conf` file takes priority over that fragment, and it - ships with `Session=plasma` baked in - so none of those session switches - ever actually stick, no matter what the fragment says. - -This script fixes all three: it ensures the conf.d directory exists, sets the -base config's `Session=`, `User=`, and `Relogin=true` correctly, and installs -a small `systemd` path-watcher that keeps the base config in sync with -whatever CachyOS's own tools write to the conf.d fragment - so switching -sessions from inside Steam or the desktop actually works, in both -directions, and resets to gamescope on the next boot by default (matching -real Deck/SteamOS behavior). - -It also optionally offers to install Valve's official **Vapor** KDE Plasma -theme - the same colors, icons, wallpapers, and Plasma look-and-feel package -used on real SteamOS - pulled directly from Valve's own SteamOS package -mirror, so your desktop session matches the gamescope side visually. +- **Boots into gaming mode** automatically, without a login screen. +- **Switch to Desktop** from Steam's power menu works, and so does going + back: use the **Return to Gaming Mode** icon on the desktop, or just log + out. +- **Back to gaming mode after a restart**, like SteamOS. +- **Steam's on-screen keyboard** (Steam + X) also works on the desktop. +- *Optional:* the **SteamOS desktop look** - Valve's own Vapor theme, + wallpaper, dark mode and taskbar. +- *Optional, Steam Machine only:* a driver for the **front LED bar**. ## Requirements -- CachyOS (or an Arch-based system) with `pacman` -- KDE Plasma installed -- `plasma-login-manager` as the active display manager - *(the script will warn and ask for confirmation if it detects SDDM or - something else instead - most of these workarounds are specific to - `plasma-login-manager`)* -- A regular user account with `sudo` access +- CachyOS with the KDE Plasma desktop (the default CachyOS Desktop edition) +- Your normal user account, with permission to use `sudo` -## Usage +## Installation + +Open **Konsole** on your Plasma desktop and run: ```bash git clone cd -chmod +x setup-gamescope-boot.sh ./setup-gamescope-boot.sh ``` -Run it as your normal user, **not** as root - it calls `sudo` internally for -the specific commands that need it. +Run it as yourself, not as root. It asks for your password once, then asks a +few yes/no questions (whether to install missing packages, the SteamOS look, +and the LED driver on a Steam Machine). At the end it offers to restart. -The script will: +Keep the whole folder: the script needs the files in `lib/` next to it. -1. Ask which user account should autologin into gamescope (defaults to - whoever's running the script). -2. Check for and offer to install `gamescope-session-cachyos`, `steam`, and - `mangohud` if any are missing. -3. Create `/etc/plasmalogin.conf.d` if it doesn't exist. -4. Back up and rewrite `/etc/plasmalogin.conf`'s `[Autologin]` section. -5. Remove any stray manual override files from earlier troubleshooting - attempts (only files matching `zzz-steamos-autologin*.conf` - it never - touches `zz-steamos-autologin.conf`, which CachyOS's own tools own). -6. Install a sync script (`/usr/local/bin/sync-steamos-session.sh`) and a - `systemd` path unit that keeps the base config aligned with session - switches made through Steam or `steamos-session-select`. -7. Ask whether you'd also like to install Valve's **Vapor** (Steam Deck) KDE - theme. If you say yes, it downloads the official `steamdeck-kde-presets` - package straight from Valve's SteamOS mirror and installs the color - scheme, Plasma look-and-feel package, wallpapers, and icons into your - user's `~/.local/share`. This step only touches your home directory - - nothing system-wide - and installing `curl`/`zstd` first if either is - missing. Apply it afterwards from **System Settings > Appearance > - Global Theme > Vapor (Steam Deck)** the next time you're in a Plasma - session. +It's safe to run again later, for example after a CachyOS update. - If you keep a folder named `icons/` next to the script (e.g. custom Steam - Deck logo assets), it'll also copy those into the right Plasma icon - directories automatically. This is entirely optional - the theme installs - fine without it. -8. Ask whether to reboot immediately to test the gamescope setup. +## Using it -It's **safe to re-run** - every step checks current state first, and any -file it modifies gets backed up once with a `.bak-gamescope-wizard` suffix -before the first change. +- **To the desktop:** in gaming mode, open the Steam menu and choose + **Power > Switch to Desktop**. +- **Back to gaming mode:** double-click **Return to Gaming Mode** on the + desktop, or log out. +- After a restart you always start in gaming mode. -## After running it +Prefer to decide yourself where your PC starts? Run one of these in Konsole: -Boot straight into gamescope: it should happen automatically. From inside -gamescope, Steam's **Power > Switch to Desktop** should now work. From the -desktop, logging out should drop you back into gamescope automatically -(thanks to CachyOS's own `cachyos-gamescope-autologin.service`, which this -script doesn't replace - it just makes sure the setting it writes actually -takes effect). +```bash +steamos-session-select persistent # start where you left off last time +steamos-session-select oneshot # always start in gaming mode (default) +``` -You can also switch sessions manually at any time: +## If something goes wrong + +**Gaming mode shows a black screen or keeps restarting.** Press +**Ctrl+Alt+F3**, log in with your username and password, and run: + +```bash +steamos-session-select plasma && sudo systemctl restart plasmalogin +``` + +That takes you back to the desktop. + +**The LED bar on the Steam Machine stays dark.** Restart the PC once more; a +freshly installed driver often needs a reboot. Still dark? See +[Front LED bar](#front-led-bar-steam-machine) under technical details. + +**Undo the SteamOS look:** pick another theme under **System Settings > +Colors & Themes > Global Theme**. + +To remove everything the script set up, see +[Removing everything](#removing-everything). + +--- + +## Technical details + +### Why this is needed + +CachyOS's `gamescope-session-cachyos` package ships `steamos-session-select`, +which is meant to work exactly like it does on real SteamOS. Since the March +2026 release, CachyOS uses `plasma-login-manager` instead of SDDM, and three +things get in the way: + +1. **No `User=` written to the autologin config.** + `steam-set-session` (called by `steamos-session-select`) only writes a + `Session=` key. `plasma-login-manager` needs both `Session=` and `User=` + to autologin at all; without `User=` it shows the greeter every boot. + +2. **Missing `/etc/plasmalogin.conf.d` directory.** + Without it, `steam-set-session` fails, and Steam's "Switch to Desktop" + hangs forever. *(Fixed upstream in `gamescope-session-cachyos` 1.1.6; the + script still creates the directory for older versions.)* + +3. **The base `/etc/plasmalogin.conf` wins over conf.d, and CachyOS ships it + with `Session=plasma`.** + Every tool that changes sessions (`steamos-session-select`, Steam's power + menu, and `cachyos-gamescope-autologin.service`, which resets you to + gamescope after a desktop session) only writes + `/etc/plasmalogin.conf.d/zz-steamos-autologin.conf`. The base file takes + priority, so none of those switches stick. + +The script sets `Session=`, `User=` and `Relogin=true` in the base config, +and installs a small systemd path watcher that copies whatever CachyOS's +tools write to the conf.d fragment into the base config. + +### What the script changes + +In order: + +1. Checks the display manager (warns if it isn't `plasma-login-manager`) and + that it runs as the user that should autologin. +2. Installs missing packages: `gamescope-session-cachyos`, `steam`, + `mangohud`, `xterm`, `ttf-liberation`, `wqy-zenhei`, `plasma-keyboard`. +3. Creates `/etc/plasmalogin.conf.d`, backs up and rewrites the + `[Autologin]` section of `/etc/plasmalogin.conf`, and removes stray + `zzz-steamos-autologin*.conf` files from manual troubleshooting (never + `zz-steamos-autologin.conf`, which CachyOS's tools own). +4. Installs `/usr/local/bin/sync-steamos-session.sh` plus + `sync-steamos-session.path`/`.service`, which keep the base config in sync + with session switches. +5. Sets up Steam for the desktop: `STEAM_GAMEPADUI_ARGS="-gamepadui -steamos3"` + (gamepad UI with Steam Deck glyphs), `KWIN_IM_SHOW_ALWAYS=1` for the + virtual keyboard, and a `steam-desktop-autostart` systemd user service + that starts Steam silently in Plasma only. +6. *(Steam Machine, optional)* the LED driver - see below. +7. *(Optional)* the SteamOS desktop look - see below. +8. Creates the **Return to Gaming Mode** shortcut, with a narrow sudoers rule + (`/etc/sudoers.d/gamescope-session-switch`) so it can restart the login + manager without a password prompt. + +Re-running doesn't duplicate any settings. Every system file it modifies is +backed up once, next to the original, with a `.bak-gamescope-wizard` suffix. + +### SteamOS desktop look + +Downloads Valve's official `steamdeck-kde-presets` package from Valve's +SteamOS mirror and installs it into `~/.local/share` - nothing system-wide: + +- the Vapor global theme, color scheme, Plasma style, wallpapers and icons; +- the Vapor GTK theme, Konsole profile and user avatars; +- Valve's SteamOS desktop defaults, merged into your own config: Noto Sans + fonts, no Xwayland input-permission prompt for Steam Input, the window + rule that keeps the Steam keyboard on top and out of the taskbar, no + screen locking, no welcome screen, light file indexing; +- the SteamOS panel: solid (not floating), full width, 44 px high, tray icons + scaled to fit, date below the time, and the SteamOS launcher icon; +- dark mode for GTK, libadwaita and portal-aware apps (Firefox, Flatpaks). + +Run inside a Plasma session, it applies everything live; otherwise it takes +effect at the next desktop login. + +**Package version.** The script always takes the newest +`steamdeck-kde-presets` from the highest-numbered SteamOS release repository +on the mirror (`jupiter-3.9`, `jupiter-3.10`, ...). That repository's pacman +database gives the exact file name and SHA-256 checksum, which is verified +after downloading. If the lookup fails, it falls back to the known-good +3.9.4 (see `install_vapor_theme()` in `lib/vapor-theme.sh`). To check the +current version yourself: + +```bash +m=https://steamdeck-packages.steamos.cloud/archlinux-mirror +r=$(curl -fsL $m/ | grep -oE 'jupiter-[0-9]+\.[0-9]+/' | tr -d / | sort -V | tail -n 1) +curl -fsL $m/$r/os/x86_64/$r.db | bsdtar -tf - | grep -o '^steamdeck-kde-presets-[^/]*' | sort -u +``` + +### Front LED bar (Steam Machine) + +Standard desktop kernels, including CachyOS's, lack Valve's `leds-valve` +driver, so the Steam Machine's front LED bar goes dark or "breathes". On +Fremont hardware (DMI `Valve`/`Fremont`) the script offers to: + +- install an AUR helper (`yay`) if neither `yay` nor `paru` is present; +- install the kernel headers for every installed kernel; +- install `leds-valve-dkms-git` from the AUR, build it for the running + kernel, and load it at every boot (`/etc/modules-load.d/leds-valve.conf`); +- optionally install the experimental `openrgb-git`, which can drive the bar. + +The LEDs appear as `/sys/class/leds/valve-leds*`. Steam itself only drives +the bar (e.g. download progress) in real SteamOS Game Mode. + +The driver's Makefile builds against the running kernel, so after a kernel +update, boot the new kernel and re-run the script (or +`sudo dkms install leds-valve-dkms/0.1 -k "$(uname -r)"`). + +Troubleshooting: + +```bash +dkms status +ls /sys/class/leds | grep valve +sudo dmesg | grep -i valve +cat /var/lib/dkms/leds-valve-dkms/0.1/build/make.log +``` + +### Manual session control ```bash steamos-session-select gamescope # switch to gamescope right now steamos-session-select plasma # switch to desktop right now -steamos-session-select persistent # remember whichever session you used last, across reboots -steamos-session-select oneshot # always start in gamescope regardless of what you used last (the default Deck-like behavior) +steamos-session-select persistent # remember the last-used session across reboots +steamos-session-select oneshot # always start in gamescope (default, like a Deck) ``` -## Restoring the originals +With `Relogin=true`, a gamescope session that fails to start is restarted +immediately, which can turn into a loop - hence the Ctrl+Alt+F3 escape above. -Every file the script modifies is backed up next to itself: +### Removing everything ```bash +# restore the original login config sudo cp /etc/plasmalogin.conf.bak-gamescope-wizard /etc/plasmalogin.conf -``` -To fully remove the sync watcher this script adds: - -```bash +# session sync watcher sudo systemctl disable --now sync-steamos-session.path sudo rm /etc/systemd/system/sync-steamos-session.path sudo rm /etc/systemd/system/sync-steamos-session.service sudo rm /usr/local/bin/sync-steamos-session.sh sudo systemctl daemon-reload + +# shortcut and its sudoers rule +sudo rm /etc/sudoers.d/gamescope-session-switch +rm ~/Desktop/"Return to Gaming Mode.desktop" + +# Steam desktop autostart and environment +systemctl --user disable --now steam-desktop-autostart.service +rm ~/.config/systemd/user/steam-desktop-autostart.service +rm ~/.config/environment.d/99-gamescope-steam-glyphs.conf \ + ~/.config/environment.d/99-kde-virtual-keyboard.conf + +# LED driver (Steam Machine) +sudo rm /etc/modules-load.d/leds-valve.conf +sudo pacman -R leds-valve-dkms-git ``` -## Caveats +### Project layout -- This works around current CachyOS/`plasma-login-manager` behavior as of - September 2026. If CachyOS fixes `steam-set-session` upstream to write - `User=` and to target whichever base config the login manager actually - reads, this script becomes unnecessary - check for updates before running - it on a freshly reinstalled system. -- Tested on a Valve Steam Machine (desktop AMD APU + discrete GPU) running - CachyOS Desktop edition. Should apply to any CachyOS desktop install using - `plasma-login-manager`, but hasn't been tested on Steam Deck/Legion Go - hardware, which typically ship with SDDM instead. -- If you find CachyOS has patched the underlying issue, please open an issue - or PR here so the script can detect that and skip the workaround. -- The Vapor theme step depends on Valve's `steamdeck-kde-presets` package - staying available at its current URL and version - (`steamdeck-kde-presets-0.29-1-any.pkg.tar.zst` on - `steamdeck-packages.steamos.cloud`). If Valve ships a newer version or - changes the mirror layout, update `pkg_ver` near the top of - `install_vapor_theme()` in the script. +| File | Responsibility | +|---|---| +| `setup-gamescope-boot.sh` | Entry point: user and sudo checks, step order, summary, reboot | +| `lib/common.sh` | Output helpers, yes/no prompts, backups | +| `lib/packages.sh` | Required packages, AUR helper (yay/paru) | +| `lib/login-manager.sh` | plasmalogin autologin, session sync bridge and its systemd units | +| `lib/steam-desktop.sh` | Steam in the Plasma session: gamepad UI flags, virtual keyboard, autostart | +| `lib/led-driver.sh` | Fremont detection, kernel headers, LED driver, OpenRGB | +| `lib/vapor-theme.sh` | Vapor theme, Valve's SteamOS defaults, panel, dark mode | +| `lib/desktop-shortcut.sh` | Return to Gaming Mode shortcut and its sudoers rule | -## Related upstream reports +Notes for contributors and AI coding agents are in [`AGENTS.md`](AGENTS.md). + +### Caveats + +- This works around CachyOS/`plasma-login-manager` behavior as of September + 2026. If CachyOS fixes `steam-set-session` upstream, parts of this script + become unnecessary - please open an issue or PR if you notice that. +- Tested on a Valve Steam Machine running CachyOS Desktop edition. It should + work on any CachyOS desktop install using `plasma-login-manager`, but + hasn't been tested on Steam Deck/Legion Go hardware, which typically use + SDDM. With another display manager the script warns and asks before + continuing. + +### Related upstream reports - [CachyOS/gamescope-session#9](https://github.com/CachyOS/gamescope-session/issues/9) - "Switch to Desktop" hang due to missing `/etc/plasmalogin.conf.d` diff --git a/icons/steamdeck-gaming-return.svg b/icons/steamdeck-gaming-return.svg deleted file mode 100644 index 5cff1ce..0000000 --- a/icons/steamdeck-gaming-return.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/lib/common.sh b/lib/common.sh new file mode 100644 index 0000000..3dc9679 --- /dev/null +++ b/lib/common.sh @@ -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 +} diff --git a/lib/desktop-shortcut.sh b/lib/desktop-shortcut.sh new file mode 100644 index 0000000..42c9b87 --- /dev/null +++ b/lib/desktop-shortcut.sh @@ -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." +} diff --git a/lib/led-driver.sh b/lib/led-driver.sh new file mode 100644 index 0000000..37271d6 --- /dev/null +++ b/lib/led-driver.sh @@ -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 +} diff --git a/lib/login-manager.sh b/lib/login-manager.sh new file mode 100644 index 0000000..90ab74a --- /dev/null +++ b/lib/login-manager.sh @@ -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 +} diff --git a/lib/packages.sh b/lib/packages.sh new file mode 100644 index 0000000..85a60c5 --- /dev/null +++ b/lib/packages.sh @@ -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 +} diff --git a/lib/steam-desktop.sh b/lib/steam-desktop.sh new file mode 100644 index 0000000..deabc51 --- /dev/null +++ b/lib/steam-desktop.sh @@ -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." +} diff --git a/lib/vapor-theme.sh b/lib/vapor-theme.sh new file mode 100644 index 0000000..4dcbd5e --- /dev/null +++ b/lib/vapor-theme.sh @@ -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 +} diff --git a/setup-gamescope-boot.sh b/setup-gamescope-boot.sh index ddfa5f1..7c4a908 100755 --- a/setup-gamescope-boot.sh +++ b/setup-gamescope-boot.sh @@ -31,7 +31,7 @@ # desktop side matches the gamescope side visually. # # On Valve Fremont hardware (DMI sys_vendor=Valve, product_name=Fremont -- -# i.e. Steam Machine / BC-250-class boards), also offers to install the +# i.e. the Steam Machine), also offers to install the # leds-valve DKMS driver from the AUR so the front LED bar is exposed under # /sys/class/leds instead of sitting dark or "breathing" under a standard # desktop kernel, plus the option to pull in an experimental OpenRGB build @@ -39,385 +39,12 @@ set -uo pipefail -# ---------- helpers ---------- +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -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 -} - -create_desktop_shortcut() { - # Determine the home directory of the target user - local user_home - user_home=$(eval echo "~$TARGET_USER") - - # 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}" - - # Ensure secure icon path and desktop directories exist - local secure_icon_dir="$user_home/.local/share/icons/hicolor/scalable/apps" - mkdir -p "$secure_icon_dir" - mkdir -p "$desktop_dir" - - # 1. Safely copy the icon to the user's permanent theme directory - local script_dir - script_dir="$(cd "$(dirname "${BASH_SOURCE}")" && pwd)" - - if [[ -f "$script_dir/icons/steamdeck-gaming-return.svg" ]]; then - info "Copying icon asset to permanent system theme path..." - cp "$script_dir/icons/steamdeck-gaming-return.svg" "$secure_icon_dir/steamdeck-gaming-return.svg" - chown "$TARGET_USER:$TARGET_USER" "$secure_icon_dir/steamdeck-gaming-return.svg" - else - warn "Icon asset not found at $script_dir/icons/steamdeck-gaming-return.svg - Shortcut will use fallback fallback." - fi - - # 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=steamos-session-select gamescope && sudo systemctl start sync-steamos-session.service && sudo systemctl restart plasmalogin -Icon=steamdeck-gaming-return -Terminal=false -Type=Application -Categories=System; -EOF - - # Fix permissions for the target user and KDE Plasma desktop ecosystem - chmod +x "$shortcut_path" - chown "$TARGET_USER:$TARGET_USER" "$shortcut_path" - - # Refresh the system icon cache so Plasma detects the standalone asset immediately - gtk-update-icon-cache -f -t "$user_home/.local/share/icons/hicolor" 2>/dev/null || true - - ok "Desktop shortcut created successfully." -} - -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 pkg_ver="0.29" - local domain="https://steamdeck-packages.steamos.cloud" - local path_dir="archlinux-mirror/jupiter-main/os/x86_64" - local file_name="steamdeck-kde-presets-${pkg_ver}-1-any.pkg.tar.zst" - local url="${domain}/${path_dir}/${file_name}" - local tmp_dir - tmp_dir="$(mktemp -d)" - - info "Downloading official SteamOS Vapor presets (v${pkg_ver})..." - if ! curl -fL "$url" -o "${tmp_dir}/presets.tar.zst"; then - err "Download failed. Valve may have moved/renamed this package;" - err "check ${domain} for the current version and update pkg_ver in the script." - 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/scalable/apps ~/.local/share/icons/hicolor/48x48/apps - - 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/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/ - - if [[ -d "./icons" ]]; then - info "Copying local Steam Deck logo assets from ./icons into structured icon paths..." - cp -r ./icons/* ~/.local/share/icons/hicolor/scalable/apps/ 2>/dev/null || true - cp -r ./icons/* ~/.local/share/icons/hicolor/48x48/apps/ 2>/dev/null || true - fi - - local metadata_dir="$HOME/.local/share/plasma/look-and-feel/com.valve.vapor.desktop" - if [[ -d "$metadata_dir" ]]; then - info "Patching look-and-feel metadata for Plasma 6 compatibility..." - mkdir -p "$metadata_dir/contents" - echo '{"KPlugin": {"Id": "com.valve.vapor.desktop", "Name": "Vapor (Steam Deck)", "ServiceTypes": ["Plasma/LookAndFeel"]}}' > "$metadata_dir/metadata.json" - fi - - info "Cleaning up temporary files..." - rm -rf "$tmp_dir" - - # Installs Valve's official SteamOS "Vapor" Plasma theme automatically - # from the AUR without requiring user interactions or prompts. - info "Installing official SteamOS Vapor theme via yay (non-interactive)..." - - if ! yay -S --noconfirm --needed --answerclean None --answerdiff None --answeredit None plasma6-themes-vapor-steamos; then - err "Failed to install plasma6-themes-vapor-steamos package from the AUR." - return 1 - fi - - # ---------- Integrated: Update Launcher Icon ---------- - local icon_name="steamdeck-gaming-return" - 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 - - # ---------- Integrated: Set Default Vapor Wallpaper (Fixed for Plasma 6) ---------- - local wallpaper_path="$HOME/.local/share/wallpapers/Vapor/contents/images/2560x1600.png" - - if [[ ! -f "$wallpaper_path" ]]; then - wallpaper_path=$(find "$HOME/.local/share/wallpapers/Vapor/contents/images" -type f \( -name "*.png" -o -name "*.jpg" \) | head -n 1) - fi - - 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 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 - - info "Refreshing Plasma environment and icon caches..." - gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor 2>/dev/null || true - kbuildsycoca6 --noincremental 2>/dev/null || true - rm -rf ~/.cache/plasmashell* ~/.cache/org.kde.dirmodel-qml.kcache - setsid plasmashell --replace >/dev/null 2>&1 & - - # ---------- Automatisch het Vapor thema activeren ---------- - # info "Vapor-thema direct toepassen op de huidige desktop..." - - # Activeer het globale Plasma look-and-feel thema - if command -v lookandfeeltool >/dev/null 2>&1; then - lookandfeeltool -a com.valve.vapor.desktop || true - fi - - # Zorg dat de iconen en kleurschema's expliciet naar Vapor overschakelen - kwriteconfig6 --file kdeglobals --group Icons --key Theme "Vapor" - kwriteconfig6 --file kdeglobals --group General --key ColorScheme "Vapor" - - # Forceer Plasma om de nieuwe instellingen direct in te laden - qdbus6 org.kde.KWin /KWin org.kde.KWin.reconfigure >/dev/null 2>&1 || true - - 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." -} - - -detect_valve_fremont() { - # Valve Steam Machine / BC-250-class boards report these DMI strings. - # Standard desktop kernels (including CachyOS's) lack the leds-valve - # driver, so the front LED bar just goes dark or breathes on idle. - 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" ]] -} - -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 -} - -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_valve_led_driver() { - # Installs the leds-valve DKMS driver from the AUR so the front LED - # bar is exposed under /sys/class/leds, then offers the two known - # ways to get Steam to actually drive it (gamepadui, which this - # script already forces via STEAM_GAMEPADUI_ARGS, or OpenRGB's - # experimental build which has native support for the panel). - # Assumes an AUR helper was already ensured back in step 2. - local aur_helper="" - if command -v yay >/dev/null 2>&1; then - aur_helper="yay" - elif command -v paru >/dev/null 2>&1; then - aur_helper="paru" - fi - - 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 - - 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)..." - local -a flags - read -ra flags <<< "$(aur_noninteractive_flags "$aur_helper")" - "$aur_helper" -S "${flags[@]}" leds-valve-dkms-git - else - ok "leds-valve-dkms-git already installed." - fi - - info "Loading the leds-valve kernel module..." - if sudo modprobe leds-valve 2>/dev/null; then - ok "Module loaded." - else - warn "modprobe leds-valve failed - a reboot is often needed to pick up a freshly built DKMS module." - fi - - if ls /sys/class/leds/ 2>/dev/null | grep -qi valve; then - ok "LED bar nodes detected: $(ls /sys/class/leds/ | grep -i valve | tr '\n' ' ')" - else - warn "No valve-led nodes under /sys/class/leds/ yet. Reboot, then check with:" - warn " ls /sys/class/leds/ | grep -i valve" - fi - - echo - info "Steam only pushes download progress to the LED bar in real SteamOS Game Mode." - # info "On a desktop install you have two options:" - # info " 1) Launch Steam's Big Picture UI (this script's -gamepadui -steamos3 flag already covers this)" - # info " 2) Install an experimental/git OpenRGB build from the AUR, which natively detects this panel" - # if ask_yn "Also install the experimental OpenRGB build from the AUR now?" n; then - local -a flags - read -ra flags <<< "$(aur_noninteractive_flags "$aur_helper")" - "$aur_helper" -S "${flags[@]}" openrgb-git - # ok "openrgb-git installed. Launch OpenRGB to detect and configure the front panel." - # fi -} - - -# ---------- start ---------- +for lib in common packages login-manager steam-desktop led-driver vapor-theme desktop-shortcut; do + # shellcheck source=/dev/null + source "$SCRIPT_DIR/lib/$lib.sh" +done require_root_helper @@ -431,6 +58,11 @@ if ! command -v pacman >/dev/null 2>&1; then exit 1 fi +# Ask for the sudo password once up front and keep it fresh, instead of +# prompting at random points during the run. +sudo -n true 2>/dev/null || sudo -v || exit 1 +while true; do sudo -n true; sleep 50; kill -0 "$$" 2>/dev/null || exit; done 2>/dev/null & + TARGET_USER="${SUDO_USER:-$USER}" read -rp "$(echo -e "${c_bold}Which user should autologin into gamescope?${c_reset} [${TARGET_USER}] ")" input_user TARGET_USER="${input_user:-$TARGET_USER}" @@ -439,247 +71,40 @@ if ! id "$TARGET_USER" >/dev/null 2>&1; then err "User '$TARGET_USER' does not exist on this system." exit 1 fi +if [[ "$TARGET_USER" != "$(id -un)" ]]; then + # Environment, systemd user units, theme and shortcut are all written + # into the invoking user's home and user session. + err "Run this script as '$TARGET_USER' itself so per-user settings land in the right home." + exit 1 +fi ok "Using user: $TARGET_USER" echo -# ---------- 1. Login manager check ---------- - -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 +check_display_manager echo -# ---------- 2. Install packages (Always Runs) ---------- - -info "Checking required packages: gamescope-session-cachyos, steam, mangohud, xterm, ttf-liberation, wqy-zenhei, plasma-keyboard" -MISSING=() -for pkg in gamescope-session-cachyos steam mangohud xterm ttf-liberation wqy-zenhei plasma-keyboard; do - pacman -Qi "$pkg" >/dev/null 2>&1 || MISSING+=("$pkg") -done - -if [[ ${#MISSING[@]} -gt 0 ]]; then - info "Missing packages: ${MISSING[*]}" - if ask_yn "Install them now?"; then - sudo pacman -S --needed "${MISSING[@]}" - else - err "Cannot continue without these packages." - exit 1 - fi -else - ok "All required packages already installed." -fi +install_required_packages echo -if detect_valve_fremont; then - info "Detected Valve Fremont hardware - checking for an AUR helper (needed for leds-valve-dkms-git)" - if command -v yay >/dev/null 2>&1 || command -v paru >/dev/null 2>&1; then - ok "AUR helper already present." - else - info "No AUR helper (yay/paru) found. Installing yay..." - bootstrap_yay || warn "Couldn't set up an AUR helper automatically. You can install one yourself later and run: yay -S leds-valve-dkms-git" - fi - echo -fi +configure_autologin +install_session_sync -# ---------- 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." +setup_steam_desktop echo -# ---------- 4. Fix the base /etc/plasmalogin.conf ---------- +setup_led_driver -BASE_CONF="/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 - -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" | head -n -1 2>/dev/null || sudo cat "$BASE_CONF" +setup_vapor_theme 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 - -# ---------- 6. Install the sync bridge: conf.d -> base config ---------- - -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 - -if grep -q '^Session=' "$DEST"; then - sed -i "s|^Session=.*|Session=$SESSION|" "$DEST" -else - echo "Session=$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 - -[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 - - -# ---------- 8b. Core SteamOS Keyboard & Glyphs Fix (Always runs) ---------- - -info "Applying non-optional SteamOS environment & keyboard fixes..." -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" -if [[ -d "$HOME/.config/gamescope-session" ]]; then - echo "STEAM_GAMEPADUI_ARGS=\"-gamepadui -steamos3\"" >> "$HOME/.config/gamescope-session/environment" -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 -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 steam-desktop-autostart.service -systemctl --user start steam-desktop-autostart.service - -ok "Steam UI parameters and keyboard autostart deployed successfully." -echo - - -# ---------- 8c. Valve Fremont hardware: front LED bar driver ---------- - -# if detect_valve_fremont; then -# echo -# info "Detected Valve Fremont hardware (Steam Machine / BC-250-class board)." -# if ask_yn "Set up the front-panel LED bar driver (leds-valve) so it isn't stuck dark/breathing?" y; then -# 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 - - -# ---------- 9. Optional: Vapor (Steam Deck) KDE theme ---------- - -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 curl - fi - if ! command -v unzstd >/dev/null 2>&1; then - warn "zstd not found, installing it first..." - sudo pacman -S --needed zstd - fi - install_vapor_theme || warn "Vapor theme install ran into a problem - see errors above. Your gamescope boot setup above is unaffected." -fi -echo - - -# ---------- 10. Create Return to Gaming Mode Desktop Shortcut ---------- - create_desktop_shortcut echo - # ---------- 11. Summary + reboot ---------- echo -e "${c_bold}Setup complete.${c_reset}" echo "What this did:" -echo " - Installed gamescope-session-cachyos, steam, mangohud (if missing)" +echo " - Installed gamescope-session-cachyos, steam, mangohud and friends (if missing)" echo " - Created /etc/plasmalogin.conf.d (fixes Switch-to-Desktop crash)" echo " - Set $BASE_CONF to autologin '$TARGET_USER' into gamescope, with Relogin=true" echo " - Installed a sync bridge + systemd watcher so Steam's Switch-to-Desktop" @@ -689,6 +114,7 @@ echo " - Configured permanent silent Steam autostart so Steam+X works everywher echo " - Injected -steamos3 flag to force original Steam Deck overlay glyphs" echo " - Optionally installed Valve's Vapor (Steam Deck) KDE theme, if you chose to" echo " - On Valve Fremont hardware, optionally set up the leds-valve front LED bar driver" +echo " - Added /etc/sudoers.d/gamescope-session-switch so the desktop shortcut works" echo echo "Backups of any files this script modified were saved with a" echo ".bak-gamescope-wizard suffix next to the original."