Compare commits

...
Author SHA1 Message Date
theupriser 986b3c8c07 Merge pull request #9 from theupriser/fix/use-cachyos-vapor
Fix/use cachyos vapor
2026-09-24 12:41:23 +02:00
theupriser afc05b0b23 feat: The shortcut's window closes itself after a countdown
- 10-second countdown after the wizard ends (Enter closes right away); after an error it waits for Enter.
- pipefail, so a failed download counts as an error.
2026-09-24 12:40:48 +02:00
theupriser b0553aecf7 docs: Shorter README intro 2026-09-24 12:34:43 +02:00
theupriser 69a420b8aa feat: The script is now steamify.sh
- setup-gamescope-boot.sh renamed to steamify.sh; all references updated.
- Releases publish the bundle as steamify.sh and, for older install commands, setup-gamescope-boot.sh.
- The Steamify shortcut runs steamify.sh.
2026-09-24 12:33:43 +02:00
theupriser 1c34a97598 chore: Repository renamed to steamify-cachyos
All URLs point to github.com/theupriser/steamify-cachyos (the old ones redirect). The script and internal names (state dir, shortcut files) keep their names so old download URLs and existing installs keep working.
2026-09-24 12:27:45 +02:00
theupriser be874b8594 feat: Rename to Steamify CachyOS
Menu header, shortcut, release titles, README and AGENTS.md. Repository, script and URLs keep the name cachyos-gamescope-boot.
2026-09-24 12:24:50 +02:00
theupriser f01aa93d0e feat: Wizard shortcut on the desktop and in the launcher
- Menu item: desktop icon + launcher entry that run the newest release in Konsole.
- Icon: Valve's return icon with a gear (assets/steam-gaming-settings.svg), published with each release.
- Desktop files are written already executable, so Plasma runs them.
- VERSION 0.9.0.
2026-09-24 11:46:25 +02:00
theupriser 852205317a Merge pull request #8 from theupriser/fix/use-cachyos-vapor
Fix/use cachyos vapor
2026-09-24 10:57:03 +02:00
theupriser 77b94c29a2 fix: Merge remote-tracking branch 'origin/main' into fix/use-cachyos-vapor 2026-09-24 10:55:55 +02:00
theupriser c0014ee623 docs: 0.7.0 was released on its own (#6, #7) 2026-09-24 10:55:55 +02:00
theupriser d39731a8b9 docs: Last commit hash in the changelog 2026-09-24 10:55:27 +02:00
theupriser 99a95abf1f fix: Kernel legend only mentions the LEDs on a Steam Machine 2026-09-24 10:54:33 +02:00
theupriser 429e840a9f fix: Shorter BIOS labels so the menu fits 80 columns 2026-09-24 10:51:53 +02:00
theupriser 45a8757f58 fix: BIOS dry run also walks through the restart choices
A dry run counts as staged, so [m]/[r] and the restart question on q show up; restarting only prints in dry-run mode.
2026-09-24 10:48:39 +02:00
theupriser 54fc9e362b feat: Menu comes back after each run, BIOS dry run, version 0.8.0
- After a run the menu returns; [m] back to the menu or [r] restart now when a restart is needed; q quits.
- A staged BIOS update greys the item out until the restart.
- WIZARD_BIOS_DRY_RUN=1: whole BIOS flow without the device check, never flashes.
- VERSION 0.8.0; CHANGELOG: BIOS work moved to 0.8.0; README: BIOS update step by step.
2026-09-24 10:40:16 +02:00
theupriser b70c9d614f fix: shellcheck in the bundle, and the docs for the BIOS checks
- Rename a variable in lib/bios.sh that clashed with lib/state.sh's array in the bundle.
- README, AGENTS.md and CHANGELOG.md for the greyed-out item, the device check and the aligned warning box.
2026-09-24 10:30:24 +02:00
theupriser 40c9ee638b fix: BIOS update only when newer, device check first, aligned warnings
- Greyed out and not tickable unless Valve has a newer BIOS (component_selectable).
- Download, SHA-256 and a fwupd device check (get-details) before the warnings.
- Warning box with a solid block frame (a coloured row of # renders narrower in Konsole); menu Now column aligned.
2026-09-24 10:29:46 +02:00
theupriser 2f9b2a54e1 feat: Opt-in BIOS update for the Steam Machine
- Menu item (Fremont only, never preselected) with the current and newest BIOS version.
- Two warnings and two confirmations (y/N, then typing UPDATE); checksum-verified download
  of Valve's fremont-hw-support; installed with fwupd, written at the next restart.
- Menu actions: one-off items that are never preselected, re-applied or listed as on/off.
2026-09-24 10:22:15 +02:00
theupriser 53f53a77ff Merge pull request #7 from theupriser/fix/use-cachyos-vapor
Fix/use cachyos vapor
2026-09-24 10:11:31 +02:00
theupriser 6afef78721 ci: The latest tag follows the newest version tag
When v$VERSION is released, move the latest tag and release to it and replace its bundle, so the older install URL keeps giving the newest version.
2026-09-24 10:09:26 +02:00
theupriser 1604ed10d4 docs: Versioning and release rules in AGENTS.md 2026-09-24 10:08:22 +02:00
theupriser 8aab946c6f ci: A release per version (tag v0.7.0, ...) instead of overwriting "latest"
- Publish release v$VERSION with its CHANGELOG.md section; never overwrite an existing version.
- Pull requests warn when VERSION is already released.
- Install URL: releases/latest/download/setup-gamescope-boot.sh (newest release).
2026-09-24 10:07:56 +02:00
theupriser 5cbe0079cb Merge pull request #6 from theupriser/fix/use-cachyos-vapor
Fix/use cachyos vapor
2026-09-24 10:06:21 +02:00
theupriser 5c00504261 docs: Commit hashes in the changelog 2026-09-24 10:04:55 +02:00
theupriser d7231ebbd8 chore: Move the bundle tool to .github/tools/bundle.sh
Update its repo-root path and all references (workflow, README, AGENTS.md).
2026-09-24 10:04:29 +02:00
theupriser eb3169efa1 ci: Version and changelog in the release notes
- Release title shows VERSION; notes include that version's CHANGELOG.md section.
- Rename a variable in steamos-extras.sh that shellcheck confused with lib/state.sh's array in the bundle.
2026-09-24 10:02:30 +02:00
theupriser 10b51644c9 docs: Version 0.7.0 and CHANGELOG
- VERSION in the entry point, shown in the menu header and the bundle.
- CHANGELOG.md with every commit per version (older versions numbered per merged PR).
- README and AGENTS.md updated for the cachyos-vapor theme, the SteamOS extras and the LED driver.
2026-09-24 10:01:08 +02:00
theupriser 7802d90186 fix: LED driver for every kernel, kernel headers at boot, console power button, kernel overview
- The leds-valve Makefile builds against uname -r, so every other kernel
  (e.g. linux-cachyos-lts) failed: add /etc/dkms/leds-valve-dkms.conf with
  MAKE[0]="make KVERSION=${kernelver}". DKMS now builds it for every
  installed kernel and rebuilds it on kernel updates.
- ensure-kernel-headers.service installs missing -headers for newly added
  kernels at boot, which makes DKMS build the driver for them.
- Console-like power handling on mains power: power button sleeps, no
  automatic suspend (journaled powerdevilrc).
- Menu: overview per installed kernel (headers, Steam controller driver,
  LED driver built, running kernel) and whether the LEDs are loaded.
- Menu: the selected row's highlight no longer stops after the green x.
- Theme label in the menu: "Vapor look (cachyos-vapor)".
2026-09-24 10:00:32 +02:00
theupriser 978f5a14cf feat: SteamOS theme from cachyos-vapor with its desktop layout, plus SteamOS desktop extras
- Install CachyOS's cachyos-vapor package instead of extracting Valve's
  steamdeck-kde-presets into ~/.local/share, and remove it again on disable
  if the wizard installed it.
- Apply the Vapor global theme with "Desktop and window layout"
  (lookandfeeltool --resetLayout): Steam Deck wallpaper, SteamOS panel and
  launcher icon. Layout files are backed up and restored, every key from
  Vapor's defaults goes through the undo journal.
- Restore the real previous color scheme (CachyOS's inline BreezeDark colors
  have no scheme name), and clear ColorScheme before plasma-apply-colorscheme,
  which otherwise skips an already named scheme.
- Register Vapor as the dark theme, so Plasma's Dark Mode toggle shows on.
- Drop the SteamOS desktop defaults (GTK, Konsole, gsettings, panel tweaks).
- New lib/steamos-extras.sh, downloaded from Valve's newest
  steamdeck-kde-presets (checksum verified) into /usr/local: Add to Steam,
  Nested Desktop, the gaming-return icon for Return to Gaming Mode, the
  Steam Keyboard window rule and an empty KWallet (only without a wallet).
- single_launcher: re-apply single user's launcher settings after the
  layout reset replaces the launcher.
2026-09-24 10:00:32 +02:00
theupriser a501df25b5 Update steam-machine.sh
Add inputplumber for the downloadbar.
2026-09-23 21:44:54 +02:00
theupriser 079c48f6fe Merge pull request #5 from theupriser/fix/review-cleanup
fix: Use curl | bash in the docs: bash <(...) doesn't work in fish
2026-09-23 15:42:01 +02:00
theupriser ee5391c126 Merge pull request #4 from theupriser/fix/review-cleanup
fix: Single-file build for curl, published by GitHub Actions
2026-09-23 15:38:31 +02:00
theupriser e741246098 Merge pull request #3 from theupriser/fix/review-cleanup
Fix/review cleanup
2026-09-23 15:27:58 +02:00
theupriser 9827bea8d5 Merge pull request #2 from theupriser/fix/review-cleanup
fix: Add single user no password option with SDDM, fetch newest Vapor…
2026-09-23 14:47:52 +02:00
theupriser dcc184ed39 Merge pull request #1 from theupriser/fix/review-cleanup
fix: Fix setup bugs, add SteamOS desktop look and LED driver, split i…
2026-09-23 14:25:28 +02:00
21 changed files with 1544 additions and 484 deletions

No files matched your search

+10 -10
View File
@@ -1,37 +1,37 @@
#!/bin/bash #!/bin/bash
# Build a single-file version of the wizard for `curl | bash` use: # Build a single-file version of the wizard for `curl | bash` use:
# tools/bundle.sh [output] (default: dist/setup-gamescope-boot.sh) # .github/tools/bundle.sh [output] (default: dist/steamify.sh)
# #
# Inlines lib/*.sh in the order setup-gamescope-boot.sh sources them and # Inlines lib/*.sh in the order steamify.sh sources them and
# wraps the entry point's body in main(), so bash has read the whole file # wraps the entry point's body in main(), so bash has read the whole file
# before anything runs. That lets the bundle reattach stdin to the terminal # before anything runs. That lets the bundle reattach stdin to the terminal
# when it was piped in by curl, so the interactive menu still works. # when it was piped in by curl, so the interactive menu still works.
set -euo pipefail set -euo pipefail
cd "$(dirname "$0")/.." cd "$(dirname "$0")/../.."
out="${1:-dist/setup-gamescope-boot.sh}" out="${1:-dist/steamify.sh}"
entry=setup-gamescope-boot.sh entry=steamify.sh
mkdir -p "$(dirname "$out")" mkdir -p "$(dirname "$out")"
# Library order, taken from the entry point's source loop. # Library order, taken from the entry point's source loop.
libs=$(grep -oP '^for lib in \K.*(?=; do$)' "$entry") libs=$(grep -oP '^for lib in \K.*(?=; do$)' "$entry")
[[ -n "$libs" ]] || { echo "bundle: can't find the lib source loop in $entry" >&2; exit 1; } [[ -n "$libs" ]] || { echo "bundle: can't find the lib source loop in $entry" >&2; exit 1; }
version="$(git describe --always --dirty 2>/dev/null || echo unknown)" version="v$(grep -oP '^VERSION=\K.*' "$entry"), $(git describe --always --dirty 2>/dev/null || echo unknown)"
{ {
# Header comment and `set` line of the entry point, up to the source loop. # Header comment and `set` line of the entry point, up to the source loop.
sed -n '1,/^SCRIPT_DIR=/p' "$entry" | sed '$d' sed -n '1,/^SCRIPT_DIR=/p' "$entry" | sed '$d'
echo "# Single-file build ($version), generated by tools/bundle.sh - edit the" echo "# Single-file build ($version), generated by .github/tools/bundle.sh - edit the"
echo "# sources in the repository instead: https://github.com/theupriser/cachyos-gamescope-boot" echo "# sources in the repository instead: https://github.com/theupriser/steamify-cachyos"
echo echo
for lib in $libs; do for lib in $libs; do
echo "# ----- lib/$lib.sh -----" echo "# ----- lib/$lib.sh -----"
# Drop each module's shebang and its "Sourced by ..." boilerplate. # Drop each module's shebang and its "Sourced by ..." boilerplate.
sed -e '1{/^#!/d}' -e '/^# Sourced by setup-gamescope-boot.sh/d' "lib/$lib.sh" sed -e '1{/^#!/d}' -e '/^# Sourced by steamify.sh/d' "lib/$lib.sh"
echo echo
done done
echo "# ----- setup-gamescope-boot.sh -----" echo "# ----- steamify.sh -----"
echo "main() {" echo "main() {"
# Everything after the source loop is the entry point's body. # Everything after the source loop is the entry point's body.
sed -n '/^for lib in /,$p' "$entry" | sed '1,/^done$/d' | sed 's/^/ /; s/^ $//' sed -n '/^for lib in /,$p' "$entry" | sed '1,/^done$/d' | sed 's/^/ /; s/^ $//'
+60 -21
View File
@@ -1,7 +1,13 @@
# Builds the single-file wizard (tools/bundle.sh) and publishes it as the # Builds the single-file wizard (.github/tools/bundle.sh) and publishes it as a
# asset of the rolling "latest" release, for: # release per version: tag v$VERSION (VERSION in steamify.sh),
# curl -fsSL https://github.com/theupriser/cachyos-gamescope-boot/releases/download/latest/setup-gamescope-boot.sh | bash # with that version's CHANGELOG.md section as notes. A released version is
# Pull requests only build and check it. # never overwritten; the newest one is marked latest, and the "latest" tag and
# release are moved to it, so both of these always get the newest version:
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify.sh | bash
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/download/latest/steamify.sh | bash
# The bundle is also published as setup-gamescope-boot.sh, its name before
# 0.9.0, so older install commands keep working.
# Pull requests only build and check it, and warn when VERSION is already released.
name: Single-file build name: Single-file build
on: on:
@@ -22,33 +28,66 @@ jobs:
fetch-depth: 0 # for the version in the bundle's header fetch-depth: 0 # for the version in the bundle's header
- name: Build - name: Build
run: tools/bundle.sh dist/setup-gamescope-boot.sh run: |
.github/tools/bundle.sh dist/steamify.sh
# Old name (before 0.9.0), for older install commands and links.
cp dist/steamify.sh dist/setup-gamescope-boot.sh
- name: Check - name: Check
run: | run: |
bash -n dist/setup-gamescope-boot.sh bash -n dist/steamify.sh
# SC2034/SC2154: variables used across the inlined modules. # SC2034/SC2154: variables used across the inlined modules.
shellcheck -S warning -e SC2034,SC2154 dist/setup-gamescope-boot.sh shellcheck -S warning -e SC2034,SC2154 dist/steamify.sh
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: setup-gamescope-boot name: steamify
path: dist/setup-gamescope-boot.sh path: dist/steamify.sh
- name: Publish to the "latest" release - name: Version
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' id: version
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
run: | run: |
notes="Single-file build of $(git describe --always) ($(git log -1 --format=%cs)). version="$(grep -oP '^VERSION=\K.*' steamify.sh)"
Run it with: echo "version=$version" >> "$GITHUB_OUTPUT"
if gh release view "v$version" >/dev/null 2>&1; then
curl -fsSL https://github.com/${{ github.repository }}/releases/download/latest/setup-gamescope-boot.sh | bash" echo "released=true" >> "$GITHUB_OUTPUT"
if gh release view latest >/dev/null 2>&1; then echo "::warning::v$version is already released and won't be overwritten. Bump VERSION in steamify.sh and add a CHANGELOG.md section to release this."
gh release edit latest --notes "$notes"
else else
gh release create latest --title "Latest single-file build" --notes "$notes" --latest echo "released=false" >> "$GITHUB_OUTPUT"
fi fi
# Move the tag to this commit and replace the asset.
git tag -f latest && git push -f origin latest - name: Publish the v${{ steps.version.outputs.version }} release
gh release upload latest dist/setup-gamescope-boot.sh --clobber if: >-
github.event_name != 'pull_request' && github.ref == 'refs/heads/main' &&
steps.version.outputs.released == 'false'
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.version.outputs.version }}
run: |
# This version's section of CHANGELOG.md, without its heading.
changes="$(awk -v v="$VERSION" '
$0 ~ "^## " { if (found) exit; if (index($0, "## " v " ") == 1) { found = 1; next } }
found' CHANGELOG.md)"
notes="Single-file build of v$VERSION ($(git describe --always), $(git log -1 --format=%cs)).
Run the newest version with:
curl -fsSL https://github.com/${{ github.repository }}/releases/latest/download/steamify.sh | bash
## Changes in v$VERSION
$changes"
gh release create "v$VERSION" dist/steamify.sh dist/setup-gamescope-boot.sh assets/steam-gaming-settings.svg \
--target "$GITHUB_SHA" --title "Steamify CachyOS v$VERSION" --notes "$notes" --latest
# "latest" follows the newest version tag, for the older install URL
# (.../releases/download/latest/...). Only moved when a new version
# is released, never for a push without a VERSION bump.
git tag -f latest "$GITHUB_SHA" && git push -f origin latest
latest_notes="Always the newest version, currently v$VERSION (see its release for the changes)."
if gh release view latest >/dev/null 2>&1; then
gh release edit latest --title "Steamify CachyOS - latest (v$VERSION)" --notes "$latest_notes" --latest=false
else
gh release create latest --title "Steamify CachyOS - latest (v$VERSION)" --notes "$latest_notes" --latest=false
fi
gh release upload latest dist/steamify.sh dist/setup-gamescope-boot.sh assets/steam-gaming-settings.svg --clobber
+79 -15
View File
@@ -5,14 +5,14 @@ for what the script does from a user's point of view.
## Project overview ## Project overview
A bash wizard that makes CachyOS (KDE Plasma 6 + `plasma-login-manager`) **Steamify CachyOS**: a bash wizard that makes CachyOS (KDE Plasma 6 + `plasma-login-manager`)
boot into a SteamOS-style gamescope session, with working switching between boot into a SteamOS-style gamescope session, with working switching between
gamescope and the Plasma desktop. Primary target: the Valve Steam Machine gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
(DMI `Valve`/`Fremont`) running CachyOS Desktop edition. (DMI `Valve`/`Fremont`) running CachyOS Desktop edition.
## Layout ## Layout
- `setup-gamescope-boot.sh` - the only entry point. Pre-flight checks, - `steamify.sh` - the only entry point. Pre-flight checks,
sources `lib/*.sh`, runs the menu and applies the plan. No component sources `lib/*.sh`, runs the menu and applies the plan. No component
logic here. logic here.
- `lib/*.sh` - one file per responsibility, each defining functions only - `lib/*.sh` - one file per responsibility, each defining functions only
@@ -31,12 +31,13 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
`krevert <component>`. Never call `kwriteconfig6` directly for settings a `krevert <component>`. Never call `kwriteconfig6` directly for settings a
component owns. System files are backed up with `backup_file` and restored component owns. System files are backed up with `backup_file` and restored
on disable. on disable.
- **Single-file build.** `tools/bundle.sh` inlines `lib/*.sh` in the order of - **Single-file build.** `.github/tools/bundle.sh` inlines `lib/*.sh` in the order of
the entry point's `for lib in ...; do` source loop and wraps everything the entry point's `for lib in ...; do` source loop and wraps everything
after that loop in `main()`. Keep that loop on one line, keep all logic in after that loop in `main()`. Keep that loop on one line, keep all logic in
functions, and don't rely on `SCRIPT_DIR` for anything but sourcing. functions, and don't rely on `SCRIPT_DIR` for anything but sourcing.
CI (`.github/workflows/bundle.yml`) publishes the bundle to the `latest` CI (`.github/workflows/bundle.yml`) publishes the bundle as release
release on pushes to `main`. `v$VERSION` on pushes to `main`; an existing version is never overwritten,
so bump `VERSION` for every release.
## Conventions ## Conventions
@@ -58,6 +59,22 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
- Comments explain *why* (the CachyOS/Plasma quirk being worked around), - Comments explain *why* (the CachyOS/Plasma quirk being worked around),
not what the next line does. not what the next line does.
- **Versioning and releases.** SemVer in `VERSION` (`steamify.sh`,
shown in the menu header and the bundle header).
- Every commit gets an entry in `CHANGELOG.md` under its version (short hash
+ subject; a commit can't contain its own hash, so fill it in with the
next commit). The section heading must be `## <VERSION> - <date>`: CI
cuts the release notes out of the changelog by that heading.
- Every PR that should be released bumps `VERSION` and adds its section.
A push to `main` publishes release `v$VERSION` (tag + bundle + that
changelog section) and marks it latest. An existing version is never
overwritten: without a bump nothing is released, and pull requests show
a warning.
- Users install through `releases/latest/download/steamify.sh`
(GitHub's newest release). The `latest` tag and release follow the newest
version tag too (moved, asset replaced, when a new version is released),
so the older URL `releases/download/latest/...` keeps working.
## Non-obvious behaviour to preserve ## Non-obvious behaviour to preserve
- `apply_changes` sets `$LOGIN_MANAGER` from the menu: `sddm` when single - `apply_changes` sets `$LOGIN_MANAGER` from the menu: `sddm` when single
@@ -80,11 +97,21 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
- The Steam desktop autostart unit is guarded with - The Steam desktop autostart unit is guarded with
`ExecCondition=... XDG_CURRENT_DESKTOP = KDE`, so it doesn't start a `ExecCondition=... XDG_CURRENT_DESKTOP = KDE`, so it doesn't start a
second Steam inside gamescope. second Steam inside gamescope.
- Vapor theme: install Valve's files as shipped. Don't rewrite Valve's - Vapor theme: comes from the `cachyos-vapor` package (system-wide, in
`metadata.json`; the icon theme is `breeze-dark` (there is no "Vapor" `/usr/share`); only remove it on disable if we installed it. It is applied
icon theme); wallpapers are flat JPGs in `usr/share/wallpapers`. with `lookandfeeltool --resetLayout` ("Desktop and window layout"), which
`lookandfeeltool` does not switch the color scheme, so replaces the panel/desktop layout: the layout files are backed up to
`plasma-apply-colorscheme Vapor` is applied explicitly. `$STATE_DIR/theme-layout` and restored, and every key from Vapor's
`contents/defaults` goes through `kset` first. `plasma-apply-colorscheme`
runs with the `ColorScheme` key cleared, since it skips a scheme already
named there. The layout reset drops single user's launcher settings, so
`single_launcher` is re-applied.
- SteamOS extras (`lib/steamos-extras.sh`, part of the theme): downloaded from
Valve's newest `steamdeck-kde-presets` (repo db gives name + SHA-256) into
`/usr/local`, never `/usr`. `gaming-return.svg` is a symlink in the package:
install `steam-gaming-return.svg` under that name. The shortcut's icon is
switched with `set_shortcut_icon`, since the conversion is created before
the theme. An existing KWallet is never replaced or removed.
- Plasma 6 has no separate systemtray containment: tray settings live on - Plasma 6 has no separate systemtray containment: tray settings live on
the systemtray applet itself (Valve's setup script targets Plasma 5). the systemtray applet itself (Valve's setup script targets Plasma 5).
- Restart plasmashell via `systemctl --user` (`plasma-plasmashell.service`), - Restart plasmashell via `systemctl --user` (`plasma-plasmashell.service`),
@@ -97,19 +124,56 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
wallpaper files only between `stop_plasmashell_for_edit` and wallpaper files only between `stop_plasmashell_for_edit` and
`restart_plasmashell_if_stopped` (`lib/common.sh`). `restart_plasmashell_if_stopped` (`lib/common.sh`).
- LED driver: `leds-valve-dkms-git`'s Makefile builds against `uname -r`, - LED driver: `leds-valve-dkms-git`'s Makefile builds against `uname -r`,
so the script builds explicitly for the running kernel and installs not DKMS's target kernel: `/etc/dkms/leds-valve-dkms.conf` sets
headers for every installed kernel first. The module creates `MAKE[0]="make KVERSION=${kernelver}"` (written before the AUR install).
Without it, other kernels build against the running kernel's tree and
fail (CachyOS kernels are clang-built; DKMS adds `LLVM=1` only for the
target's tree). Headers for every installed kernel are installed first,
and `ensure-kernel-headers.service` installs missing ones at boot (a
pacman hook can't run pacman), which triggers DKMS's install hook. The module creates
`/sys/class/leds/valve-leds*`. `/sys/class/leds/valve-leds*`.
- `bios` is an *action* (`ACTIONS` in `lib/menu.sh`), not an on/off
component: never preselected (not even on a first run), never re-applied
by `a`, not listed in the state overview, and `bios_status` is always off.
Only selectable when Valve's version differs from the installed one
(`component_selectable`, greyed out otherwise). Download, SHA-256 and the
fwupd device check (`get-details --json`: no `UpdateError`) come before the
warnings. The warning box uses `█` for its frame: Konsole draws a long
coloured row of `#` narrower, so the right edge wouldn't line up.
Keep both confirmations (y/N, then typing `UPDATE`) and the warnings; the
firmware comes from the newest `holo-X.Y` repo (`.files` db names the
`.cab`, `.db` gives the SHA-256), and fwupd itself refuses non-Fremont
hardware. In the VM, test it with `WIZARD_BIOS_DRY_RUN=1` (skips only the
device check, never flashes) and a faked version (dev-env
`BIOS_VERSION=F7F0107 ./run.sh --fremont`).
- The entry point loops: menu, run, "back to the menu" (or `[m]`/`[r]` when a
restart is needed), until `q`; the restart question is asked once at the
end. Scripted input that runs out ends the loop like `q`.
- Steamify shortcut (`launcher`): the icon runs `curl | bash` of
`releases/latest/download/steamify.sh` in Konsole, so it's
always the newest release. Its icon, `assets/steam-gaming-settings.svg`
(Valve's GPL-2.0 return icon with a gear), is a release asset too, and is
downloaded from there (Steam's icon if that fails). Desktop files are
written with their mode already set (`install_executable`): Plasma opens a
desktop icon it first saw non-executable in an editor.
The start script closes its window after a 10-second countdown on success
and waits for Enter after an error; it uses `pipefail`, or a failed
download would run an empty script and count as success.
- `Relogin=true` means a gamescope that fails to start is relaunched in a - `Relogin=true` means a gamescope that fails to start is relaunched in a
tight loop; keep that in mind when changing session handling. tight loop; keep that in mind when changing session handling.
## Checking changes ## Checking changes
The bundle puts every module in one file, so shellcheck sees all their
`local` variables together: don't reuse a name another module uses as an
array (e.g. `g` in `lib/state.sh`), or CI's shellcheck on the bundle fails.
There is no test suite. At minimum: There is no test suite. At minimum:
```bash ```bash
for f in setup-gamescope-boot.sh lib/*.sh; do bash -n "$f"; done for f in steamify.sh lib/*.sh; do bash -n "$f"; done
shellcheck -S warning setup-gamescope-boot.sh lib/*.sh # if available shellcheck -S warning steamify.sh lib/*.sh # if available
``` ```
Behaviour is verified in a CachyOS QEMU/KVM test VM. The VM scripts and a Behaviour is verified in a CachyOS QEMU/KVM test VM. The VM scripts and a
@@ -118,5 +182,5 @@ the wizard with scripted menu input such as `printf '2\n\ny\nn\n'`, the
per-component checks; when stdin is not a terminal the menu falls back to a per-component checks; when stdin is not a terminal the menu falls back to a
numbered prompt, which is what scripted runs use, reboot checks, the full test matrix, `--fremont` to numbered prompt, which is what scripted runs use, reboot checks, the full test matrix, `--fremont` to
fake Steam Machine hardware) live in fake Steam Machine hardware) live in
[cachyos-gamescope-boot-dev-env](https://github.com/theupriser/cachyos-gamescope-boot-dev-env). [steamify-cachyos-dev](https://github.com/theupriser/steamify-cachyos-dev).
Gamescope itself and the real LED bar can only be verified on hardware. Gamescope itself and the real LED bar can only be verified on hardware.
+167
View File
@@ -0,0 +1,167 @@
# Changelog
All notable changes, per version and per commit. Versions follow
[Semantic Versioning](https://semver.org/); the current one is `VERSION` in
`steamify.sh`. Versions before 0.7.0 were numbered afterwards,
one per merged pull request.
## 0.9.0 - 2026-09-24
The wizard is now called **Steamify CachyOS**, and can put a shortcut to
itself on the desktop.
- `f01aa93` **feat: Wizard shortcut on the desktop and in the launcher**
- New menu item: a desktop icon and app launcher entry
that open the newest release of the wizard in Konsole (`curl | bash`).
- Its icon: Valve's Return to Gaming Mode icon with a gear instead of the
arrow (`assets/steam-gaming-settings.svg`), published with every release
and downloaded from there.
- Desktop files are written already executable, so Plasma runs them instead
of opening them in an editor.
- `be874b8` **feat: Rename to Steamify CachyOS**
- Menu header, the shortcut ("Steamify CachyOS", menu item "Steamify
shortcut"), release titles, README and AGENTS.md.
- `1c34a97` **chore: Repository renamed to steamify-cachyos**
- All URLs point to `github.com/theupriser/steamify-cachyos` (the old ones
redirect). The script keeps its name, `setup-gamescope-boot.sh`, so the
old download URL keeps working; internal names such as
`~/.local/state/cachyos-gamescope-boot` stay, so existing installs can
still be turned off.
- `69a420b` **feat: The script is now steamify.sh**
- `setup-gamescope-boot.sh` is renamed to `steamify.sh`; releases publish the
bundle under both names, so `.../download/setup-gamescope-boot.sh` keeps
working. The Steamify shortcut runs `steamify.sh`.
- `b0553ae` **docs: Shorter README intro**
- **feat: The shortcut's window closes itself after a countdown**
- After the wizard ends, a 10-second countdown closes the Konsole window
(Enter closes it right away); after an error it stays open until Enter.
- A failed download now counts as an error (`pipefail`) instead of running
an empty script.
## 0.8.0 - 2026-09-24 (#8)
An opt-in BIOS update for the Steam Machine, and a menu that comes back after
every run.
- `2f9b2a5` **feat: Opt-in BIOS update for the Steam Machine**
- New menu item (Steam Machine only, never ticked by default) showing the
current BIOS version and the newest from Valve's `fremont-hw-support`.
- Two large warnings and two confirmations (y/N, then typing `UPDATE`),
checksum-verified download, installed with fwupd; the wizard then offers
the restart that writes it, with a warning to keep the power on.
- `40c9ee6` **fix: BIOS update only when newer, device check first, aligned warnings**
- The item is greyed out and can't be ticked unless Valve has a newer BIOS.
- Before any warning: SHA-256 of Valve's package, then fwupd confirms the
firmware fits this machine's hardware; otherwise it stops.
- Warning box drawn with a solid red frame whose edges line up, and the
menu's "Now" column aligned.
- `b70c9d6` **fix: shellcheck in the bundle, and the docs for the BIOS checks**
- A variable name in `lib/bios.sh` clashed with `lib/state.sh` in the bundle
(CI's shellcheck would fail); README, AGENTS.md and this changelog updated.
- `54fc9e3` **feat: Menu comes back after each run, BIOS dry run, version 0.8.0**
- After a run the menu returns with the new state; quit with `q`. When
something needs a restart, choose between back to the menu (`m`) and
restart now (`r`); quitting asks once more.
- A staged BIOS update greys the item out until the restart.
- `WIZARD_BIOS_DRY_RUN=1` walks through the whole BIOS update (download,
checksum, both warnings) but only prints the install and never flashes.
- README: the menu loop and the BIOS update step by step.
- `45a8757` **fix: BIOS dry run also walks through the restart choices**
- A dry run counts as staged, so `[m]`/`[r]` and the restart question on `q`
show up; in dry-run mode restarting only prints what it would do.
- `429e840` **fix: Shorter BIOS labels so the menu fits 80 columns**
- "now F7F0107, newest F7F0108 (own risk)", "F7F0108 waits for a restart",
"F7F0107 is up to date"; shorter dry-run line.
- `99a95ab` **fix: Kernel legend only mentions the LEDs on a Steam Machine**
- `c0014ee` **docs: Last commit hash in the changelog; 0.7.0 was released on its own (#6, #7)**
## 0.7.0 - 2026-09-24 (#6, #7)
The SteamOS theme comes from CachyOS's `cachyos-vapor` package, applied with
Vapor's own desktop layout, and gains the SteamOS desktop extras. The Steam
Machine LED driver works on every installed kernel and survives kernel updates.
- `978f5a1` **feat: SteamOS theme from cachyos-vapor with its desktop layout, plus SteamOS desktop extras**
- Installs `cachyos-vapor` instead of extracting Valve's `steamdeck-kde-presets`
into `~/.local/share`; removes it on disable if the wizard installed it.
- Applies the Vapor global theme with "Desktop and window layout": Steam Deck
wallpaper, SteamOS panel and launcher icon. Layout files are backed up and
restored; every key from Vapor's defaults goes through the undo journal.
- Restores the real previous color scheme on disable (CachyOS's BreezeDark
colors have no scheme name), instead of falling back to light Breeze.
- Registers Vapor as the dark theme, so Plasma's Dark Mode toggle shows on.
- Drops the extra SteamOS desktop defaults (GTK, Konsole, gsettings, panel tweaks).
- New SteamOS desktop extras, downloaded from Valve's newest
`steamdeck-kde-presets` (checksum verified) into `/usr/local`: Add to Steam,
Nested Desktop, the SteamOS Return to Gaming Mode icon, the Steam Keyboard
window rule and an empty KWallet (only when there is no wallet yet).
- Single user mode's launcher settings are re-applied after the layout reset.
- `7802d90` **fix: LED driver for every kernel, kernel headers at boot, console power button, kernel overview**
- DKMS override (`/etc/dkms/leds-valve-dkms.conf`) so the LED driver builds
for every installed kernel (the LTS kernel failed) and on kernel updates.
- `ensure-kernel-headers.service` installs missing kernel headers at boot, so
a newly added kernel gets the LED driver too.
- Power button sleeps and no automatic suspend on mains power (Steam Machine).
- Menu shows every kernel with its headers, Steam controller driver and LED
driver, plus whether the LEDs are loaded.
- The selected menu row is highlighted in full.
- `10b5164` **docs: Version 0.7.0 and CHANGELOG**
- `VERSION` in the entry point, shown in the menu header and the bundle.
- README and AGENTS.md updated for the theme, the extras and the LED driver.
- `eb3169e` **ci: Version and changelog in the release notes**
- The `latest` release's title shows the version, and its notes include this
version's section of `CHANGELOG.md`.
- Fix a shellcheck warning in the bundle (a variable name shared with
`lib/state.sh`), which would have failed the CI check.
- `d7231eb` **chore: Move the bundle tool to .github/tools/bundle.sh**
- `5c00504` **docs: Commit hashes in the changelog**
- `8aab946` **ci: A release per version (tag v0.7.0, ...) instead of overwriting "latest"**
- Each push to `main` publishes release `v$VERSION` with that version's
changelog; an already released version is never overwritten (pull requests
get a warning when `VERSION` wasn't bumped).
- Install with `.../releases/latest/download/setup-gamescope-boot.sh`, which
always points to the newest release.
- `1604ed1` **docs: Versioning and release rules in AGENTS.md**
- `6afef78` **ci: The latest tag follows the newest version tag**
- When a new version is released, the `latest` tag and release move to it
(bundle replaced), so the older `.../releases/download/latest/...` URL
also always gives the newest version.
## 0.6.2 - 2026-09-23
- `a501df2` **Update steam-machine.sh**: install and enable `inputplumber`
next to `steamos-manager`.
## 0.6.1 - 2026-09-23 (#5)
- `c0bd16e` **fix: Use curl | bash in the docs**: `bash <(...)` doesn't work in fish.
## 0.6.0 - 2026-09-23 (#4)
- `d654d44` **fix: Single-file build for curl, published by GitHub Actions**:
`tools/bundle.sh` inlines `lib/*.sh`; CI publishes it to the `latest` release.
## 0.5.0 - 2026-09-23 (#3)
- `b91fa4b` **fix: Replace the question flow with a menu that turns components on and off**
- `3d7f376` **fix: Interactive checkbox menu**: arrow keys to move, Space to select, Enter to run.
## 0.4.0 - 2026-09-23 (#2)
- `a0bc385` **fix: Add single user no password option with SDDM, fetch newest Vapor presets**
## 0.3.0 - 2026-09-23 (#1)
- `4ad6c2c` **fix: Fix setup bugs, add SteamOS desktop look and LED driver, split into modules**
## 0.2.0 - 2026-09-07
- `151a3bf` **add steam keyboard to desktop**
- `1999b6e` **Automatically change theme**
## 0.1.0 - 2026-09-06
First version of the script.
- `16a7739`, `97ee6f3`, `6be1051` **commit the script**
- `e1aee45`, `25bc8c0` **change return to gaming mode logo to original logo**
+104 -60
View File
@@ -1,14 +1,10 @@
# CachyOS Gamescope Boot Wizard # Steamify CachyOS
Turn a CachyOS desktop PC into a SteamOS-style console: it boots straight 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 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 desktop and back whenever you like - just like on a Steam Deck or Valve's
Steam Machine. Steam Machine.
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 you get ## What you get
The wizard is a menu: pick what you want, and it turns each part on or off. The wizard is a menu: pick what you want, and it turns each part on or off.
@@ -18,15 +14,21 @@ Everything you turn off is put back the way it was.
Desktop** in Steam works and so does going back (the **Return to Gaming Desktop** in Steam works and so does going back (the **Return to Gaming
Mode** icon on the desktop), you're back in gaming mode after a restart, Mode** icon on the desktop), you're back in gaming mode after a restart,
and Steam's on-screen keyboard (Steam + X) also works on the desktop. and Steam's on-screen keyboard (Steam + X) also works on the desktop.
2. **SteamOS theme** - Valve's own Vapor look for the desktop: wallpaper, 2. **SteamOS theme** - the Vapor look for the desktop (CachyOS's
dark mode and the SteamOS taskbar. `cachyos-vapor` package).
3. **Steam Deck/Machine icons** - Steam Deck button icons in gaming mode. 3. **Steam Deck/Machine icons** - Steam Deck button icons in gaming mode.
4. **Single user mode** - like SteamOS: never a login or lock screen, no 4. **Single user mode** - like SteamOS: never a login or lock screen, no
user switching or logging out. Typing a password with a controller is no user switching or logging out. Typing a password with a controller is no
fun. (Needs 1.) fun. (Needs 1.)
5. **Steam Machine support** - only shown on a Valve Steam Machine: the 5. **Steamify shortcut** - a "Steamify CachyOS" icon on the desktop and in
the app launcher (the Steam logo with a gear) that opens the newest version
of Steamify in Konsole, so you don't need the install command again.
6. **Steam Machine support** - only shown on a Valve Steam Machine: the
driver for the front LED bar, and the hardware settings in Steam (fan, driver for the front LED bar, and the hardware settings in Steam (fan,
TV control over HDMI-CEC). TV control over HDMI-CEC).
7. **Update BIOS** - only on a Steam Machine, opt-in and at your own risk:
installs the newest Steam Machine BIOS from Valve (see
[BIOS updates](#bios-updates-steam-machine)).
## Requirements ## Requirements
@@ -38,35 +40,48 @@ Everything you turn off is put back the way it was.
Open **Konsole** on your Plasma desktop and run: Open **Konsole** on your Plasma desktop and run:
```bash ```bash
curl -fsSL https://github.com/theupriser/cachyos-gamescope-boot/releases/download/latest/setup-gamescope-boot.sh | bash curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify.sh | bash
``` ```
That downloads and runs the latest single-file version, and works in any (Before 0.9.0 the script was called `setup-gamescope-boot.sh`; that name
still works in the download URL.) That downloads and runs the latest
single-file version, and works in any
shell, including fish (CachyOS's default). Prefer to keep a copy, or to look shell, including fish (CachyOS's default). Prefer to keep a copy, or to look
at the script first? Clone the repository instead: at the script first? Clone the repository instead:
```bash ```bash
git clone https://github.com/theupriser/cachyos-gamescope-boot.git git clone https://github.com/theupriser/steamify-cachyos.git
cd cachyos-gamescope-boot cd steamify-cachyos
./setup-gamescope-boot.sh ./steamify.sh
``` ```
Run it as yourself, not as root. You'll see a checklist: Run it as yourself, not as root. You'll see a checklist:
``` ```
[x] SteamOS conversion: boot into gaming mode, Steam on the desktop (now: off) [x] SteamOS conversion: boot into gaming mode, Steam on the desktop (now: off)
> [x] Install SteamOS theme: Vapor look, dark mode, SteamOS taskbar (now: off) > [x] Install SteamOS theme: Vapor look (cachyos-vapor) (now: off)
[x] Install Steam Deck/Machine icons: Deck button icons in gaming mode (now: off) [x] Install Steam Deck/Machine icons: Deck button icons in gaming mode (now: off)
[x] Single user mode: no password, lock screen or log out (SDDM) (now: off) [x] Single user mode: no password, lock screen or log out (SDDM) (now: off)
Kernels (> = running; controller = Steam controller driver, LEDs = LED bar driver built)
6.18.52-1-cachyos-lts linux-cachyos-lts headers yes controller yes
> 7.2.7-1-cachyos linux-cachyos headers yes controller yes
Up/Down move Space select Enter run a run + re-apply what's on q quit Up/Down move Space select Enter run a run + re-apply what's on q quit
``` ```
Move with the **arrow keys**, tick or untick with **Space**, and press Move with the **arrow keys**, tick or untick with **Space**, and press
**Enter** to run. "now:" shows what's on at the moment. The wizard then **Enter** to run. "now:" shows what's on at the moment. Below the list, every
installed kernel is shown with its headers and Steam controller driver; on a
Steam Machine also whether the LED bar driver is built for it, and whether
it's loaded right now, so a kernel update is easy to check. The wizard then
shows what it will change, asks your password once, and at the end offers shows what it will change, asks your password once, and at the end offers
to restart (needed for changes to how the PC starts). to restart (needed for changes to how the PC starts).
After each run the menu comes back with the new state, so you can change more
in one go; quit with **q**. When something needs a restart, you choose
between going back to the menu and restarting now; quitting asks once more.
Run it again whenever you like - to change your choices, to turn things off Run it again whenever you like - to change your choices, to turn things off
again, or after a CachyOS update (press `a` in the menu to re-apply again, or after a CachyOS update (press `a` in the menu to re-apply
everything that's on). everything that's on).
@@ -213,35 +228,29 @@ file), which makes Steam show Steam Deck button glyphs in gaming mode.
### SteamOS desktop look ### SteamOS desktop look
Downloads Valve's official `steamdeck-kde-presets` package from Valve's Installs CachyOS's `cachyos-vapor` package (the SteamOS Vapor theme the
SteamOS mirror and installs it into `~/.local/share` - nothing system-wide: CachyOS handheld edition uses) from the CachyOS repository and switches your
desktop to its Vapor global theme, including its desktop and window layout
(like ticking "Desktop and window layout" in System Settings): Vapor colors
and Plasma style, the SteamOS panel and launcher icon, and the Steam Deck
wallpaper. Run it from the Plasma desktop: applying the layout needs a
running Plasma session.
- the Vapor global theme, color scheme, Plasma style, wallpapers and icons; It also adds the SteamOS desktop extras that `cachyos-vapor` doesn't ship,
- the Vapor GTK theme, Konsole profile and user avatars; taken from the newest `steamdeck-kde-presets` on Valve's SteamOS mirror
- Valve's SteamOS desktop defaults, merged into your own config: Noto Sans (checksum verified) and installed to `/usr/local`:
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
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 - **Add to Steam** in the right-click menu of apps, AppImages and `.exe`
effect at the next desktop login. files, and in the launcher: adds them to Steam as non-Steam games;
- **Nested Desktop**: add it to Steam from the launcher, then start it in
gaming mode for a Plasma desktop inside gaming mode;
- the SteamOS **Return to Gaming Mode** icon (Steam logo with a return arrow);
- a window rule that keeps the **Steam keyboard** above other windows;
- an empty, password-less **KWallet**, only if you don't have a wallet yet,
so nothing asks for a wallet password after autologin.
**Package version.** The script always takes the newest Turning it off restores your previous look and panel layout, and removes `cachyos-vapor` again
`steamdeck-kde-presets` from the highest-numbered SteamOS release repository if the wizard installed it (and nothing else, like `cachyos-handheld`, needs it).
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) ### Front LED bar (Steam Machine)
@@ -251,10 +260,12 @@ The **Steam Machine support** component, only shown on Fremont hardware
- installs an AUR helper (`yay`) if neither `yay` nor `paru` is present; - installs an AUR helper (`yay`) if neither `yay` nor `paru` is present;
- installs the kernel headers for every installed kernel; - installs the kernel headers for every installed kernel;
- installs `leds-valve-dkms-git` from the AUR - Valve's own driver from the - installs `leds-valve-dkms-git` from the AUR - Valve's own driver from the
SteamOS kernel - builds it for the running kernel, and loads it at every SteamOS kernel - builds it for every installed kernel (so the LTS kernel
boot (`/etc/modules-load.d/leds-valve.conf`); works too), and loads it at every boot (`/etc/modules-load.d/leds-valve.conf`);
- adds a udev rule (`/etc/udev/rules.d/70-valve-leds-user.rules`) that gives - adds a udev rule (`/etc/udev/rules.d/70-valve-leds-user.rules`) that gives
your user the LED files, so Steam, which runs as you, can drive the bar; your user the LED files, so Steam, which runs as you, can drive the bar;
- console-like power button: it puts the machine to sleep, and it never
suspends by itself on mains power (the launcher's Shut Down still shuts down);
- installs and enables `steamos-manager`, the service Steam in gaming mode - installs and enables `steamos-manager`, the service Steam in gaming mode
uses for hardware settings (fan, HDMI-CEC, performance); it recognises the uses for hardware settings (fan, HDMI-CEC, performance); it recognises the
Steam Machine from its DMI data. Steam Machine from its DMI data.
@@ -262,9 +273,13 @@ The **Steam Machine support** component, only shown on Fremont hardware
Turning it off removes all of that again (the AUR helper is kept). Turning it off removes all of that again (the AUR helper is kept).
The LEDs appear as `/sys/class/leds/valve-leds*`. The driver's Makefile The LEDs appear as `/sys/class/leds/valve-leds*`. The driver's Makefile
builds against the running kernel, so after a kernel update, boot the new builds against the running kernel (`uname -r`) instead of the kernel DKMS
kernel and re-apply (`a` in the menu) or run builds for, so the wizard adds a DKMS override
`sudo dkms install leds-valve-dkms/0.1 -k "$(uname -r)"`. (`/etc/dkms/leds-valve-dkms.conf`) that passes DKMS's target kernel in. With
it, DKMS rebuilds the driver whenever a kernel or its headers are installed
or upgraded. A newly added kernel doesn't come with its headers, so
`ensure-kernel-headers.service` checks at every boot and installs any
missing `-headers` package, which makes DKMS build the driver for it.
Troubleshooting: Troubleshooting:
@@ -276,17 +291,41 @@ cat /var/lib/dkms/leds-valve-dkms/0.1/build/make.log
journalctl --user -b | grep -i led journalctl --user -b | grep -i led
``` ```
**BIOS updates** are not part of the wizard. Valve ships the Steam Machine ### BIOS updates (Steam Machine)
BIOS as `F7F0108.cab` in its `fremont-hw-support` package for fwupd. To
install it by hand (keep the machine on mains power and don't interrupt it): The **Update BIOS** item is only shown on a Steam Machine and is never ticked
by default. It shows the BIOS version you have now and the newest one Valve
ships (the `.cab` file in its `fremont-hw-support` package, looked up on
Valve's SteamOS mirror):
```bash
sudo dmidecode -s bios-version # current version
sudo pacman -S fwupd
curl -LO https://steamdeck-packages.steamos.cloud/archlinux-mirror/holo-3.9/os/x86_64/fremont-hw-support-20260807.1-1-any.pkg.tar.zst
mkdir fhw && tar -I zstd -xf fremont-hw-support-*.pkg.tar.zst -C fhw
sudo fwupdmgr install fhw/usr/share/fwupd/remotes.d/fremont/firmware/F7F0108.cab
``` ```
[ ] Update BIOS: now F7F0107, newest F7F0108 (own risk) (opt-in, runs once)
```
It can only be ticked when Valve has a newer BIOS than yours; when you're up
to date, when the newest version can't be looked up (offline), or when an
update is already waiting for a restart, it's greyed out.
When you run it, the wizard:
1. downloads Valve's package and checks its **SHA-256** against Valve's
repository, so it's exactly Valve's file;
2. asks **fwupd** whether the firmware is for this very machine (fwupd compares
the firmware's hardware IDs with the device) and stops if it isn't;
3. shows a large red **warning** with the current and the new version, and asks
whether you understand the risks (default: no);
4. shows the warning **again** and only continues when you type `UPDATE`;
5. hands the firmware to fwupd, which writes it during the **next restart**:
choose "restart now" or restart later yourself.
**At your own risk:** a failed or interrupted BIOS update can leave the machine
unable to start. Keep it on mains power, and never turn off the power, unplug
it or press the power button while it updates, including during the restart;
the screen can stay black for several minutes.
To walk through it without flashing anything, run the wizard with
`WIZARD_BIOS_DRY_RUN=1`: it downloads and checks the package and shows both
warnings, skips fwupd's device check, and only prints the install command.
### Manual session control ### Manual session control
@@ -310,7 +349,7 @@ gamescope-session, ...) stay installed.
| File | Responsibility | | File | Responsibility |
|---|---| |---|---|
| `setup-gamescope-boot.sh` | Entry point: checks, menu, apply, summary, restart | | `steamify.sh` | Entry point: checks, menu, apply, summary, restart |
| `lib/menu.sh` | The menu: detect, toggle, plan and apply changes | | `lib/menu.sh` | The menu: detect, toggle, plan and apply changes |
| `lib/state.sh` | Undo journal for KDE settings (`kset`/`krevert`) | | `lib/state.sh` | Undo journal for KDE settings (`kset`/`krevert`) |
| `lib/common.sh` | Output helpers, prompts, backups, plasmashell handling | | `lib/common.sh` | Output helpers, prompts, backups, plasmashell handling |
@@ -318,15 +357,20 @@ gamescope-session, ...) stay installed.
| `lib/login-manager.sh` | SteamOS conversion: SDDM or plasmalogin autologin, sync bridge | | `lib/login-manager.sh` | SteamOS conversion: SDDM or plasmalogin autologin, sync bridge |
| `lib/steam-desktop.sh` | Steam in the Plasma session; Steam Deck/Machine icons | | `lib/steam-desktop.sh` | Steam in the Plasma session; Steam Deck/Machine icons |
| `lib/desktop-shortcut.sh` | Return to Gaming Mode shortcut and its sudoers rule | | `lib/desktop-shortcut.sh` | Return to Gaming Mode shortcut and its sudoers rule |
| `lib/vapor-theme.sh` | SteamOS theme: Vapor, Valve's defaults, panel, dark mode | | `lib/vapor-theme.sh` | SteamOS theme: installs and switches to `cachyos-vapor` |
| `lib/steamos-extras.sh` | SteamOS desktop extras from Valve's package (Add to Steam, Nested Desktop, icon, keyboard rule, KWallet) |
| `lib/single-user.sh` | Single user mode: no lock screen, user switching or log out | | `lib/single-user.sh` | Single user mode: no lock screen, user switching or log out |
| `lib/wizard-shortcut.sh` | Steamify shortcut: desktop icon and launcher entry that run the newest release |
| `lib/bios.sh` | Update BIOS (Steam Machine, opt-in): current/newest version, double confirmation, fwupd |
| `lib/steam-machine.sh` | Steam Machine support: LED driver, LED access, steamos-manager | | `lib/steam-machine.sh` | Steam Machine support: LED driver, LED access, steamos-manager |
| `tools/bundle.sh` | Builds the single-file version (`dist/setup-gamescope-boot.sh`) | | `.github/tools/bundle.sh` | Builds the single-file version (`dist/steamify.sh`) |
| `.github/workflows/bundle.yml` | Builds and checks it on every push; publishes it on `main` | | `.github/workflows/bundle.yml` | Builds and checks it on every push; publishes it on `main` |
The single-file version is generated: on every push to `main`, GitHub The single-file version is generated: on every push to `main`, GitHub
Actions runs `tools/bundle.sh`, checks the result with `bash -n` and Actions runs `.github/tools/bundle.sh`, checks the result with `bash -n` and
shellcheck, and uploads it to the rolling `latest` release. It inlines shellcheck, and publishes it as a release per version (tag `v<VERSION>`,
never overwritten; the newest is marked latest, and the `latest` tag follows
it). It inlines
`lib/*.sh` and wraps the entry point in `main()`, so bash has read the whole `lib/*.sh` and wraps the entry point in `main()`, so bash has read the whole
file before anything runs; when stdin is a pipe (`curl | bash`) it reattaches file before anything runs; when stdin is a pipe (`curl | bash`) it reattaches
the terminal for the menu (set `WIZARD_KEEP_STDIN=1` to keep piped input). the terminal for the menu (set `WIZARD_KEEP_STDIN=1` to keep piped input).
+224
View File
@@ -0,0 +1,224 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Icon for the "Steamify CachyOS" shortcut (cachyos-gamescope-boot).
Derived from Valve's steam-gaming-return.svg (steamdeck-kde-presets,
GPL-2.0): the Return to Gaming Mode arrow replaced by a gear. -->
<svg
width="48"
height="48"
viewBox="0 0 48 48"
version="1.1"
id="svg11"
sodipodi:docname="steam-gaming-settings.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview11"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="true"
inkscape:zoom="8"
inkscape:cx="16.6875"
inkscape:cy="16.4375"
inkscape:window-width="1920"
inkscape:window-height="1014"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg11">
<inkscape:grid
id="grid11"
units="px"
originx="0"
originy="0"
spacingx="1"
spacingy="1"
empcolor="#3f3fff"
empopacity="0.25098039"
color="#3f3fff"
opacity="0.1254902"
empspacing="5"
enabled="true"
visible="true" />
</sodipodi:namedview>
<defs
id="defs4">
<linearGradient
id="blue-gradient-5">
<stop
style="stop-color:#054e87;stop-opacity:1"
offset="0"
id="stop1" />
<stop
style="stop-color:#1d99f3;stop-opacity:1"
offset="1"
id="stop2" />
</linearGradient>
<linearGradient
id="shadow-3">
<stop
style="stop-color:#31363b;stop-opacity:0.208"
offset="0"
id="stop3" />
<stop
style="stop-color:#31363b;stop-opacity:0;"
offset="1"
id="stop4" />
</linearGradient>
<linearGradient
xlink:href="#shadow-3"
id="linearGradient4199"
x1="21.969"
y1="1035.362"
x2="35.969"
y2="1049.362"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(10,-1018.3622)" />
<linearGradient
xlink:href="#shadow-3"
id="linearGradient4203"
gradientUnits="userSpaceOnUse"
x1="23"
y1="1035.362"
x2="36"
y2="1048.362"
gradientTransform="translate(13,-1021.3622)" />
<linearGradient
xlink:href="#shadow-3"
id="linearGradient4227"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-15.75,-1005.8622)"
x1="21.475"
y1="1035.158"
x2="34.475"
y2="1048.158" />
<linearGradient
xlink:href="#blue-gradient-5"
id="linearGradient4351"
x1="24"
y1="1008.362"
x2="24"
y2="1048.362"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,-1004.3622)" />
<linearGradient
xlink:href="#shadow-3"
id="linearGradient4373"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(5.9783302,-1021.8757)"
x1="21.085"
y1="1036.975"
x2="35.969"
y2="1049.362" />
<linearGradient
xlink:href="#shadow-3"
id="linearGradient4377"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-6.3297472,-1009.2141)"
x1="21.085"
y1="1036.975"
x2="35.969"
y2="1049.362" />
<linearGradient
xlink:href="#shadow-3"
id="linearGradient4381"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-2.8703079,-1005.5321)"
x1="21.969"
y1="1035.362"
x2="35.969"
y2="1049.362" />
<linearGradient
inkscape:collect="always"
xlink:href="#blue-gradient-5"
id="linearGradient12"
x1="34.848072"
y1="2.7402468"
x2="35.248451"
y2="46.980125"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#shadow-3"
id="linearGradient14"
x1="34.345119"
y1="37.896885"
x2="42.498322"
y2="45.409893"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#shadow-3"
id="linearGradient16"
x1="3.5050631"
y1="46.96999"
x2="3.6629491"
y2="50.93874"
gradientUnits="userSpaceOnUse" />
</defs>
<circle
style="fill:url(#linearGradient4351);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="24"
cy="23.999861"
r="20"
id="circle4" />
<circle
id="path43518"
cx="34.995033"
cy="34.995033"
style="fill:url(#linearGradient12);fill-rule:evenodd;stroke-width:0.999996;fill-opacity:1"
r="12" />
<path
style="fill:url(#linearGradient4203);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 35.923828,13.330078 C 37.206994,14.675942 38,16.493653 38,18.5 c -10e-7,4.142136 -3.357865,7.5 -7.5,7.5 -0.203438,-0.0015 -0.406734,-0.01127 -0.609375,-0.0293 l -5.896484,5.111328 c -0.0083,0.739616 -0.153346,1.471317 -0.427735,2.158203 -1.23712,3.074359 -4.732395,4.563589 -7.80664,3.326172 -0.24293,-0.09775 -0.470788,-0.215212 -0.69336,-0.339844 -0.04331,-0.0244 -0.08621,-0.04875 -0.128906,-0.07422 -0.211403,-0.125374 -0.414371,-0.260582 -0.605469,-0.408203 -0.0063,-0.0049 -0.01326,-0.0088 -0.01953,-0.01367 l -0.01172,0.002 8.19336,8.193359 C 22.994984,43.969391 23.497293,43.994196 24,44 a 20,20 0 0 0 1.310547,-0.04687 c 0.06663,-0.0043 0.132772,-0.01066 0.199219,-0.01563 a 20,20 0 0 0 1.845703,-0.222656 c 0.03199,-0.0054 0.06377,-0.01202 0.0957,-0.01758 a 20,20 0 0 0 1.396484,-0.3125 c 0.240477,-0.05998 0.481808,-0.116947 0.71875,-0.185547 A 20,20 0 0 0 30.7832,42.79492 c 0.221103,-0.07984 0.444706,-0.154851 0.662109,-0.242188 a 20,20 0 0 0 1.148441,-0.51367 C 32.813074,41.93437 33.035177,41.835015 33.25,41.722656 a 20,20 0 0 0 1.03125,-0.591797 c 0.231705,-0.139379 0.46789,-0.271541 0.693359,-0.419921 a 20,20 0 0 0 0.953125,-0.685547 c 0.206289,-0.153802 0.419094,-0.299475 0.619141,-0.460938 a 20,20 0 0 0 0.908203,-0.796875 c 0.190109,-0.173313 0.387023,-0.339092 0.570313,-0.519531 a 20,20 0 0 0 0.833984,-0.892578 c 0.163426,-0.181706 0.335418,-0.355335 0.492187,-0.542969 a 20,20 0 0 0 0.863282,-1.136719 c 0.102363,-0.141895 0.215754,-0.275269 0.314453,-0.419922 a 20,20 0 0 0 3.371094,-9.257812 c 6.3e-5,-6.43e-4 -6.4e-5,-0.0013 0,-0.002 a 20,20 0 0 0 0,-0.0059 A 20,20 0 0 0 44,24 20,20 0 0 0 43.779297,21.185547 Z"
id="path6" />
<path
style="fill:url(#linearGradient4227);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 5.7695312,29.3125 19.363281,43.431641 a 20,20 0 0 0 3.13086,0.49414 l -8.19336,-8.193359 -2.001953,-4.97461 z"
id="path9" />
<g
id="g11">
<path
d="m 26.586,23.6632 8.104641,-0.4201 1.91027,-6.7552 -5.59099,-4.4419 -6.871493,5.8329 z"
style="fill:url(#linearGradient4373);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4" />
<path
style="fill:url(#linearGradient4199);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 31.027515,23.9946 35,23 l 1.976562,-2.9766 -4,-4 -4.949047,4.9712 z"
id="path5" />
<path
style="fill:url(#linearGradient4377);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 15.316486,35.7061 21.675457,35.065 23.0333,27.5143 16.403747,25.7462 12.603749,30.3195 Z"
id="path7" />
<path
d="m 16.985332,35.8717 3.972485,-0.4165 2.210937,-3.4453 -3.0625,-3.1563 -6.933422,3.9868 z"
style="fill:url(#linearGradient4381);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path8" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 30.5,11 c -4.142135,0 -7.499999,3.357864 -7.5,7.5 0.0017,0.201484 0.01143,0.402819 0.0293,0.603516 -1.289636,1.575722 -4.80544,5.546997 -5.113281,5.902343 -2.416761,0.03284 -4.577852,1.51258 -5.482422,3.753907 -1.237432,3.074241 0.2518,6.569519 3.326172,7.80664 3.074245,1.237417 6.56952,-0.251813 7.80664,-3.326172 0.274389,-0.686886 0.419405,-1.418587 0.427735,-2.158203 l 5.896484,-5.111328 C 30.093269,25.98873 30.296562,25.998503 30.5,26 34.642135,26 37.999999,22.642136 38,18.5 37.999999,14.357864 34.642135,11 30.5,11 Z m 0,3 c 2.48528,0 4.499999,2.014718 4.5,4.5 -10e-7,2.485282 -2.01472,4.5 -4.5,4.5 -2.48528,0 -4.499999,-2.014718 -4.5,-4.5 1e-6,-2.485282 2.01472,-4.5 4.5,-4.5 z M 18.046875,27 c 0.496175,0.0058 0.986949,0.103774 1.447266,0.289062 2.049689,0.825003 3.042263,3.155585 2.216797,5.205079 -0.825003,2.049668 -3.155579,3.04224 -5.205079,2.216797 C 14.45617,33.885935 13.463596,31.555353 14.289062,29.505859 14.905127,27.975627 16.397393,26.980525 18.046875,27 Z"
id="path10" />
<circle
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="30.5"
cy="18.4998"
id="ellipse10"
r="3.5" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 5.0722656,22.564453 A 19,19 0 0 0 5,24 19,19 0 0 0 5.7695312,29.3125 l 11.1113278,4.470703 a 3,3 0 0 0 3.902344,-1.664062 3,3 0 0 0 -1.664062,-3.902344 z"
id="path11" />
</g>
<path id="gear" style="fill:#ffffff;fill-rule:evenodd;stroke:none" d="M 40.440,33.688 L 42.527,33.983 L 42.527,36.007 L 40.440,36.302 L 39.770,37.921 L 41.037,39.606 L 39.606,41.037 L 37.921,39.770 L 36.302,40.440 L 36.007,42.527 L 33.983,42.527 L 33.688,40.440 L 32.069,39.770 L 30.384,41.037 L 28.953,39.606 L 30.220,37.921 L 29.550,36.302 L 27.463,36.007 L 27.463,33.983 L 29.550,33.688 L 30.220,32.069 L 28.953,30.384 L 30.384,28.953 L 32.069,30.220 L 33.688,29.550 L 33.983,27.463 L 36.007,27.463 L 36.302,29.550 L 37.921,30.220 L 39.606,28.953 L 41.037,30.384 L 39.770,32.069 Z M 37.395,34.995 A 2.4,2.4 0 1 0 32.595,34.995 A 2.4,2.4 0 1 0 37.395,34.995 Z" />
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

+203
View File
@@ -0,0 +1,203 @@
#!/bin/bash
# "Update BIOS" menu item, only on a Steam Machine and always opt-in: flashes
# the newest Steam Machine BIOS from Valve's fremont-hw-support package with
# fwupd. It's an action, not an on/off component: never preselected, never
# re-applied, and there is nothing to turn off afterwards.
# Sourced by steamify.sh; not meant to be run on its own.
BIOS_REPO_PREFIX=holo
# WIZARD_BIOS_DRY_RUN=1 walks through the whole BIOS update (download,
# checksum, both warnings) but never flashes: fwupd's device check is
# skipped and the install is only printed. For testing, e.g. in a VM.
BIOS_DRY_RUN="${WIZARD_BIOS_DRY_RUN:-}"
bios_available() { detect_valve_fremont; }
bios_status() { return 1; }
bios_disable() { return 0; }
bios_current() {
cat /sys/class/dmi/id/bios_version 2>/dev/null || echo unknown
}
bios_lookup_newest() {
# Sets BIOS_REPO, BIOS_PKG, BIOS_SHA256 and BIOS_NEWEST (e.g. F7F0108)
# from the newest holo-X.Y repository on Valve's mirror: its .files
# database names the firmware file, its .db gives the package checksum.
# Only once per run, and with short timeouts so an offline machine
# doesn't hold up the menu.
[[ -n "${BIOS_LOOKED_UP:-}" ]] && return 0
BIOS_LOOKED_UP=1; BIOS_NEWEST=""
local tmp desc
BIOS_REPO="$(curl -fsL --max-time 10 "$VALVE_MIRROR/" | grep -oE "$BIOS_REPO_PREFIX-[0-9]+\.[0-9]+/" | tr -d / | sort -uV | tail -n 1)"
[[ -n "$BIOS_REPO" ]] || return 1
tmp="$(mktemp -d)"
if curl -fsL --max-time 30 "$VALVE_MIRROR/$BIOS_REPO/os/x86_64/$BIOS_REPO.files" -o "$tmp/files" &&
curl -fsL --max-time 30 "$VALVE_MIRROR/$BIOS_REPO/os/x86_64/$BIOS_REPO.db" -o "$tmp/db"; then
desc="$(tar -tf "$tmp/files" 2>/dev/null | grep -E '^fremont-hw-support-[0-9][^/]*/files$' | head -n 1)"
[[ -n "$desc" ]] && BIOS_CAB="$(tar -xOf "$tmp/files" "$desc" | grep -E '^usr/share/fwupd/.*\.cab$' | head -n 1)"
desc="${desc%/files}/desc"
BIOS_PKG="$(tar -xOf "$tmp/db" "$desc" 2>/dev/null | awk '/^%FILENAME%$/ { getline; print }')"
BIOS_SHA256="$(tar -xOf "$tmp/db" "$desc" 2>/dev/null | awk '/^%SHA256SUM%$/ { getline; print }')"
[[ -n "${BIOS_CAB:-}" && -n "$BIOS_PKG" && -n "$BIOS_SHA256" ]] && BIOS_NEWEST="$(basename "$BIOS_CAB" .cab)"
fi
rm -rf "$tmp"
[[ -n "$BIOS_NEWEST" ]]
}
bios_selectable() {
# Only when Valve has a newer BIOS than the one installed; the menu
# greys the item out otherwise (up to date, or newest unknown/offline).
[[ -z "${BIOS_NEEDS_RESTART:-}" && -n "${BIOS_NEWEST:-}" && "$BIOS_NEWEST" != "$(bios_current)" ]]
}
bios_label() {
# Menu label with the current and the newest version; kept short so the
# row (with "(not available)" when greyed out) fits 80 columns.
local current
current="$(bios_current)"
if [[ -n "${BIOS_NEEDS_RESTART:-}" ]]; then
echo "Update BIOS: $BIOS_NEWEST waits for a restart"
elif [[ -z "${BIOS_NEWEST:-}" ]]; then
echo "Update BIOS: now $current, newest unknown (offline?)"
elif [[ "$BIOS_NEWEST" == "$current" ]]; then
echo "Update BIOS: $current is up to date"
else
echo "Update BIOS: now $current, newest $BIOS_NEWEST (own risk)"
fi
}
BIOS_BOX_WIDTH=68
bios_box_line() {
# bios_box_line [text]: one line of the warning box, padded to the same
# visible width (colour codes don't count) so the right edge lines up.
local text="${1:-}" plain pad
plain="$(printf '%b' "$text" | sed 's/\x1b\[[0-9;]*m//g')"
pad=$(( BIOS_BOX_WIDTH - 6 - ${#plain} ))
(( pad < 0 )) && pad=0
printf ' %b██%b %b%*s%b██%b\n' "$c_red" "$c_reset" "$text" "$pad" "" "$c_red" "$c_reset"
}
bios_box_border() {
# Block characters: Konsole draws a long coloured row of # narrower
# than the box's other lines, so its right edge wouldn't line up.
printf ' %b%s%b\n' "$c_red" "$(printf '█%.0s' $(seq "$BIOS_BOX_WIDTH"))" "$c_reset"
}
bios_disclaimer() {
# bios_disclaimer <title> [line...]: the warning box; extra lines are
# shown above the fixed risks.
local title="$1" line b="$c_bold" n="$c_reset"
shift
echo
bios_box_border
bios_box_line
bios_box_line "$c_red$b$title$n"
bios_box_line
bios_box_border
for line in "$@"; do bios_box_line "$line"; done
bios_box_line
bios_box_line "- A failed or interrupted BIOS update can leave the machine"
bios_box_line " unable to start (bricked). This wizard, CachyOS and Valve"
bios_box_line " take no responsibility for that."
bios_box_line "- ${b}NEVER turn off the power, unplug the machine or press${n}"
bios_box_line " ${b}the power button while the update runs${n}, including the"
bios_box_line " restart(s) afterwards, when the firmware is written."
bios_box_line "- The screen can stay black for several minutes. Wait."
bios_box_line "- Only on mains power, with all other programs closed."
bios_box_line
bios_box_border
echo
}
bios_fits_device() {
# bios_fits_device <cab>: does fwupd match this firmware to a device in
# this machine? It compares the file's hardware IDs (GUIDs) with the
# hardware; for a file that doesn't fit it reports an UpdateError.
local details
details="$(sudo fwupdmgr get-details --json "$1" 2>/dev/null)" || return 1
grep -q '"Guid"' <<< "$details" && ! grep -q '"UpdateError"' <<< "$details"
}
bios_enable() {
if ! bios_lookup_newest; then
err "Couldn't find the newest Steam Machine BIOS on Valve's mirror ($VALVE_MIRROR)."
return 1
fi
local current tmp compatible
current="$(bios_current)"
[[ -n "$BIOS_DRY_RUN" ]] && warn "DRY RUN (WIZARD_BIOS_DRY_RUN): nothing will be flashed."
if [[ "$current" == "$BIOS_NEWEST" ]]; then
ok "The BIOS is already the newest version ($current); nothing to do."
return 0
fi
# Download and check everything before asking: the warnings only come
# when the file is Valve's (SHA-256 from Valve's repo database) and fwupd
# confirms it's firmware for this very machine.
pacman -Q fwupd >/dev/null 2>&1 || sudo pacman -S --needed --noconfirm fwupd ||
{ err "Installing fwupd failed."; return 1; }
tmp="$(mktemp -d)"
info "Downloading $BIOS_PKG ($BIOS_REPO)..."
if ! curl -fsSL "$VALVE_MIRROR/$BIOS_REPO/os/x86_64/$BIOS_PKG" -o "$tmp/pkg.tar.zst" ||
! echo "$BIOS_SHA256 $tmp/pkg.tar.zst" | sha256sum -c --quiet - ||
! tar -I unzstd -xf "$tmp/pkg.tar.zst" -C "$tmp" "$BIOS_CAB"; then
err "Downloading or verifying $BIOS_PKG failed; the BIOS was not touched."
rm -rf "$tmp"
return 1
fi
ok "Checksum OK: this is Valve's $BIOS_PKG."
local yes="$c_green$c_bold" b="$c_bold" n="$c_reset"
if [[ -n "$BIOS_DRY_RUN" ]]; then
warn "Dry run: skipping fwupd's check that the firmware fits this machine."
compatible="${c_yellow}${b}not checked${n} (dry run)"
elif bios_fits_device "$tmp/$BIOS_CAB"; then
ok "fwupd confirms BIOS $BIOS_NEWEST is firmware for this machine."
compatible="${yes}yes${n} (checked by fwupd)"
else
err "fwupd says BIOS $BIOS_NEWEST is not for this machine's hardware; not installing it."
rm -rf "$tmp"
return 1
fi
bios_disclaimer "WARNING: BIOS UPDATE - ENTIRELY AT YOUR OWN RISK" \
"Current BIOS: ${b}$current${n}" \
"New BIOS: ${b}$BIOS_NEWEST${n}" \
"Checksum: ${yes}OK${n} (Valve's package)" \
"Compatible: $compatible"
if ! ask_yn "Do you understand the risks and want to continue?" n; then
info "BIOS update cancelled; nothing was changed."; rm -rf "$tmp"; return 0
fi
bios_disclaimer "LAST CHANCE: THIS FLASHES BIOS $BIOS_NEWEST" \
"After this, keep the power on until the machine has fully" \
"started again. Don't touch it, even if the screen is black."
local reply
read -rp "$(echo -e "${c_red}${c_bold}Type UPDATE (in capitals) to flash the BIOS, anything else cancels:${c_reset} ")" reply
if [[ "$reply" != UPDATE ]]; then
info "BIOS update cancelled; nothing was changed."; rm -rf "$tmp"; return 0
fi
if [[ -n "$BIOS_DRY_RUN" ]]; then
ok "Dry run: would run: fwupdmgr install -y --no-reboot-check $(basename "$BIOS_CAB")"
ok "Dry run finished; nothing was flashed."
# Treated as staged, so the restart choices that follow a real
# update show up too; restarting only prints (see restart_now).
BIOS_NEEDS_RESTART=1
rm -rf "$tmp"
return 0
fi
info "Handing BIOS $BIOS_NEWEST to fwupd. Do NOT turn off the power from now on."
# -y: we already asked twice; --no-reboot-check: the wizard's own
# restart question comes at the end.
if ! sudo fwupdmgr install -y --no-reboot-check "$tmp/$BIOS_CAB"; then
err "fwupd could not install the BIOS update (see above); the BIOS was not changed."
rm -rf "$tmp"
return 1
fi
rm -rf "$tmp"
BIOS_NEEDS_RESTART=1
ok "BIOS $BIOS_NEWEST is staged. It is written during the next restart:"
warn "keep the power on and don't touch the machine until it has fully started again."
}
+2 -2
View File
@@ -1,8 +1,8 @@
#!/bin/bash #!/bin/bash
# Output helpers, prompts and small shared utilities. # Output helpers, prompts and small shared utilities.
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own. # Sourced by steamify.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" c_reset="\033[0m"; c_bold="\033[1m"; c_green="\033[32m"; c_yellow="\033[33m"; c_red="\033[31m"; c_cyan="\033[36m"; c_dim="\033[2m"
info() { echo -e "${c_cyan}[INFO]${c_reset} $*"; } info() { echo -e "${c_cyan}[INFO]${c_reset} $*"; }
ok() { echo -e "${c_green}[OK]${c_reset} $*"; } ok() { echo -e "${c_green}[OK]${c_reset} $*"; }
+12 -3
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# "Return to Gaming Mode" desktop shortcut and the sudoers rule it needs. # "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. # Sourced by steamify.sh; not meant to be run on its own.
create_desktop_shortcut() { create_desktop_shortcut() {
# Determine the home directory of the target user # Determine the home directory of the target user
@@ -43,9 +43,10 @@ EOF
# SteamOS itself uses Valve's "gaming-return" icon (Steam logo with a # SteamOS itself uses Valve's "gaming-return" icon (Steam logo with a
# return arrow; the Deck-style arrow is only used on Steam Deck # 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. # hardware). The theme installs it (lib/steamos-extras.sh) and updates
# the shortcut via set_shortcut_icon; without it, use Steam's icon.
local shortcut_icon="steam" local shortcut_icon="steam"
[[ -f "$user_home/.local/share/icons/hicolor/scalable/actions/gaming-return.svg" ]] && [[ -f /usr/local/share/icons/hicolor/scalable/actions/gaming-return.svg ]] &&
shortcut_icon="gaming-return" shortcut_icon="gaming-return"
# 2. Generate the .desktop shortcut with instant session switcher strings # 2. Generate the .desktop shortcut with instant session switcher strings
@@ -69,6 +70,14 @@ EOF
ok "Desktop shortcut created successfully." ok "Desktop shortcut created successfully."
} }
set_shortcut_icon() {
# set_shortcut_icon <icon>: update an existing shortcut, if any.
local shortcut
shortcut="$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop")/Return to Gaming Mode.desktop"
[[ -f "$shortcut" ]] && sed -i "s/^Icon=.*/Icon=$1/" "$shortcut"
return 0
}
remove_desktop_shortcut() { remove_desktop_shortcut() {
local desktop_dir local desktop_dir
desktop_dir="$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop")" desktop_dir="$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop")"
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Booting into gaming mode: autologin into gamescope with SDDM or # Booting into gaming mode: autologin into gamescope with SDDM or
# plasma-login-manager (plus the session sync bridge the latter needs). # plasma-login-manager (plus the session sync bridge the latter needs).
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own. # Sourced by steamify.sh; not meant to be run on its own.
BASE_CONF="/etc/plasmalogin.conf" BASE_CONF="/etc/plasmalogin.conf"
+58 -15
View File
@@ -1,23 +1,40 @@
#!/bin/bash #!/bin/bash
# Interactive menu: detects which components are on, lets the user pick what # Interactive menu: detects which components are on, lets the user pick what
# they want, then turns components on or off to match. # they want, then turns components on or off to match.
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own. # Sourced by steamify.sh; not meant to be run on its own.
# Menu order. Components are turned on in this order and off in reverse; # Menu order. Components are turned on in this order and off in reverse;
# gaming must come first (single user builds on it). # gaming must come first (single user builds on it).
COMPONENTS=(gaming theme glyphs single machine) COMPONENTS=(gaming theme glyphs single launcher machine bios)
# One-off actions rather than on/off components: never preselected, never
# re-applied, not listed as on or off.
ACTIONS=(bios)
declare -A LABEL=( declare -A LABEL=(
[gaming]="SteamOS conversion: boot into gaming mode, Steam on the desktop" [gaming]="SteamOS conversion: boot into gaming mode, Steam on the desktop"
[theme]="Install SteamOS theme: Vapor look, dark mode, SteamOS taskbar" [theme]="Install SteamOS theme: Vapor look (cachyos-vapor)"
[glyphs]="Install Steam Deck/Machine icons: Deck button icons in gaming mode" [glyphs]="Install Steam Deck/Machine icons: Deck button icons in gaming mode"
[single]="Single user mode: no password, lock screen or log out (SDDM)" [single]="Single user mode: no password, lock screen or log out (SDDM)"
[launcher]="Steamify shortcut: desktop icon to run this again"
[machine]="Steam Machine support: LED bar driver, hardware settings in Steam" [machine]="Steam Machine support: LED bar driver, hardware settings in Steam"
[bios]="Update BIOS"
) )
declare -A CURRENT WANTED declare -A CURRENT WANTED
component_available() { component_available() {
[[ "$1" != machine ]] || machine_available case "$1" in
machine) machine_available ;;
bios) bios_available ;;
esac
}
is_action() { [[ " ${ACTIONS[*]} " == *" $1 "* ]]; }
component_selectable() {
# Greyed out and not tickable when it has nothing to do.
case "$1" in
bios) bios_selectable ;;
esac
} }
detect_components() { detect_components() {
@@ -27,16 +44,19 @@ detect_components() {
if "${c}_status"; then CURRENT[$c]=1; any=true; else CURRENT[$c]=0; fi if "${c}_status"; then CURRENT[$c]=1; any=true; else CURRENT[$c]=0; fi
WANTED[$c]=${CURRENT[$c]} WANTED[$c]=${CURRENT[$c]}
done done
# First run: preselect the full SteamOS experience. # Shows the current and newest BIOS version.
bios_available && { bios_lookup_newest; LABEL[bios]="$(bios_label)"; }
# First run: preselect the full SteamOS experience (never an action).
if [[ "$any" == false ]]; then if [[ "$any" == false ]]; then
for c in "${COMPONENTS[@]}"; do for c in "${COMPONENTS[@]}"; do
component_available "$c" && WANTED[$c]=1 component_available "$c" && ! is_action "$c" && WANTED[$c]=1
done done
fi fi
} }
toggle_component() { toggle_component() {
local c="$1" local c="$1"
component_selectable "$c" || return 1
WANTED[$c]=$(( 1 - WANTED[$c] )) WANTED[$c]=$(( 1 - WANTED[$c] ))
# Single user mode only makes sense on top of the SteamOS conversion. # Single user mode only makes sense on top of the SteamOS conversion.
if [[ "$c" == single && "${WANTED[single]}" == 1 ]]; then WANTED[gaming]=1; fi if [[ "$c" == single && "${WANTED[single]}" == 1 ]]; then WANTED[gaming]=1; fi
@@ -51,17 +71,29 @@ show_menu() {
for c in "${COMPONENTS[@]}"; do for c in "${COMPONENTS[@]}"; do
component_available "$c" || continue component_available "$c" || continue
i=$((i + 1)); MENU_ITEMS[$i]=$c i=$((i + 1)); MENU_ITEMS[$i]=$c
now="off"; [[ "${CURRENT[$c]}" == 1 ]] && now="${c_green}on${c_reset} " # Pad the plain word, then colour it: colour codes would count as width.
now="off"; [[ "${CURRENT[$c]}" == 1 ]] && now="on"
is_action "$c" && now="-"
now="$(printf '%-6s' "$now")"
[[ "${CURRENT[$c]}" == 1 ]] && now="${now/on/${c_green}on${c_reset}}"
want="[ ]"; [[ "${WANTED[$c]}" == 1 ]] && want="[x]" want="[ ]"; [[ "${WANTED[$c]}" == 1 ]] && want="[x]"
printf " %-3s %-6b %-6s %s\n" "$i" "$now " "$want" "${LABEL[$c]}" if component_selectable "$c"; then
printf " %-3s %b %-6s %s\n" "$i" "$now" "$want" "${LABEL[$c]}"
else
printf " %b%-3s %-6s %-6s %s (not available)%b\n" "$c_dim" "$i" "$now" "$want" "${LABEL[$c]}" "$c_reset"
fi
done done
echo echo
echo -e "$KERNEL_OVERVIEW"
echo
} }
run_menu() { run_menu() {
# Sets REAPPLY. Returns 1 if the user quit. A checkbox list on a # Sets REAPPLY. Returns 1 if the user quit. A checkbox list on a
# terminal; a plain numbered prompt when input is piped (scripted runs). # terminal; a plain numbered prompt when input is piped (scripted runs).
REAPPLY=false REAPPLY=false
# Built once: the TUI redraws on every key press.
KERNEL_OVERVIEW="$(kernel_overview)"
if [[ -t 0 && -t 1 ]]; then if [[ -t 0 && -t 1 ]]; then
run_menu_tui run_menu_tui
else else
@@ -72,7 +104,7 @@ run_menu() {
draw_menu_tui() { draw_menu_tui() {
local cursor="$1" i=0 c box state line local cursor="$1" i=0 c box state line
printf '\033[H\033[2J' printf '\033[H\033[2J'
echo -e "${c_bold}CachyOS Steam Deck-style Gamescope Boot Wizard${c_reset}" echo -e "${c_bold}Steamify CachyOS${c_reset} v$VERSION"
echo "Pick what you want. Anything you untick is put back the way it was." echo "Pick what you want. Anything you untick is put back the way it was."
echo echo
MENU_ITEMS=() MENU_ITEMS=()
@@ -80,16 +112,24 @@ draw_menu_tui() {
component_available "$c" || continue component_available "$c" || continue
MENU_ITEMS[$i]=$c MENU_ITEMS[$i]=$c
box="[ ]"; [[ "${WANTED[$c]}" == 1 ]] && box="[${c_green}x${c_reset}]" box="[ ]"; [[ "${WANTED[$c]}" == 1 ]] && box="[${c_green}x${c_reset}]"
state="off"; [[ "${CURRENT[$c]}" == 1 ]] && state="${c_green}on${c_reset}" state=" (now: off)"; [[ "${CURRENT[$c]}" == 1 ]] && state=" (now: ${c_green}on${c_reset})"
is_action "$c" && state=" (opt-in, runs once)"
line="$box ${LABEL[$c]}" line="$box ${LABEL[$c]}"
if (( i == cursor )); then if ! component_selectable "$c"; then
echo -e " ${c_cyan}>${c_reset} ${c_bold}${line}${c_reset} (now: ${state})" # Greyed out: nothing to do (e.g. BIOS already up to date).
local mark=" "; (( i == cursor )) && mark=" ${c_cyan}>${c_reset} "
echo -e "${mark}${c_dim}[ ] ${LABEL[$c]} (not available)${c_reset}"
elif (( i == cursor )); then
# The green x's reset would end the bold too: re-enable it after.
echo -e " ${c_cyan}>${c_reset} ${c_bold}${line//"$c_reset"/"$c_reset$c_bold"}${c_reset}${state}"
else else
echo -e " ${line} (now: ${state})" echo -e " ${line}${state}"
fi fi
i=$((i + 1)) i=$((i + 1))
done done
echo echo
echo -e "$KERNEL_OVERVIEW"
echo
echo -e " ${c_bold}Up/Down${c_reset} move ${c_bold}Space${c_reset} select ${c_bold}Enter${c_reset} run ${c_bold}a${c_reset} run + re-apply what's on ${c_bold}q${c_reset} quit" echo -e " ${c_bold}Up/Down${c_reset} move ${c_bold}Space${c_reset} select ${c_bold}Enter${c_reset} run ${c_bold}a${c_reset} run + re-apply what's on ${c_bold}q${c_reset} quit"
} }
@@ -130,7 +170,8 @@ run_menu_lines() {
q|Q) return 1 ;; q|Q) return 1 ;;
*) *)
if [[ "$reply" =~ ^[0-9]+$ && -n "${MENU_ITEMS[$reply]:-}" ]]; then if [[ "$reply" =~ ^[0-9]+$ && -n "${MENU_ITEMS[$reply]:-}" ]]; then
toggle_component "${MENU_ITEMS[$reply]}" toggle_component "${MENU_ITEMS[$reply]}" ||
warn "Not available: ${LABEL[${MENU_ITEMS[$reply]}]}"
else else
warn "Unknown choice: $reply" warn "Unknown choice: $reply"
fi fi
@@ -153,6 +194,7 @@ plan_changes() {
for c in "${COMPONENTS[@]}"; do for c in "${COMPONENTS[@]}"; do
component_available "$c" || continue component_available "$c" || continue
[[ "${WANTED[$c]}" == 1 ]] || continue [[ "${WANTED[$c]}" == 1 ]] || continue
if is_action "$c"; then TO_ENABLE+=("$c"); continue; fi
if [[ "${CURRENT[$c]}" == 0 || "$REAPPLY" == true ]] || if [[ "${CURRENT[$c]}" == 0 || "$REAPPLY" == true ]] ||
[[ "$c" == gaming && "${CURRENT[single]}" != "${WANTED[single]}" ]]; then [[ "$c" == gaming && "${CURRENT[single]}" != "${WANTED[single]}" ]]; then
TO_ENABLE+=("$c") TO_ENABLE+=("$c")
@@ -170,7 +212,8 @@ apply_changes() {
"${c}_disable" || failed+=("$c") "${c}_disable" || failed+=("$c")
done done
for c in "${TO_ENABLE[@]}"; do for c in "${TO_ENABLE[@]}"; do
echo; echo -e "${c_bold}Turning on: ${LABEL[$c]}${c_reset}" if is_action "$c"; then echo; echo -e "${c_bold}Running: ${LABEL[$c]%%:*}${c_reset}"
else echo; echo -e "${c_bold}Turning on: ${LABEL[$c]}${c_reset}"; fi
"${c}_enable" || failed+=("$c") "${c}_enable" || failed+=("$c")
done done
FAILED=("${failed[@]}") FAILED=("${failed[@]}")
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# Required packages and AUR helper handling. # Required packages and AUR helper handling.
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own. # Sourced by steamify.sh; not meant to be run on its own.
aur_noninteractive_flags() { aur_noninteractive_flags() {
# Fully non-interactive install flags for the given AUR helper: no # Fully non-interactive install flags for the given AUR helper: no
+16 -10
View File
@@ -3,18 +3,31 @@
# log out, since typing a password with a controller is miserable. Goes # log out, since typing a password with a controller is miserable. Goes
# together with SDDM (see lib/login-manager.sh). All changes are journaled # together with SDDM (see lib/login-manager.sh). All changes are journaled
# per user (lib/state.sh), so turning it off restores KDE's previous state. # per user (lib/state.sh), so turning it off restores KDE's previous state.
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own. # Sourced by steamify.sh; not meant to be run on its own.
single_status() { single_status() {
[[ "$(kreadconfig6 --file kdeglobals --group "KDE Action Restrictions" --key action/lock_screen)" == false ]] [[ "$(kreadconfig6 --file kdeglobals --group "KDE Action Restrictions" --key action/lock_screen)" == false ]]
} }
single_launcher() {
# Launcher: only Sleep / Restart / Shut Down, no Session dropdown (where
# Log Out lives). Restricting action/logout instead would also hide
# Restart and Shut Down. Separate so the theme can re-apply it after
# replacing the Plasma layout. Edit only while plasmashell is stopped.
local applet grp
for applet in $(plasma_applets org.kde.plasma.kickoff); do
grp="Containments|${applet%%:*}|Applets|${applet#*:}|Configuration|General"
kset single plasma-org.kde.plasma.desktop-appletsrc "$grp" primaryActions 3
kset single plasma-org.kde.plasma.desktop-appletsrc "$grp" systemFavorites 'suspend,reboot,shutdown'
done
}
single_enable() { single_enable() {
info "Turning off the lock screen, user switching and logging out..." info "Turning off the lock screen, user switching and logging out..."
stop_plasmashell_for_edit stop_plasmashell_for_edit
# Hides Lock / Switch User in menus. # Hides Lock / Switch User in menus.
local action applet local action
for action in lock_screen switch_user start_new_session; do for action in lock_screen switch_user start_new_session; do
kset single kdeglobals "KDE Action Restrictions" "action/$action" false kset single kdeglobals "KDE Action Restrictions" "action/$action" false
done done
@@ -26,14 +39,7 @@ single_enable() {
# Ctrl+Alt+Del logout screen (format: current,default,description). # Ctrl+Alt+Del logout screen (format: current,default,description).
kset single kglobalshortcutsrc ksmserver "Lock Session" $'none,Screensaver\tMeta+L,Lock Session' kset single kglobalshortcutsrc ksmserver "Lock Session" $'none,Screensaver\tMeta+L,Lock Session'
kset single kglobalshortcutsrc ksmserver "Log Out" 'none,Ctrl+Alt+Del,Show Logout Screen' kset single kglobalshortcutsrc ksmserver "Log Out" 'none,Ctrl+Alt+Del,Show Logout Screen'
# Launcher: only Sleep / Restart / Shut Down, no Session dropdown (where single_launcher
# Log Out lives). Restricting action/logout instead would also hide
# Restart and Shut Down.
for applet in $(plasma_applets org.kde.plasma.kickoff); do
local grp="Containments|${applet%%:*}|Applets|${applet#*:}|Configuration|General"
kset single plasma-org.kde.plasma.desktop-appletsrc "$grp" primaryActions 3
kset single plasma-org.kde.plasma.desktop-appletsrc "$grp" systemFavorites 'suspend,reboot,shutdown'
done
restart_plasmashell_if_stopped restart_plasmashell_if_stopped
ok "Single user: no lock screen, user switching or log out." ok "Single user: no lock screen, user switching or log out."
+1 -1
View File
@@ -3,7 +3,7 @@
# off again exactly: the first time a component changes a key, the key's # off again exactly: the first time a component changes a key, the key's
# previous value is recorded, and reverting the component puts it back (or # previous value is recorded, and reverting the component puts it back (or
# deletes the key if it didn't exist before). # deletes the key if it didn't exist before).
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own. # Sourced by steamify.sh; not meant to be run on its own.
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/cachyos-gamescope-boot" STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/cachyos-gamescope-boot"
UNSET=$'\x01' UNSET=$'\x01'
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Steam in the Plasma desktop session: gamepad UI args, virtual keyboard, # Steam in the Plasma desktop session: gamepad UI args, virtual keyboard,
# autostart. Part of the SteamOS conversion (gaming_enable/gaming_disable). # autostart. Part of the SteamOS conversion (gaming_enable/gaming_disable).
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own. # Sourced by steamify.sh; not meant to be run on its own.
steam_enable() { steam_enable() {
info "Applying non-optional SteamOS environment & keyboard fixes..." info "Applying non-optional SteamOS environment & keyboard fixes..."
+125 -16
View File
@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# "Steam Machine support" menu item, only on Valve Fremont hardware: the # "Steam Machine support" menu item, only on Valve Fremont hardware: the
# front LED bar driver, LED access for Steam, and steamos-manager. # front LED bar driver, LED access for Steam, and steamos-manager.
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own. # Sourced by steamify.sh; not meant to be run on its own.
detect_valve_fremont() { detect_valve_fremont() {
# Valve Steam Machine (Fremont) boards. The leds-valve driver's own DMI # Valve Steam Machine (Fremont) boards. The leds-valve driver's own DMI
@@ -51,6 +51,14 @@ install_valve_led_driver() {
# silently builds nothing, so modprobe later finds no module. # silently builds nothing, so modprobe later finds no module.
install_kernel_headers || return 1 install_kernel_headers || return 1
# The upstream Makefile builds against `uname -r` instead of the kernel
# DKMS targets, so every other installed kernel (e.g. linux-cachyos-lts)
# got the running kernel's tree and failed (wrong kernel, and gcc against
# a clang-built tree). DKMS reads this override after the package's
# dkms.conf; written before the install so its own build works too.
printf '%s\n' "# Written by cachyos-gamescope-boot: build for DKMS's target kernel." \
'MAKE[0]="make KVERSION=${kernelver}"' | sudo tee "$LED_DKMS_OVERRIDE" >/dev/null
if ! pacman -Qi leds-valve-dkms-git >/dev/null 2>&1; then 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)..." info "Installing leds-valve-dkms-git from the AUR via $aur_helper (non-interactive)..."
if ! "$aur_helper" -S "${flags[@]}" leds-valve-dkms-git; then if ! "$aur_helper" -S "${flags[@]}" leds-valve-dkms-git; then
@@ -61,19 +69,20 @@ install_valve_led_driver() {
ok "leds-valve-dkms-git already installed." ok "leds-valve-dkms-git already installed."
fi fi
# Build for the running kernel explicitly. The upstream Makefile uses # Build for every installed kernel that has headers, so booting another
# `uname -r` rather than DKMS's target kernel, so building for the # one (e.g. the LTS kernel) still has the driver.
# running kernel is the one case that reliably works. local kdir kver
local kver for kdir in /usr/lib/modules/*/build; do
kver="$(uname -r)" [[ -d "$kdir" ]] || continue
if ! dkms status -k "$kver" leds-valve-dkms 2>/dev/null | grep -q installed; then kver="$(basename "$(dirname "$kdir")")"
info "Building leds-valve for running kernel $kver..." dkms status -k "$kver" leds-valve-dkms 2>/dev/null | grep -q installed && continue
info "Building leds-valve for kernel $kver..."
if ! sudo dkms install leds-valve-dkms/0.1 -k "$kver"; then if ! sudo dkms install leds-valve-dkms/0.1 -k "$kver"; then
err "DKMS build failed. See the build log:" err "DKMS build for $kver failed. See the build log:"
err " /var/lib/dkms/leds-valve-dkms/0.1/build/make.log" err " /var/lib/dkms/leds-valve-dkms/0.1/build/make.log"
return 1 return 1
fi fi
fi done
# Load now, and on every boot. # Load now, and on every boot.
echo leds-valve | sudo tee /etc/modules-load.d/leds-valve.conf >/dev/null echo leds-valve | sudo tee /etc/modules-load.d/leds-valve.conf >/dev/null
@@ -96,6 +105,89 @@ install_valve_led_driver() {
} }
LED_UDEV_RULE="/etc/udev/rules.d/70-valve-leds-user.rules" LED_UDEV_RULE="/etc/udev/rules.d/70-valve-leds-user.rules"
LED_DKMS_OVERRIDE="/etc/dkms/leds-valve-dkms.conf"
HEADERS_SCRIPT="/usr/local/lib/cachyos-gamescope-boot/ensure-kernel-headers"
HEADERS_UNIT="/etc/systemd/system/ensure-kernel-headers.service"
install_headers_boot_check() {
# DKMS's pacman hook rebuilds leds-valve for every kernel whose headers
# get installed, but a newly added kernel (e.g. linux-cachyos-bore)
# comes without them, and a pacman hook can't install packages itself.
# So check at every boot and install missing headers, which triggers
# that hook and builds the driver for the new kernel.
sudo mkdir -p "$(dirname "$HEADERS_SCRIPT")"
sudo tee "$HEADERS_SCRIPT" > /dev/null << 'EOF'
#!/bin/bash
# Installed by cachyos-gamescope-boot: install missing -headers for every
# installed kernel, so DKMS builds leds-valve for it.
set -u
missing=()
for k in $(pacman -Qqo /usr/lib/modules/*/pkgbase 2>/dev/null | sort -u); do
pacman -Q "${k}-headers" >/dev/null 2>&1 && continue
pacman -Si "${k}-headers" >/dev/null 2>&1 && missing+=("${k}-headers")
done
[[ ${#missing[@]} -eq 0 ]] && exit 0
# Another pacman is running (e.g. an update): try again next boot.
[[ -e /var/lib/pacman/db.lck ]] && { echo "pacman is busy, skipping"; exit 0; }
echo "Installing missing kernel headers: ${missing[*]}"
exec pacman -S --needed --noconfirm "${missing[@]}"
EOF
sudo chmod 755 "$HEADERS_SCRIPT"
sudo tee "$HEADERS_UNIT" > /dev/null << EOF
[Unit]
Description=Install missing kernel headers so DKMS builds leds-valve for every kernel
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=$HEADERS_SCRIPT
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable ensure-kernel-headers.service
}
reload_powerdevil() {
qdbus6 org.kde.Solid.PowerManagement /org/kde/Solid/PowerManagement \
org.kde.Solid.PowerManagement.refreshStatus >/dev/null 2>&1 || true
}
kernel_overview() {
# Per installed kernel: headers (needed to build DKMS modules), the
# in-kernel Steam controller driver, and, on a Steam Machine, whether the
# LED driver is built for it. Printed in the menu so it's easy to verify
# a kernel update or a newly added kernel got everything.
local mark="${c_green}yes${c_reset}" miss="${c_red}no ${c_reset}" leds=false
detect_valve_fremont && command -v dkms >/dev/null 2>&1 && leds=true
local kdir k pkg headers hid led running line
local legend="> = running; controller = Steam controller driver"
[[ "$leds" == true ]] && legend+=", LEDs = LED bar driver built"
echo -e " ${c_bold}Kernels${c_reset} ($legend)"
for kdir in /usr/lib/modules/*/; do
k="$(basename "$kdir")"
[[ -f "$kdir/pkgbase" ]] || continue
pkg="$(< "$kdir/pkgbase")"
headers="$miss"; [[ -d "$kdir/build" ]] && headers="$mark"
hid="$miss"; compgen -G "$kdir/kernel/drivers/hid/hid-steam.ko*" >/dev/null && hid="$mark"
running=" "; [[ "$k" == "$(uname -r)" ]] && running="${c_cyan}>${c_reset} "
line="$(printf '%-23s %-18s headers %b controller %b' "$k" "$pkg" "$headers" "$hid")"
if [[ "$leds" == true ]]; then
led="$miss"; dkms status -k "$k" leds-valve-dkms 2>/dev/null | grep -q installed && led="$mark"
line+="$(printf ' LEDs %b' "$led")"
fi
echo -e " ${running}${line}"
done
if [[ "$leds" == true ]]; then
local loaded="$miss" nodes
lsmod | grep -q '^leds_valve' && loaded="$mark"
nodes="$(compgen -G '/sys/class/leds/valve-leds*' | wc -l)"
echo
echo -e " LED driver loaded: ${loaded} LED nodes: ${nodes} steamos-manager: $(systemctl is-active steamos-manager 2>/dev/null)"
fi
}
machine_available() { detect_valve_fremont; } machine_available() { detect_valve_fremont; }
@@ -106,6 +198,7 @@ machine_status() {
machine_enable() { machine_enable() {
ensure_aur_helper || { warn "Couldn't set up an AUR helper automatically. Install yay or paru, then run the wizard again."; return 1; } ensure_aur_helper || { warn "Couldn't set up an AUR helper automatically. Install yay or paru, then run the wizard again."; return 1; }
install_valve_led_driver || { warn "LED driver setup ran into a problem - see errors above."; return 1; } install_valve_led_driver || { warn "LED driver setup ran into a problem - see errors above."; return 1; }
install_headers_boot_check
# The LED files are root-only. Steam runs as the user; let it write them # The LED files are root-only. Steam runs as the user; let it write them
# in case it drives the bar directly (Valve's own privileged-write helper # in case it drives the bar directly (Valve's own privileged-write helper
@@ -118,13 +211,22 @@ EOF
sudo udevadm control --reload sudo udevadm control --reload
sudo udevadm trigger --subsystem-match=leds --action=add sudo udevadm trigger --subsystem-match=leds --action=add
# steamos-manager is what Steam in gaming mode talks to for hardware # steamos-manager and inputplumber are what Steam in gaming mode talks to
# settings (fan, HDMI-CEC TV control, performance); it knows the Steam # for hardware and controller settings; it knows the Steam Machine from its DMI data.
# Machine from its DMI data. info "Installing hardware manager packages (steamos-manager & inputplumber)..."
info "Installing steamos-manager (Steam Machine hardware settings in Steam)..." sudo pacman -S --needed --noconfirm steamos-manager inputplumber || { err "Installing hardware manager packages failed."; return 1; }
sudo pacman -S --needed --noconfirm steamos-manager || { err "Installing steamos-manager failed."; return 1; } sudo systemctl enable --now inputplumber.service
sudo systemctl enable --now steamos-manager.service sudo systemctl enable --now steamos-manager.service
systemctl --user enable steamos-manager.service 2>/dev/null systemctl --user enable steamos-manager.service 2>/dev/null
# Console-like power handling, as SteamOS's powerdevilrc (mains-power
# part; there is no battery): the power button sleeps instead of showing
# the logout screen, and the machine never suspends on its own (e.g.
# during a long download). The launcher's Shut Down still shuts down.
kset machine powerdevilrc "AC|SuspendAndShutdown" PowerButtonAction 1
kset machine powerdevilrc "AC|SuspendAndShutdown" AutoSuspendAction 0
kset machine powerdevilrc "AC|Display" LockBeforeTurnOffDisplay false
reload_powerdevil
ok "Steam Machine support on." ok "Steam Machine support on."
} }
@@ -132,10 +234,17 @@ machine_disable() {
info "Removing Steam Machine support..." info "Removing Steam Machine support..."
systemctl --user disable --now steamos-manager.service 2>/dev/null systemctl --user disable --now steamos-manager.service 2>/dev/null
sudo systemctl disable --now steamos-manager.service 2>/dev/null sudo systemctl disable --now steamos-manager.service 2>/dev/null
sudo pacman -Rns --noconfirm steamos-manager 2>/dev/null sudo systemctl disable --now inputplumber.service 2>/dev/null
sudo pacman -Rns --noconfirm steamos-manager inputplumber 2>/dev/null
sudo rm -f "$LED_UDEV_RULE" /etc/modules-load.d/leds-valve.conf sudo rm -f "$LED_UDEV_RULE" /etc/modules-load.d/leds-valve.conf
sudo udevadm control --reload sudo udevadm control --reload
sudo modprobe -r leds-valve 2>/dev/null sudo modprobe -r leds-valve 2>/dev/null
sudo pacman -Rns --noconfirm leds-valve-dkms-git 2>/dev/null sudo pacman -Rns --noconfirm leds-valve-dkms-git 2>/dev/null
krevert machine
reload_powerdevil
sudo systemctl disable ensure-kernel-headers.service 2>/dev/null
sudo rm -f "$LED_DKMS_OVERRIDE" "$HEADERS_UNIT" "$HEADERS_SCRIPT"
sudo rmdir "$(dirname "$HEADERS_SCRIPT")" 2>/dev/null
sudo systemctl daemon-reload
ok "Steam Machine support removed (the AUR helper, if installed, is kept)." ok "Steam Machine support removed (the AUR helper, if installed, is kept)."
} }
+128
View File
@@ -0,0 +1,128 @@
#!/bin/bash
# SteamOS desktop extras that cachyos-vapor doesn't ship, taken from the
# newest Valve steamdeck-kde-presets package: "Add to Steam" (file
# right-click menu and launcher), Nested Desktop (Plasma as a game inside
# gaming mode), the "Return to Gaming Mode" icon, the Steam Keyboard window
# rule and an empty KWallet. Part of the SteamOS theme component
# (lib/vapor-theme.sh). System files go to /usr/local (nothing
# package-owned); per-user settings go through the undo journal.
# Sourced by steamify.sh; not meant to be run on its own.
VALVE_MIRROR="https://steamdeck-packages.steamos.cloud/archlinux-mirror"
NESTED_DIR=/usr/local/share/applications/steam/holo-nested-desktop
EXTRAS_FILES=(
/usr/local/bin/holo-add-to-steam
/usr/local/bin/holo-nested-desktop
/usr/local/share/kio/servicemenus/steam.desktop
/usr/local/share/plasma/kickeractions/steam.desktop
/usr/local/share/icons/hicolor/scalable/actions/gaming-return.svg
"$NESTED_DIR"
)
# Group name for our rule in kwinrulesrc; KWin accepts any unique id.
STEAM_KEYBOARD_RULE=cachyos-gamescope-boot-steam-keyboard
fetch_valve_presets() {
# fetch_valve_presets <dir>: download and extract the newest
# steamdeck-kde-presets into <dir>. The newest SteamOS release repo
# (jupiter-3.9, jupiter-3.10, ...) is found on the mirror; its pacman
# database gives the exact file name and SHA-256, which is verified.
local dir="$1" repo desc file_name sha256
repo="$(curl -fsL "$VALVE_MIRROR/" | grep -oE 'jupiter-[0-9]+\.[0-9]+/' | tr -d / | sort -V | tail -n 1)"
[[ -n "$repo" ]] && curl -fsL "$VALVE_MIRROR/$repo/os/x86_64/$repo.db" -o "$dir/repo.db" ||
{ err "Couldn't read Valve's SteamOS package index ($VALVE_MIRROR)."; return 1; }
desc="$(tar -tf "$dir/repo.db" 2>/dev/null | grep -E '^steamdeck-kde-presets-[0-9][^/]*/desc$' | head -n 1)"
[[ -n "$desc" ]] || { err "No steamdeck-kde-presets in Valve's $repo repository."; return 1; }
file_name="$(tar -xOf "$dir/repo.db" "$desc" | awk '/^%FILENAME%$/ { getline; print }')"
sha256="$(tar -xOf "$dir/repo.db" "$desc" | awk '/^%SHA256SUM%$/ { getline; print }')"
info "Downloading $file_name ($repo)..."
curl -fsL "$VALVE_MIRROR/$repo/os/x86_64/$file_name" -o "$dir/presets.pkg.tar.zst" ||
{ err "Downloading $file_name failed."; return 1; }
echo "$sha256 $dir/presets.pkg.tar.zst" | sha256sum -c --quiet - ||
{ err "Checksum mismatch for $file_name; not using it."; return 1; }
tar -I unzstd -xf "$dir/presets.pkg.tar.zst" -C "$dir" usr/bin usr/share ||
{ err "Extracting $file_name failed."; return 1; }
}
extras_enable() {
info "Installing SteamOS desktop extras (Add to Steam, Nested Desktop, icons)..."
local pkg
for pkg in curl zstd kdialog; do
# holo-add-to-steam and holo-nested-desktop show errors with kdialog.
pacman -Q "$pkg" >/dev/null 2>&1 && continue
sudo pacman -S --needed --noconfirm "$pkg" && state_set theme "installed_$pkg" 1
done
local tmp_dir src
tmp_dir="$(mktemp -d)"
if ! fetch_valve_presets "$tmp_dir"; then
rm -rf "$tmp_dir"
return 1
fi
src="$tmp_dir/usr"
sudo install -D -m 755 "$src/bin/holo-add-to-steam" /usr/local/bin/holo-add-to-steam
sudo install -D -m 755 "$src/bin/holo-nested-desktop" /usr/local/bin/holo-nested-desktop
sudo install -D -m 644 "$src/share/kio/servicemenus/steam.desktop" /usr/local/share/kio/servicemenus/steam.desktop
sudo install -D -m 644 "$src/share/plasma/kickeractions/steam.desktop" /usr/local/share/plasma/kickeractions/steam.desktop
# gaming-return.svg is a symlink in the package; SteamOS uses it for the
# Return to Gaming Mode shortcut on non-Deck hardware.
sudo install -D -m 644 "$src/share/icons/hicolor/scalable/actions/steam-gaming-return.svg" \
/usr/local/share/icons/hicolor/scalable/actions/gaming-return.svg
# Nested Desktop with its Steam library art; the entry points at the
# art under /usr/share, which is where Valve puts it.
sudo rm -rf "$NESTED_DIR"
sudo mkdir -p "$(dirname "$NESTED_DIR")"
sudo cp -r "$src/share/applications/steam/holo-nested-desktop" "$NESTED_DIR"
sudo sed -i "s|/usr/share/applications/steam/holo-nested-desktop|$NESTED_DIR|" "$NESTED_DIR/holo-nested-desktop.desktop"
# An empty, password-less wallet (Valve's), so nothing asks for a wallet
# password: with autologin nobody typed one to unlock it. Only when the
# user has no wallet yet; an existing one is never touched.
local wallet="${XDG_DATA_HOME:-$HOME/.local/share}/kwalletd"
if [[ ! -f "$wallet/kdewallet.kwl" ]]; then
mkdir -p "$wallet"
install -m 600 "$src/share/kwalletd/kdewallet.kwl" "$src/share/kwalletd/kdewallet.salt" "$wallet/"
kset theme kwalletrc Wallet "First Use" false
fi
rm -rf "$tmp_dir"
sudo gtk-update-icon-cache -q -f -t /usr/local/share/icons/hicolor 2>/dev/null || true
kbuildsycoca6 >/dev/null 2>&1 || true
set_shortcut_icon gaming-return
# Keep Steam's on-screen keyboard (Steam + X) above other windows and out
# of the taskbar, like SteamOS's kwinrulesrc.
local rules rule="$STEAM_KEYBOARD_RULE"
rules="$(kreadconfig6 --file kwinrulesrc --group General --key rules)"
if [[ ",$rules," != *",$rule,"* ]]; then
kset theme kwinrulesrc "$rule" Description "Window settings for Steam Keyboard"
kset theme kwinrulesrc "$rule" above true
kset theme kwinrulesrc "$rule" aboverule 2
kset theme kwinrulesrc "$rule" skiptaskbar true
kset theme kwinrulesrc "$rule" skiptaskbarrule 2
kset theme kwinrulesrc "$rule" title "Steam Keyboard"
kset theme kwinrulesrc "$rule" titlematch 2
kset theme kwinrulesrc "$rule" type 16
kset theme kwinrulesrc "$rule" typerule 2
kset theme kwinrulesrc "$rule" wmclass steam
kset theme kwinrulesrc "$rule" wmclasscomplete true
kset theme kwinrulesrc "$rule" wmclassmatch 2
kset theme kwinrulesrc General rules "${rules:+$rules,}$rule"
kset theme kwinrulesrc General count "$(( $(kreadconfig6 --file kwinrulesrc --group General --key count --default 0) + 1 ))"
qdbus6 org.kde.KWin /KWin org.kde.KWin.reconfigure >/dev/null 2>&1 || true
fi
}
extras_disable() {
sudo rm -rf "${EXTRAS_FILES[@]}"
sudo gtk-update-icon-cache -q -f -t /usr/local/share/icons/hicolor 2>/dev/null || true
kbuildsycoca6 >/dev/null 2>&1 || true
set_shortcut_icon steam
local pkg
for pkg in kdialog zstd curl; do
[[ -n "$(state_get theme "installed_$pkg")" ]] && sudo pacman -R --noconfirm "$pkg" >/dev/null 2>&1
done
# The keyboard rule and kwalletrc are reverted by the theme's journal.
# The wallet itself stays: secrets may have been saved in it since.
return 0
}
+112 -246
View File
@@ -1,269 +1,135 @@
#!/bin/bash #!/bin/bash
# Valve's SteamOS Vapor desktop theme and SteamOS desktop defaults. # SteamOS Vapor desktop theme, from CachyOS's cachyos-vapor package.
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own. # Sourced by steamify.sh; not meant to be run on its own.
merge_kde_config() { VAPOR_LOOKANDFEEL=com.valve.vapor.desktop
# Merge a KDE-style ini file (e.g. one of Valve's /etc/xdg defaults) into VAPOR_DEFAULTS=/usr/share/plasma/look-and-feel/$VAPOR_LOOKANDFEEL/contents/defaults
# the user's own config (journaled, see lib/state.sh), so SteamOS defaults win over # Files the "Desktop and window layout" part replaces wholesale (panels,
# the distro's without touching package-owned files in /etc/xdg. # widgets, wallpaper); backed up as-is rather than journaled key by key.
# Nested groups ("[A][B]") are supported; immutable "[$i]" groups and THEME_LAYOUT_FILES=(plasma-org.kde.plasma.desktop-appletsrc plasmashellrc kdedefaults)
# comments are skipped.
local src="$1" target="$2" line key value
local groups=""
local skip=0
while IFS= read -r line || [[ -n "$line" ]]; do
line="${line%$'\r'}"
[[ -z "$line" || "$line" == \#* ]] && continue
if [[ "$line" == \[* ]]; then
skip=0; groups=""
[[ "$line" == *'[$'* ]] && { skip=1; continue; }
local rest="$line"
while [[ "$rest" =~ ^\[([^]]*)\](.*)$ ]]; do
groups+="${groups:+|}${BASH_REMATCH[1]}"
rest="${BASH_REMATCH[2]}"
done
continue
fi
(( skip )) && continue
[[ "$line" == *=* ]] || continue
key="${line%%=*}"; value="${line#*=}"
kset theme "$target" "$groups" "$key" "$value"
done < "$src"
}
install_vapor_theme() { current_colorscheme() {
# Installs Valve's official SteamOS "Vapor" Plasma theme (color scheme, # CachyOS ships its dark Breeze colors inline in kdeglobals without a
# Plasma look-and-feel package, wallpapers, icons) straight from Valve's # ColorScheme name, so fall back to the scheme matching ColorSchemeHash.
# own SteamOS package mirror, into the current user's ~/.local/share. local name hash f
# This only touches the invoking user's home directory, never system name="$(kreadconfig6 --file kdeglobals --group General --key ColorScheme)"
# files, so it's independent from everything else this script does. if [[ -z "$name" ]]; then
local mirror="https://steamdeck-packages.steamos.cloud/archlinux-mirror" hash="$(kreadconfig6 --file kdeglobals --group General --key ColorSchemeHash)"
local repo file_name sha256 tmp_dir for f in ~/.local/share/color-schemes/*.colors /usr/share/color-schemes/*.colors; do
tmp_dir="$(mktemp -d)" [[ -f "$f" && -n "$hash" ]] || continue
[[ "$(sha1sum < "$f")" == "$hash "* ]] && { name="$(basename "$f" .colors)"; break; }
# Take the package from the newest SteamOS release repo (jupiter-3.9,
# jupiter-3.10, ...), read from its pacman database: that gives the
# exact file name and checksum. Fall back to a known-good version.
info "Looking up the current SteamOS Vapor presets..."
repo="$(curl -fsL "$mirror/" | grep -oE 'jupiter-[0-9]+\.[0-9]+/' | tr -d / | sort -V | tail -n 1)"
if [[ -n "$repo" ]] && curl -fsL "$mirror/$repo/os/x86_64/$repo.db" -o "$tmp_dir/repo.db"; then
local desc
desc="$(tar -tf "$tmp_dir/repo.db" 2>/dev/null | grep -E '^steamdeck-kde-presets-[0-9][^/]*/desc$' | head -n 1)"
if [[ -n "$desc" ]]; then
file_name="$(tar -xOf "$tmp_dir/repo.db" "$desc" | awk '/^%FILENAME%$/ { getline; print }')"
sha256="$(tar -xOf "$tmp_dir/repo.db" "$desc" | awk '/^%SHA256SUM%$/ { getline; print }')"
fi
fi
if [[ -z "${file_name:-}" ]]; then
warn "Couldn't read Valve's package index; using the known-good version."
repo="jupiter-3.9"
file_name="steamdeck-kde-presets-3.9.4-2-any.pkg.tar.zst"
sha256=""
fi
info "Downloading $file_name ($repo)..."
if ! curl -fL "$mirror/$repo/os/x86_64/$file_name" -o "${tmp_dir}/presets.tar.zst"; then
err "Download failed. Valve may have moved/renamed this package;"
err "check $mirror for the current version."
rm -rf "$tmp_dir"
return 1
fi
if [[ -n "$sha256" ]] && ! echo "$sha256 ${tmp_dir}/presets.tar.zst" | sha256sum -c --quiet -; then
err "Checksum mismatch for $file_name; not installing it."
rm -rf "$tmp_dir"
return 1
fi
info "Extracting package contents..."
if ! tar -I unzstd -xf "${tmp_dir}/presets.tar.zst" -C "$tmp_dir"; then
err "Extraction failed (is 'zstd' installed? try: sudo pacman -S --needed zstd)"
rm -rf "$tmp_dir"
return 1
fi
info "Creating local configuration directories..."
mkdir -p ~/.local/share/color-schemes \
~/.local/share/plasma/desktoptheme \
~/.local/share/plasma/look-and-feel \
~/.local/share/wallpapers \
~/.local/share/icons/hicolor
info "Installing Steam Deck theme assets..."
[[ -d "${tmp_dir}/usr/share/color-schemes" ]] && cp -r "${tmp_dir}/usr/share/color-schemes/"* ~/.local/share/color-schemes/
[[ -d "${tmp_dir}/usr/share/plasma/desktoptheme/Vapor" ]] && cp -r "${tmp_dir}/usr/share/plasma/desktoptheme/Vapor" ~/.local/share/plasma/desktoptheme/
[[ -d "${tmp_dir}/usr/share/plasma/look-and-feel/com.valve.vapor.desktop" ]] && cp -r "${tmp_dir}/usr/share/plasma/look-and-feel/com.valve.vapor.desktop" ~/.local/share/plasma/look-and-feel/
[[ -d "${tmp_dir}/usr/share/wallpapers" ]] && cp -r "${tmp_dir}/usr/share/wallpapers/"* ~/.local/share/wallpapers/
[[ -d "${tmp_dir}/usr/share/icons/hicolor" ]] && cp -r "${tmp_dir}/usr/share/icons/hicolor/"* ~/.local/share/icons/hicolor/
# The rest of what SteamOS ships: GTK theme (Steam, Firefox, ...), Konsole
# profile and user avatars -- the same files the AUR package installs.
mkdir -p ~/.local/share/themes ~/.local/share/konsole ~/.local/share/plasma/avatars
[[ -d "${tmp_dir}/usr/share/themes/Vapor" ]] && cp -r "${tmp_dir}/usr/share/themes/Vapor" ~/.local/share/themes/
[[ -d "${tmp_dir}/usr/share/konsole" ]] && cp -r "${tmp_dir}/usr/share/konsole/"* ~/.local/share/konsole/
[[ -d "${tmp_dir}/usr/share/plasma/avatars" ]] && cp -r "${tmp_dir}/usr/share/plasma/avatars/"* ~/.local/share/plasma/avatars/
# Valve's SteamOS desktop defaults (fonts, Steam keyboard window rule,
# no welcome screen, light file indexing, GTK theme).
# Left out on purpose: autostart entries (Steam is started by our user
# unit), sddm/profile.d, and files hardcoding /home/deck or SteamOS tools.
info "Applying SteamOS desktop defaults..."
local f
# kscreenlockerrc is left to the single-user question.
for f in kdeglobals kwinrc kwinrulesrc kded5rc baloofilerc kcminputrc; do
[[ -f "${tmp_dir}/etc/xdg/$f" ]] && merge_kde_config "${tmp_dir}/etc/xdg/$f" "$f"
done
[[ -f "${tmp_dir}/etc/xdg/kded5rc" ]] && merge_kde_config "${tmp_dir}/etc/xdg/kded5rc" kded6rc
kset theme konsolerc "Desktop Entry" DefaultProfile Vapor.profile
mkdir -p ~/.config/gtk-3.0 ~/.config/gtk-4.0
kset theme ~/.config/gtk-3.0/settings.ini Settings gtk-theme-name Vapor
kset theme ~/.config/gtk-4.0/settings.ini Settings gtk-theme-name Vapor
# Vapor is a dark theme: tell GTK, libadwaita and portal-aware apps
# (Firefox, Steam's web views, Flatpaks) to use their dark variants too.
kset theme ~/.config/gtk-3.0/settings.ini Settings gtk-application-prefer-dark-theme true
kset theme ~/.config/gtk-4.0/settings.ini Settings gtk-application-prefer-dark-theme true
if command -v gsettings >/dev/null 2>&1; then
[[ -n "$(state_get theme gsettings_scheme)" ]] ||
state_set theme gsettings_scheme "$(gsettings get org.gnome.desktop.interface color-scheme 2>/dev/null)"
[[ -n "$(state_get theme gsettings_gtk)" ]] ||
state_set theme gsettings_gtk "$(gsettings get org.gnome.desktop.interface gtk-theme 2>/dev/null)"
gsettings set org.gnome.desktop.interface color-scheme prefer-dark 2>/dev/null || true
gsettings set org.gnome.desktop.interface gtk-theme Vapor 2>/dev/null || true
fi
[[ -f "${tmp_dir}/etc/xdg/gtk-2.0/gtkrc" ]] && cp "${tmp_dir}/etc/xdg/gtk-2.0/gtkrc" ~/.gtkrc-2.0
info "Cleaning up temporary files..."
rm -rf "$tmp_dir"
# Remember what to go back to when the theme is turned off again.
[[ -n "$(state_get theme lookandfeel)" ]] ||
state_set theme lookandfeel "$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage \
--default "$(kreadconfig6 --file /etc/xdg/kdeglobals --group KDE --key LookAndFeelPackage --default org.kde.breeze.desktop)")"
[[ -n "$(state_get theme colorscheme)" ]] ||
state_set theme colorscheme "$(kreadconfig6 --file kdeglobals --group General --key ColorScheme --default BreezeLight)"
local appletsrc="plasma-org.kde.plasma.desktop-appletsrc" applet
# Same launcher icon SteamOS sets (Vapor Deck plasmoid setup script).
for applet in $(plasma_applets org.kde.plasma.kickoff); do
kset theme "$appletsrc" "Containments|${applet%%:*}|Applets|${applet#*:}|Configuration|General" icon distributor-logo-steamdeck
done
# SteamOS scales tray icons to the panel height. Valve's setup script
# targets Plasma 5's separate tray containment; in Plasma 6 the setting
# lives on the systemtray applet itself.
for applet in $(plasma_applets org.kde.plasma.systemtray); do
kset theme "$appletsrc" "Containments|${applet%%:*}|Applets|${applet#*:}|General" scaleIconsToFit true
done
# SteamOS shows the date below the time.
for applet in $(plasma_applets org.kde.plasma.digitalclock); do
kset theme "$appletsrc" "Containments|${applet%%:*}|Applets|${applet#*:}|Configuration|Appearance" dateDisplayFormat BelowTime
done
# Wallpaper: Valve ships them as flat JPGs in usr/share/wallpapers.
local wallpaper="$HOME/.local/share/wallpapers/Steam Deck Logo Default.jpg" containment
if [[ -f "$wallpaper" ]]; then
for containment in $(grep -oP '^\[Containments\]\[\K[0-9]+(?=\]$)' ~/.config/"$appletsrc" 2>/dev/null); do
grep -q "^\[Containments\]\[$containment\]\[Wallpaper\]" ~/.config/"$appletsrc" || continue
kset theme "$appletsrc" "Containments|$containment|Wallpaper|org.kde.image|General" Image "file://$wallpaper"
done done
fi fi
echo "${name:-BreezeLight}"
}
# Vapor color scheme and Plasma style; the theme's own defaults use journal_vapor_defaults() {
# breeze-dark icons (there is no "Vapor" icon theme). # Write every setting from Vapor's defaults file ("[file][group]" +
kset theme kdeglobals KDE LookAndFeelPackage com.valve.vapor.desktop # key=value) through the undo journal, so disable restores the user's
kset theme kdeglobals Icons Theme breeze-dark # icons, cursor, splash, window decoration etc. The color scheme is
kset theme kdeglobals General ColorScheme Vapor # left to plasma-apply-colorscheme.
kset theme plasmarc Theme name Vapor local line file groups key rest
while IFS= read -r line || [[ -n "$line" ]]; do
# SteamOS uses a solid, full-width bottom panel at Plasma's stock 44px if [[ "$line" =~ ^\[([^]]+)\](.*)$ ]]; then
# height (CachyOS ships a floating 30px one). file="${BASH_REMATCH[1]}"; rest="${BASH_REMATCH[2]}"; groups=""
local panel while [[ "$rest" =~ ^\[([^]]*)\](.*)$ ]]; do
for panel in $(grep -oP '^\[PlasmaViews\]\[Panel \K[0-9]+(?=\]$)' ~/.config/plasmashellrc 2>/dev/null); do groups+="${groups:+|}${BASH_REMATCH[1]}"; rest="${BASH_REMATCH[2]}"
kset theme plasmashellrc "PlasmaViews|Panel $panel" floating 0 done
kset theme plasmashellrc "PlasmaViews|Panel $panel|Defaults" thickness 44 elif [[ "$line" == *=* && -n "$groups" ]]; then
done key="${line%%=*}"
[[ "$key" == ColorScheme ]] && continue
gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor 2>/dev/null || true kset theme "$file" "$groups" "$key" "${line#*=}"
kbuildsycoca6 --noincremental 2>/dev/null || true fi
done < "$VAPOR_DEFAULTS"
# Live changes need a running Plasma session; otherwise they are picked
# up from the config files above at the next Plasma login.
if [[ "${PLASMASHELL_WAS_RUNNING:-false}" == true ]]; then
info "Applying the theme to the running Plasma session..."
lookandfeeltool -a com.valve.vapor.desktop >/dev/null 2>&1 || true
# lookandfeeltool leaves the distro's color scheme active; apply it explicitly.
plasma-apply-colorscheme Vapor >/dev/null 2>&1 || true
qdbus6 org.kde.kded6 /modules/gtkconfig org.kde.gtkconfig.setGtkTheme Vapor >/dev/null 2>&1 || true
qdbus6 org.kde.KWin /KWin org.kde.KWin.reconfigure >/dev/null 2>&1 || true
else
info "No running Plasma session; the theme applies at your next desktop login."
fi
ok "SteamOS desktop look installed."
} }
theme_status() { theme_status() {
[[ "$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage)" == com.valve.vapor.desktop ]] [[ "$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage)" == "$VAPOR_LOOKANDFEEL" ]]
} }
theme_enable() { theme_enable() {
command -v curl >/dev/null 2>&1 || sudo pacman -S --needed --noconfirm curl if ! pacman -Q cachyos-vapor >/dev/null 2>&1; then
command -v unzstd >/dev/null 2>&1 || sudo pacman -S --needed --noconfirm zstd info "Installing cachyos-vapor..."
# Stop plasmashell first: the panel and wallpaper edits go to files it sudo pacman -S --needed --noconfirm cachyos-vapor || { err "Installing cachyos-vapor failed."; return 1; }
# would otherwise overwrite with its in-memory config on exit. # Only remove it again on disable if we were the ones installing it.
stop_plasmashell_for_edit state_set theme installed_pkg 1
install_vapor_theme fi
local rc=$? if [[ -z "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ]]; then
restart_plasmashell_if_stopped err "Run this from the Plasma desktop: applying the Vapor layout needs a running Plasma session."
(( rc == 0 )) || warn "Vapor theme install ran into a problem - see errors above." return 1
return $rc fi
# Only the first time, so a re-apply doesn't overwrite the originals.
local backup="$STATE_DIR/theme-layout" f
if [[ ! -d "$backup" ]]; then
mkdir -p "$backup"
for f in "${THEME_LAYOUT_FILES[@]}"; do
[[ -e ~/.config/"$f" ]] && cp -a ~/.config/"$f" "$backup/"
done
fi
# Applying a color scheme or cursor rewrites more than the journal
# covers (inline colors, GTK settings): remember what to re-apply.
[[ -n "$(state_get theme colorscheme)" ]] || state_set theme colorscheme "$(current_colorscheme)"
[[ -n "$(state_get theme cursor)" ]] ||
state_set theme cursor "$(kreadconfig6 --file kcminputrc --group Mouse --key cursorTheme --default breeze_cursors)"
for f in ~/.config/gtk-3.0/settings.ini ~/.config/gtk-4.0/settings.ini; do
[[ -f "$f" ]] && kset theme "$f" Settings gtk-cursor-theme-name \
"$(kreadconfig6 --file "$f" --group Settings --key gtk-cursor-theme-name)"
done
kset theme kdeglobals KDE LookAndFeelPackage "$VAPOR_LOOKANDFEEL"
# Vapor is a dark theme: register it as the dark one, or Plasma's Dark
# Mode toggle shows "off" and offers to switch to Breeze Dark.
kset theme kdeglobals KDE DefaultDarkLookAndFeel "$VAPOR_LOOKANDFEEL"
journal_vapor_defaults
# Journal the key, then clear it: plasma-apply-colorscheme skips a
# scheme that is already named in the config, without writing its colors.
kset theme kdeglobals General ColorScheme --delete
plasma-apply-colorscheme Vapor >/dev/null 2>&1 || true
info "Applying the Vapor theme with its desktop and window layout..."
# --resetLayout is "Desktop and window layout" in System Settings: the
# SteamOS panel, launcher icon and wallpaper. It needs plasmashell up.
lookandfeeltool -a "$VAPOR_LOOKANDFEEL" --resetLayout >/dev/null 2>&1 ||
{ err "lookandfeeltool failed."; return 1; }
# The new layout has a new launcher, without single user's settings.
if single_status; then
sleep 3
stop_plasmashell_for_edit
single_launcher
restart_plasmashell_if_stopped
fi
extras_enable || warn "SteamOS desktop extras not installed (see above); the theme itself is on."
ok "Vapor theme active."
} }
theme_disable() { theme_disable() {
info "Restoring the previous desktop look..." extras_disable
local lookandfeel colorscheme info "Restoring the previous desktop look and layout..."
lookandfeel="$(state_get theme lookandfeel org.kde.breeze.desktop)" plasma-apply-colorscheme "$(state_get theme colorscheme BreezeLight)" >/dev/null 2>&1 || true
colorscheme="$(state_get theme colorscheme BreezeLight)" plasma-apply-cursortheme "$(state_get theme cursor breeze_cursors)" >/dev/null 2>&1 || true
local backup="$STATE_DIR/theme-layout" f
stop_plasmashell_for_edit stop_plasmashell_for_edit
if has_journal theme; then if [[ -d "$backup" ]]; then
krevert theme for f in "${THEME_LAYOUT_FILES[@]}"; do
else rm -rf ~/.config/"$f"
# Installed by a version of this script without the undo journal: [[ -e "$backup/$f" ]] && cp -a "$backup/$f" ~/.config/
# undo the main settings it made.
kwriteconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage --delete
kwriteconfig6 --file kdeglobals --group General --key ColorScheme --delete
kwriteconfig6 --file kdeglobals --group Icons --key Theme --delete
kwriteconfig6 --file plasmarc --group Theme --key name --delete
local f
for f in ~/.config/gtk-3.0/settings.ini ~/.config/gtk-4.0/settings.ini; do
[[ -f "$f" ]] || continue
kwriteconfig6 --file "$f" --group Settings --key gtk-theme-name --delete
kwriteconfig6 --file "$f" --group Settings --key gtk-application-prefer-dark-theme --delete
done done
kwriteconfig6 --file konsolerc --group "Desktop Entry" --key DefaultProfile --delete rm -rf "$backup"
fi
if command -v gsettings >/dev/null 2>&1; then
local v
v="$(state_get theme gsettings_scheme)"
if [[ -n "$v" ]]; then gsettings set org.gnome.desktop.interface color-scheme "$v" 2>/dev/null
else gsettings reset org.gnome.desktop.interface color-scheme 2>/dev/null; fi
v="$(state_get theme gsettings_gtk)"
if [[ -n "$v" ]]; then gsettings set org.gnome.desktop.interface gtk-theme "$v" 2>/dev/null
else gsettings reset org.gnome.desktop.interface gtk-theme 2>/dev/null; fi
fi
rm -rf ~/.local/share/plasma/look-and-feel/com.valve.vapor.desktop \
~/.local/share/plasma/desktoptheme/Vapor \
~/.local/share/themes/Vapor \
~/.local/share/color-schemes/Vapor.colors ~/.local/share/color-schemes/VGUI.colors \
~/.local/share/konsole/Vapor.profile ~/.local/share/konsole/Vapor.colorscheme \
~/.local/share/wallpapers/Steam\ Deck\ Logo*.jpg ~/.local/share/wallpapers/Troll.jpg
rm -f ~/.gtkrc-2.0
kbuildsycoca6 --noincremental 2>/dev/null || true
if [[ -n "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ]]; then
lookandfeeltool -a "$lookandfeel" >/dev/null 2>&1 || true
plasma-apply-colorscheme "$colorscheme" >/dev/null 2>&1 || true
fi fi
# After the tools above, so keys that weren't set before are removed again.
krevert theme
single_status && single_launcher
restart_plasmashell_if_stopped restart_plasmashell_if_stopped
qdbus6 org.kde.KWin /KWin org.kde.KWin.reconfigure >/dev/null 2>&1 || true
if [[ -n "$(state_get theme installed_pkg)" ]]; then
# Fails, and stays installed, when another package (e.g.
# cachyos-handheld) depends on it.
sudo pacman -R --noconfirm cachyos-vapor >/dev/null 2>&1 ||
info "Keeping cachyos-vapor installed (another package needs it)."
fi
state_clear theme state_clear theme
ok "Previous desktop look restored." ok "Previous desktop look restored."
} }
+90
View File
@@ -0,0 +1,90 @@
#!/bin/bash
# "Steamify shortcut" menu item: a desktop icon and an app launcher entry that
# open the newest release of Steamify CachyOS in Konsole (curl | bash, like the
# install command in the README), so it's never out of date.
# Sourced by steamify.sh; not meant to be run on its own.
WIZARD_RELEASE="https://github.com/theupriser/steamify-cachyos/releases/latest/download"
WIZARD_URL="$WIZARD_RELEASE/steamify.sh"
# Steam logo with a gear (assets/ in the repo, published with every release).
WIZARD_ICON="${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor/scalable/apps/cachyos-gamescope-boot-wizard.svg"
WIZARD_LAUNCHER="${XDG_DATA_HOME:-$HOME/.local/share}/cachyos-gamescope-boot/run-wizard"
WIZARD_DESKTOP_NAME="cachyos-gamescope-boot-wizard.desktop"
WIZARD_APP_ENTRY="${XDG_DATA_HOME:-$HOME/.local/share}/applications/$WIZARD_DESKTOP_NAME"
wizard_desktop_file() {
echo "$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop")/$WIZARD_DESKTOP_NAME"
}
install_executable() {
# install_executable <dest> <mode>: stdin to <dest>, with its mode set
# before it appears. Plasma treats a desktop icon it saw while it wasn't
# executable yet as untrusted and opens it in an editor.
local dest="$1" tmp
mkdir -p "$(dirname "$dest")"
tmp="$(mktemp "$(dirname "$dest")/.tmp.XXXXXX")"
cat > "$tmp"
chmod "$2" "$tmp"
mv -f "$tmp" "$dest"
}
launcher_status() {
[[ -x "$WIZARD_LAUNCHER" && -f "$WIZARD_APP_ENTRY" ]]
}
launcher_enable() {
info "Adding the Steamify shortcut (desktop icon and app launcher entry)..."
install_executable "$WIZARD_LAUNCHER" 755 << EOF
#!/bin/bash
# Installed by Steamify CachyOS: run its newest release.
# pipefail: a failed download must count as a failure, not an empty script.
set -o pipefail
if ! curl -fsSL --max-time 30 "$WIZARD_URL" | bash; then
echo
echo "Couldn't download or run the wizard. Are you connected to the internet?"
echo
read -rp "Press Enter to close this window... " _
exit 1
fi
# Done: close the window after a countdown (Enter closes it right away).
echo
for (( i = 10; i > 0; i-- )); do
printf '\rClosing this window in %2d seconds (Enter to close now)... ' "\$i"
read -rs -t 1 _ && break
done
echo
EOF
# The icon comes with the release; without it, Steam's own icon.
local icon=steam tmp
tmp="$(mktemp)"
if curl -fsSL --max-time 20 "$WIZARD_RELEASE/steam-gaming-settings.svg" -o "$tmp" && grep -q '<svg' "$tmp"; then
install -D -m 644 "$tmp" "$WIZARD_ICON"
icon=cachyos-gamescope-boot-wizard
gtk-update-icon-cache -q -f -t "${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor" 2>/dev/null || true
else
warn "Couldn't download the shortcut's icon; using Steam's icon instead."
fi
rm -f "$tmp"
local entry
entry="[Desktop Entry]
Type=Application
Name=Steamify CachyOS
Comment=Turn the SteamOS-style parts of this PC on or off
Exec=konsole -e $WIZARD_LAUNCHER
Icon=$icon
Terminal=false
Categories=Game;System;Settings;"
printf '%s\n' "$entry" | install_executable "$WIZARD_APP_ENTRY" 644
printf '%s\n' "$entry" | install_executable "$(wizard_desktop_file)" 755
kbuildsycoca6 >/dev/null 2>&1 || true
ok "Steamify shortcut added: \"Steamify CachyOS\" on the desktop and in the launcher."
}
launcher_disable() {
rm -f "$(wizard_desktop_file)" "$WIZARD_APP_ENTRY" "$WIZARD_LAUNCHER" "$WIZARD_ICON"
rmdir "$(dirname "$WIZARD_LAUNCHER")" 2>/dev/null || true
kbuildsycoca6 >/dev/null 2>&1 || true
ok "Steamify shortcut removed."
}
-82
View File
@@ -1,82 +0,0 @@
#!/bin/bash
#
# setup-gamescope-boot.sh
#
# Wizard that makes a CachyOS (KDE Plasma) install behave like SteamOS:
# boot into a Steam Deck-style gamescope session, switch to the Plasma
# desktop and back, and reset to gamescope on the next boot/logout.
#
# It opens with a menu that detects which components are on and turns
# them on or off to match what the user picks; turning one off restores
# what was there before (system files from .bak-gamescope-wizard backups,
# KDE settings from the undo journal in lib/state.sh). Components live in
# lib/, see README.md. Safe to re-run.
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
for lib in common state packages login-manager single-user steam-desktop steam-machine vapor-theme desktop-shortcut menu; do
# shellcheck source=/dev/null
source "$SCRIPT_DIR/lib/$lib.sh"
done
require_root_helper
echo -e "${c_bold}CachyOS Steam Deck-style Gamescope Boot Wizard${c_reset}"
echo "Turn the SteamOS-style parts on or off. The menu shows what is on now;"
echo "anything you turn off is put back the way it was."
if ! command -v pacman >/dev/null 2>&1; then
err "This doesn't look like an Arch/CachyOS system (no pacman found). Aborting."
exit 1
fi
# Everything per-user (autologin user, Steam, theme, shortcut) is for the
# user running the script.
TARGET_USER="$(id -un)"
detect_components
run_menu || { info "Nothing changed."; exit 0; }
plan_changes
if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then
ok "Everything is already the way you want it."
exit 0
fi
echo
echo -e "${c_bold}This will:${c_reset}"
for c in "${TO_DISABLE[@]}"; do echo " - turn off: ${LABEL[$c]}"; done
for c in "${TO_ENABLE[@]}"; do
if [[ "${CURRENT[$c]}" == 1 ]]; then echo " - re-apply: ${LABEL[$c]}"; else echo " - turn on: ${LABEL[$c]}"; fi
done
ask_yn "Go ahead?" y || { info "Nothing changed."; exit 0; }
# Ask for the sudo password once 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 &
apply_changes
echo
detect_components
echo -e "${c_bold}Done. Current state:${c_reset}"
for c in "${COMPONENTS[@]}"; do
component_available "$c" || continue
if [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " off ${LABEL[$c]}"; fi
done
if [[ ${#FAILED[@]} -gt 0 ]]; then
warn "These had problems (see above): ${FAILED[*]}"
fi
echo
# Login manager changes only take effect after a restart.
if [[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " == *" gaming "* || " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " == *" single "* ]]; then
if ask_yn "Restart now so the changes take effect?" n; then
sudo reboot
else
info "Restart whenever you're ready."
fi
fi
Executable
+150
View File
@@ -0,0 +1,150 @@
#!/bin/bash
#
# steamify.sh - Steamify CachyOS
#
# Wizard that makes a CachyOS (KDE Plasma) install behave like SteamOS:
# boot into a Steam Deck-style gamescope session, switch to the Plasma
# desktop and back, and reset to gamescope on the next boot/logout.
#
# It opens with a menu that detects which components are on and turns
# them on or off to match what the user picks; turning one off restores
# what was there before (system files from .bak-gamescope-wizard backups,
# KDE settings from the undo journal in lib/state.sh). Components live in
# lib/, see README.md. Safe to re-run.
set -uo pipefail
# Release version, see CHANGELOG.md.
VERSION=0.9.0
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
for lib in common state packages login-manager single-user steam-desktop steam-machine vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut menu; do
# shellcheck source=/dev/null
source "$SCRIPT_DIR/lib/$lib.sh"
done
require_root_helper
echo -e "${c_bold}Steamify CachyOS${c_reset} v$VERSION"
echo "Turn the SteamOS-style parts on or off. The menu shows what is on now;"
echo "anything you turn off is put back the way it was."
if ! command -v pacman >/dev/null 2>&1; then
err "This doesn't look like an Arch/CachyOS system (no pacman found). Aborting."
exit 1
fi
# Everything per-user (autologin user, Steam, theme, shortcut) is for the
# user running the script.
TARGET_USER="$(id -un)"
restart_needed() { [[ -n "${BIOS_NEEDS_RESTART:-}" || "$RESTART_FOR_LOGIN" == true ]]; }
restart_now() {
if [[ -n "${BIOS_NEEDS_RESTART:-}" ]]; then
warn "The BIOS update is written during this restart. Keep the power on and don't"
warn "touch the machine until it has fully started again, even if the screen stays black."
fi
if [[ -n "${BIOS_DRY_RUN:-}" ]]; then
ok "Dry run: would restart now (sudo reboot); not restarting."
exit 0
fi
info "Restarting..."
sudo reboot
exit 0
}
after_run() {
# After a run: back to the menu, or restart right away when something
# that just ran needs it. Returns 1 when input has ended (scripted runs).
local reply
if ! restart_needed; then
read -rp "Press Enter to go back to the menu... " _ || return 1
return 0
fi
if [[ -n "${BIOS_NEEDS_RESTART:-}" ]]; then
warn "The BIOS update is installed at the next restart."
else
info "The login changes take effect after a restart."
fi
while true; do
read -rp "$(echo -e "${c_bold}[m]${c_reset} back to the menu ${c_bold}[r]${c_reset} restart now: ")" reply || return 1
case "$reply" in
m|M|"") return 0 ;;
r|R) restart_now ;;
*) warn "Type m or r." ;;
esac
done
}
# Menu loop: after each run the menu comes back with the new state, until
# the user quits; the restart question comes then, once, for everything.
RESTART_FOR_LOGIN=false
SUDO_KEEPALIVE=false
while true; do
detect_components
run_menu || break
plan_changes
if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then
ok "Everything is already the way you want it."
read -rp "Press Enter to go back to the menu... " _ || break
continue
fi
echo
echo -e "${c_bold}This will:${c_reset}"
for c in "${TO_DISABLE[@]}"; do echo " - turn off: ${LABEL[$c]}"; done
for c in "${TO_ENABLE[@]}"; do
if is_action "$c"; then echo " - run: ${LABEL[$c]%%:*} at your own risk (checks, then asks twice more)"
elif [[ "${CURRENT[$c]}" == 1 ]]; then echo " - re-apply: ${LABEL[$c]}"; else echo " - turn on: ${LABEL[$c]}"; fi
done
ask_yn "Go ahead?" y || { info "Nothing changed."; continue; }
# Ask for the sudo password once and keep it fresh, instead of prompting
# at random points during the run.
sudo -n true 2>/dev/null || sudo -v || exit 1
if [[ "$SUDO_KEEPALIVE" == false ]]; then
SUDO_KEEPALIVE=true
while true; do sudo -n true; sleep 50; kill -0 "$$" 2>/dev/null || exit; done 2>/dev/null &
fi
apply_changes
# Login manager changes only take effect after a restart.
[[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " == *" gaming "* || " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " == *" single "* ]] &&
RESTART_FOR_LOGIN=true
echo
detect_components
echo -e "${c_bold}Done. Current state:${c_reset}"
for c in "${COMPONENTS[@]}"; do
component_available "$c" && ! is_action "$c" || continue
if [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " off ${LABEL[$c]}"; fi
done
if [[ ${#FAILED[@]} -gt 0 ]]; then
warn "These had problems (see above): ${FAILED[*]}"
fi
echo
after_run || break
done
echo
# A staged BIOS update is written during the restart.
if [[ -n "${BIOS_NEEDS_RESTART:-}" ]]; then
warn "The BIOS update is written during the next restart. Keep the power on and"
warn "don't touch the machine until it has fully started again, even if the screen stays black."
if ask_yn "Restart now to install the BIOS update?" n; then
restart_now
else
info "The BIOS update installs at your next restart."
fi
elif [[ "$RESTART_FOR_LOGIN" == true ]]; then
if ask_yn "Restart now so the changes take effect?" n; then
restart_now
else
info "Restart whenever you're ready."
fi
else
info "Bye."
fi