Merge pull request #1 from theupriser/fix/review-cleanup

fix: Fix setup bugs, add SteamOS desktop look and LED driver, split i…
This commit is contained in:
theupriser authored and GitHub committed 2026-09-23 14:25:28 +02:00
commit dcc184ed39
11 files changed
+1147 -731

No files matched your search

+97
View File
@@ -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.
+226 -125
View File
@@ -1,172 +1,273 @@
# CachyOS Gamescope Boot Wizard # CachyOS Gamescope Boot Wizard
A setup script that makes a CachyOS desktop install (running KDE Plasma + Turn a CachyOS desktop PC into a SteamOS-style console: it boots straight
`plasma-login-manager`) always boot into a **Steam Deck-style gamescope into Steam's Big Picture (gamescope), and you can switch to the KDE Plasma
session** - the same "always boots into Big Picture, switch to desktop and desktop and back whenever you like - just like on a Steam Deck or Valve's
back on demand" experience SteamOS gives you on a real Deck or the Valve
Steam Machine. Steam Machine.
It exists because, as of the CachyOS March 2026 release (which switched the Out of the box, CachyOS doesn't quite manage this: the switch to the desktop
default login manager from SDDM to `plasma-login-manager`), the stock can hang, and the PC doesn't reliably boot back into gaming mode. This
`gamescope-session-cachyos` tooling has a few gaps that stop this from script fixes that and sets everything up for you.
working out of the box. This script patches around them.
## What's actually broken (and what this fixes) ## What you get
CachyOS's `gamescope-session-cachyos` package ships `steamos-session-select`, - **Boots into gaming mode** automatically, without a login screen.
which is meant to work exactly like it does on real SteamOS. On a - **Switch to Desktop** from Steam's power menu works, and so does going
`plasma-login-manager` system, three things get in the way: back: use the **Return to Gaming Mode** icon on the desktop, or just log
out.
1. **No `User=` written to the autologin config.** - **Back to gaming mode after a restart**, like SteamOS.
`steam-set-session` (the script `steamos-session-select` calls under the - **Steam's on-screen keyboard** (Steam + X) also works on the desktop.
hood) only ever writes a `Session=` key to the autologin config file. On - *Optional:* the **SteamOS desktop look** - Valve's own Vapor theme,
SDDM this is fine, because SDDM's `User=` is set once by the installer and wallpaper, dark mode and taskbar.
never touched again. `plasma-login-manager` needs *both* `Session=` and - *Optional, Steam Machine only:* a driver for the **front LED bar**.
`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.
## Requirements ## Requirements
- CachyOS (or an Arch-based system) with `pacman` - CachyOS with the KDE Plasma desktop (the default CachyOS Desktop edition)
- KDE Plasma installed - Your normal user account, with permission to use `sudo`
- `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
## Usage ## Installation
Open **Konsole** on your Plasma desktop and run:
```bash ```bash
git clone <this-repo-url> git clone <this-repo-url>
cd <this-repo> cd <this-repo>
chmod +x setup-gamescope-boot.sh
./setup-gamescope-boot.sh ./setup-gamescope-boot.sh
``` ```
Run it as your normal user, **not** as root - it calls `sudo` internally for Run it as yourself, not as root. It asks for your password once, then asks a
the specific commands that need it. 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 It's safe to run again later, for example after a CachyOS update.
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.
If you keep a folder named `icons/` next to the script (e.g. custom Steam ## Using it
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.
It's **safe to re-run** - every step checks current state first, and any - **To the desktop:** in gaming mode, open the Steam menu and choose
file it modifies gets backed up once with a `.bak-gamescope-wizard` suffix **Power > Switch to Desktop**.
before the first change. - **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 ```bash
gamescope, Steam's **Power > Switch to Desktop** should now work. From the steamos-session-select persistent # start where you left off last time
desktop, logging out should drop you back into gamescope automatically steamos-session-select oneshot # always start in gaming mode (default)
(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).
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 ```bash
steamos-session-select gamescope # switch to gamescope right now steamos-session-select gamescope # switch to gamescope right now
steamos-session-select plasma # switch to desktop 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 persistent # remember the last-used session across reboots
steamos-session-select oneshot # always start in gamescope regardless of what you used last (the default Deck-like behavior) 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 ```bash
# restore the original login config
sudo cp /etc/plasmalogin.conf.bak-gamescope-wizard /etc/plasmalogin.conf sudo cp /etc/plasmalogin.conf.bak-gamescope-wizard /etc/plasmalogin.conf
```
To fully remove the sync watcher this script adds: # session sync watcher
```bash
sudo systemctl disable --now sync-steamos-session.path 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.path
sudo rm /etc/systemd/system/sync-steamos-session.service sudo rm /etc/systemd/system/sync-steamos-session.service
sudo rm /usr/local/bin/sync-steamos-session.sh sudo rm /usr/local/bin/sync-steamos-session.sh
sudo systemctl daemon-reload 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 | File | Responsibility |
September 2026. If CachyOS fixes `steam-set-session` upstream to write |---|---|
`User=` and to target whichever base config the login manager actually | `setup-gamescope-boot.sh` | Entry point: user and sudo checks, step order, summary, reboot |
reads, this script becomes unnecessary - check for updates before running | `lib/common.sh` | Output helpers, yes/no prompts, backups |
it on a freshly reinstalled system. | `lib/packages.sh` | Required packages, AUR helper (yay/paru) |
- Tested on a Valve Steam Machine (desktop AMD APU + discrete GPU) running | `lib/login-manager.sh` | plasmalogin autologin, session sync bridge and its systemd units |
CachyOS Desktop edition. Should apply to any CachyOS desktop install using | `lib/steam-desktop.sh` | Steam in the Plasma session: gamepad UI flags, virtual keyboard, autostart |
`plasma-login-manager`, but hasn't been tested on Steam Deck/Legion Go | `lib/led-driver.sh` | Fremont detection, kernel headers, LED driver, OpenRGB |
hardware, which typically ship with SDDM instead. | `lib/vapor-theme.sh` | Vapor theme, Valve's SteamOS defaults, panel, dark mode |
- If you find CachyOS has patched the underlying issue, please open an issue | `lib/desktop-shortcut.sh` | Return to Gaming Mode shortcut and its sudoers rule |
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.
## 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` - [CachyOS/gamescope-session#9](https://github.com/CachyOS/gamescope-session/issues/9) - "Switch to Desktop" hang due to missing `/etc/plasmalogin.conf.d`
-7
View File
@@ -1,7 +0,0 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg id="svg2" height="64" style="" viewBox="0 0 64 64" width="64" xmlns="http://www.w3.org/2000/svg" sodipodi:docname="deck_icon.svg" inkscape:version="1.2-dev (d2dd25d878, 2022-02-01, custom)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg">
<path id="path42220" d="m 32 4 v 10 a 18 18 0 0 1 18 18 a 18 18 0 0 1 -18 18 v 10 a 28 28 0 0 0 28 -28 a 28 28 0 0 0 -28 -28 z" style="fill:#fff;fill-rule:evenodd"/>
<circle id="path43518" cx="32" cy="32" r="14" style="fill:#1a9fff;fill-rule:evenodd"/>
<path id="path52048" d="m 32 22 a 2 2 0 0 0 -1.4140625 .5859375 l -8 8 a 2.0002 2.0002 0 0 0 0 2.828125 l 8 8 a 2 2 0 0 0 2.828125 0 a 2 2 0 0 0 0 -2.828125 l -4.5859375 -4.5859375 h 11.171875 a 2 2 0 0 0 2 -2 a 2 2 0 0 0 -2 -2 h -11.171875 l 4.5859375 -4.5859375 a 2 2 0 0 0 0 -2.828125 a 2 2 0 0 0 -1.4140625 -.5859375 z" style="fill:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"/>
<g id="path89696" style=""/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

+35
View File
@@ -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
}
+62
View File
@@ -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."
}
+113
View File
@@ -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
}
+129
View File
@@ -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
}
+83
View File
@@ -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
}
+54
View File
@@ -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."
}
+323
View File
@@ -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
}
+25 -599
View File
@@ -31,7 +31,7 @@
# desktop side matches the gamescope side visually. # desktop side matches the gamescope side visually.
# #
# On Valve Fremont hardware (DMI sys_vendor=Valve, product_name=Fremont -- # 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 # 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 # /sys/class/leds instead of sitting dark or "breathing" under a standard
# desktop kernel, plus the option to pull in an experimental OpenRGB build # desktop kernel, plus the option to pull in an experimental OpenRGB build
@@ -39,385 +39,12 @@
set -uo pipefail 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" for lib in common packages login-manager steam-desktop led-driver vapor-theme desktop-shortcut; do
# shellcheck source=/dev/null
info() { echo -e "${c_cyan}[INFO]${c_reset} $*"; } source "$SCRIPT_DIR/lib/$lib.sh"
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 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 ----------
require_root_helper require_root_helper
@@ -431,6 +58,11 @@ if ! command -v pacman >/dev/null 2>&1; then
exit 1 exit 1
fi 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}" TARGET_USER="${SUDO_USER:-$USER}"
read -rp "$(echo -e "${c_bold}Which user should autologin into gamescope?${c_reset} [${TARGET_USER}] ")" input_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}" 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." err "User '$TARGET_USER' does not exist on this system."
exit 1 exit 1
fi 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" ok "Using user: $TARGET_USER"
echo echo
# ---------- 1. Login manager check ---------- check_display_manager
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
echo echo
# ---------- 2. Install packages (Always Runs) ---------- install_required_packages
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
echo echo
if detect_valve_fremont; then configure_autologin
info "Detected Valve Fremont hardware - checking for an AUR helper (needed for leds-valve-dkms-git)" install_session_sync
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
# ---------- 3. Ensure plasmalogin.conf.d exists (fixes Switch-to-Desktop crash) ---------- setup_steam_desktop
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 echo
# ---------- 4. Fix the base /etc/plasmalogin.conf ---------- setup_led_driver
BASE_CONF="/etc/plasmalogin.conf" setup_vapor_theme
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"
echo 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 create_desktop_shortcut
echo echo
# ---------- 11. Summary + reboot ---------- # ---------- 11. Summary + reboot ----------
echo -e "${c_bold}Setup complete.${c_reset}" echo -e "${c_bold}Setup complete.${c_reset}"
echo "What this did:" 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 " - Created /etc/plasmalogin.conf.d (fixes Switch-to-Desktop crash)"
echo " - Set $BASE_CONF to autologin '$TARGET_USER' into gamescope, with Relogin=true" 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" 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 " - 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 " - 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 " - 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
echo "Backups of any files this script modified were saved with a" echo "Backups of any files this script modified were saved with a"
echo ".bak-gamescope-wizard suffix next to the original." echo ".bak-gamescope-wizard suffix next to the original."