Compare commits

..
15 Commits
Author SHA1 Message Date
theupriser 508f22acaf Merge pull request #11 from theupriser/feat/boot-into-desktop
Feat/boot into desktop
2026-09-24 13:35:24 +02:00
theupriser 3f8e0372d1 fix: Messages say which way it boots
"boot into: gamescope (from the next boot)" instead of "turn off: Boot into the desktop instead of gaming mode"; also in the progress line and the Done overview.
2026-09-24 13:30:56 +02:00
theupriser bd90e29e7a feat: Left/Right arrows choose on the "Boot into" row
Left = gamescope, right = desktop (Space still switches); the row says ←/→ choose and the key help lists Left/Right.
2026-09-24 13:28:14 +02:00
theupriser 4895de3a46 feat: "Boot into" is a sub-option of the conversion
Shown indented under the SteamOS conversion, and only while it's ticked.
2026-09-24 13:27:02 +02:00
theupriser 0f503dd112 feat: Boot into gamescope or the desktop
- Menu row "Boot into: [gamescope] / desktop" under the conversion; gamescope is the default.
- Desktop: steamify-boot-desktop.service sets the autologin session to Plasma at every boot, before the login manager; switching back and forth keeps working.
- VERSION 0.10.0.
2026-09-24 13:24:52 +02:00
theupriser d7cf0c0491 Merge pull request #10 from theupriser/bugfix/dkms-for-leds
Bugfix/dkms for leds
2026-09-24 12:57:33 +02:00
theupriser 5ab83d6e6f feat: Restart question on quit is "Restart now? [Y/n]"
When a restart is needed, q asks with yes as the default; n goes back to the menu and the next q asks again. Ctrl+C quits without restarting; scripted input that runs out never restarts.
2026-09-24 12:55:51 +02:00
theupriser 2e107e537e fix: LED driver for every kernel on a fresh install
The DKMS override was written to /etc/dkms before dkms was installed, when that folder doesn't exist yet: the write failed silently and other kernels (e.g. LTS) built against the running one and failed. Create the folder first and stop on a failed write. VERSION 0.9.1.
2026-09-24 12:55:38 +02:00
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
21 changed files with 652 additions and 104 deletions

No files matched your search

+7 -7
View File
@@ -1,16 +1,16 @@
#!/bin/bash
# Build a single-file version of the wizard for `curl | bash` use:
# .github/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
# 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.
set -euo pipefail
cd "$(dirname "$0")/../.."
out="${1:-dist/setup-gamescope-boot.sh}"
entry=setup-gamescope-boot.sh
out="${1:-dist/steamify.sh}"
entry=steamify.sh
mkdir -p "$(dirname "$out")"
# Library order, taken from the entry point's source loop.
@@ -23,15 +23,15 @@ version="v$(grep -oP '^VERSION=\K.*' "$entry"), $(git describe --always --dirty
# Header comment and `set` line of the entry point, up to the source loop.
sed -n '1,/^SCRIPT_DIR=/p' "$entry" | sed '$d'
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
for lib in $libs; do
echo "# ----- lib/$lib.sh -----"
# 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
done
echo "# ----- setup-gamescope-boot.sh -----"
echo "# ----- steamify.sh -----"
echo "main() {"
# 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/^ $//'
+21 -16
View File
@@ -1,10 +1,12 @@
# Builds the single-file wizard (.github/tools/bundle.sh) and publishes it as a
# release per version: tag v$VERSION (VERSION in setup-gamescope-boot.sh),
# release per version: tag v$VERSION (VERSION in steamify.sh),
# with that version's CHANGELOG.md section as notes. A released version is
# 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/cachyos-gamescope-boot/releases/latest/download/setup-gamescope-boot.sh | 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
# 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
@@ -26,29 +28,32 @@ jobs:
fetch-depth: 0 # for the version in the bundle's header
- name: Build
run: .github/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
run: |
bash -n dist/setup-gamescope-boot.sh
bash -n dist/steamify.sh
# 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
with:
name: setup-gamescope-boot
path: dist/setup-gamescope-boot.sh
name: steamify
path: dist/steamify.sh
- name: Version
id: version
env:
GH_TOKEN: ${{ github.token }}
run: |
version="$(grep -oP '^VERSION=\K.*' setup-gamescope-boot.sh)"
version="$(grep -oP '^VERSION=\K.*' steamify.sh)"
echo "version=$version" >> "$GITHUB_OUTPUT"
if gh release view "v$version" >/dev/null 2>&1; then
echo "released=true" >> "$GITHUB_OUTPUT"
echo "::warning::v$version is already released and won't be overwritten. Bump VERSION in setup-gamescope-boot.sh and add a CHANGELOG.md section to release this."
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."
else
echo "released=false" >> "$GITHUB_OUTPUT"
fi
@@ -68,12 +73,12 @@ jobs:
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/setup-gamescope-boot.sh | bash
curl -fsSL https://github.com/${{ github.repository }}/releases/latest/download/steamify.sh | bash
## Changes in v$VERSION
$changes"
gh release create "v$VERSION" dist/setup-gamescope-boot.sh \
--target "$GITHUB_SHA" --title "v$VERSION" --notes "$notes" --latest
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
@@ -81,8 +86,8 @@ jobs:
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 "Latest (v$VERSION)" --notes "$latest_notes" --latest=false
gh release edit latest --title "Steamify CachyOS - latest (v$VERSION)" --notes "$latest_notes" --latest=false
else
gh release create latest --title "Latest (v$VERSION)" --notes "$latest_notes" --latest=false
gh release create latest --title "Steamify CachyOS - latest (v$VERSION)" --notes "$latest_notes" --latest=false
fi
gh release upload latest dist/setup-gamescope-boot.sh --clobber
gh release upload latest dist/steamify.sh dist/setup-gamescope-boot.sh assets/steam-gaming-settings.svg --clobber
+31 -9
View File
@@ -5,14 +5,14 @@ for what the script does from a user's point of view.
## Project overview
A bash wizard that makes CachyOS (KDE Plasma 6 + `plasma-login-manager`)
**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
gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
(DMI `Valve`/`Fremont`) running CachyOS Desktop edition.
## Layout
- `setup-gamescope-boot.sh` - the only entry point. Pre-flight checks,
- `steamify.sh` - the only entry point. Pre-flight checks,
sources `lib/*.sh`, runs the menu and applies the plan. No component
logic here.
- `lib/*.sh` - one file per responsibility, each defining functions only
@@ -59,7 +59,7 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
- Comments explain *why* (the CachyOS/Plasma quirk being worked around),
not what the next line does.
- **Versioning and releases.** SemVer in `VERSION` (`setup-gamescope-boot.sh`,
- **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
@@ -70,7 +70,7 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
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/setup-gamescope-boot.sh`
- 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.
@@ -132,6 +132,16 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
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*`.
- `boot` ("Boot into: [gamescope] / desktop") is a choice row, not a
checkbox: on = desktop. It's a sub-option of the conversion: `menu_visible`
only shows it (indented, `└`) while `gaming` is ticked, so menu numbers
after it shift by one when the conversion is unticked. It needs `gaming` (ticking it ticks gaming,
unticking gaming unticks it) and is never preselected (`NO_PRESELECT`), so
the conversion boots into gamescope by default. Desktop is
`steamify-boot-desktop.service`, ordered `Before=` the login managers:
`steam-set-session plasma.desktop` plus the plasmalogin sync bridge when it
exists, at every boot. CachyOS's `cachyos-gamescope-autologin` still sets
gamescope during each desktop session; the unit corrects it at boot.
- `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.
@@ -147,8 +157,20 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
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`.
restart is needed), until `q`. When a restart is needed, `q` asks "Restart
now? [Y/n]" (`quit_prompt`): `n` goes back to the menu, the next `q` asks
again. Scripted input that runs out quits without restarting (never
restart on EOF: `ask_yn` would take its default).
- 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
tight loop; keep that in mind when changing session handling.
@@ -162,8 +184,8 @@ array (e.g. `g` in `lib/state.sh`), or CI's shellcheck on the bundle fails.
There is no test suite. At minimum:
```bash
for f in setup-gamescope-boot.sh lib/*.sh; do bash -n "$f"; done
shellcheck -S warning setup-gamescope-boot.sh lib/*.sh # if available
for f in steamify.sh lib/*.sh; do bash -n "$f"; done
shellcheck -S warning steamify.sh lib/*.sh # if available
```
Behaviour is verified in a CachyOS QEMU/KVM test VM. The VM scripts and a
@@ -172,5 +194,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
numbered prompt, which is what scripted runs use, reboot checks, the full test matrix, `--fremont` to
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.
+69 -3
View File
@@ -2,10 +2,76 @@
All notable changes, per version and per commit. Versions follow
[Semantic Versioning](https://semver.org/); the current one is `VERSION` in
`setup-gamescope-boot.sh`. Versions before 0.7.0 were numbered afterwards,
`steamify.sh`. Versions before 0.7.0 were numbered afterwards,
one per merged pull request.
## 0.8.0 - 2026-09-24
## 0.10.0 - 2026-09-24
- `0f503dd` **feat: Boot into gamescope or the desktop**
- New menu row under the conversion, "Boot into: [gamescope] / desktop";
gamescope stays the default and is never changed on a first run.
- Desktop: `steamify-boot-desktop.service` sets the autologin session to
Plasma at every boot, before the login manager starts; Return to Gaming
Mode and Steam's Switch to Desktop keep working. Back to gamescope removes
it. Choosing desktop turns the conversion on; turning the conversion off
resets it to gamescope.
- The later menu items move down one (BIOS is now 8 on a Steam Machine).
- `4895de3` **feat: "Boot into" is a sub-option of the conversion**
- Shown indented under the SteamOS conversion, and only while it's ticked.
- `bd90e29` **feat: Left/Right arrows choose on the "Boot into" row**
- Left = gamescope, right = desktop (Space still switches); the row says
"←/→ choose" and the key help lists Left/Right.
- **fix: Messages say which way it boots**
- "This will: boot into: gamescope (from the next boot)" instead of "turn
off: Boot into the desktop instead of gaming mode", and the same in the
progress line and the "Done" overview.
## 0.9.1 - 2026-09-24 (#10)
- `2e107e5` **fix: LED driver for every kernel on a fresh install**
- The DKMS override was written to `/etc/dkms` before `dkms` was installed,
when that folder doesn't exist yet, so it silently failed and the build
for other kernels (e.g. LTS) failed as in 0.6. The folder is created first
now, and a failed write stops with an error.
- `5ab83d6` **feat: Restart question on quit is "Restart now? [Y/n]"**
- When something needs a restart, `q` asks with yes as the default; `n` goes
back to the menu and the next `q` asks again. Ctrl+C quits without
restarting; scripted input that runs out never restarts.
## 0.9.0 - 2026-09-24 (#9)
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**
- `afc05b0` **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.
@@ -40,7 +106,7 @@ every run.
- "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**
- **docs: Last commit hash in the changelog; 0.7.0 was released on its own (#6, #7)**
- `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)
+34 -21
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
into Steam's Big Picture (gamescope), and you can switch to the KDE Plasma
desktop and back whenever you like - just like on a Steam Deck or Valve's
Steam Machine.
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
The wizard is a menu: pick what you want, and it turns each part on or off.
@@ -18,16 +14,23 @@ Everything you turn off is put back the way it was.
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,
and Steam's on-screen keyboard (Steam + X) also works on the desktop.
2. **SteamOS theme** - the Vapor look for the desktop (CachyOS's
2. **Boot into: [gamescope] / desktop** - a sub-option of 1, shown under it
while it's ticked: where the PC starts. Gamescope (gaming mode) is the
default; choose desktop to start in KDE Plasma instead, with Return to
Gaming Mode one double-click away.
3. **SteamOS theme** - the Vapor look for the desktop (CachyOS's
`cachyos-vapor` package).
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. **Steam Deck/Machine icons** - Steam Deck button icons in gaming mode.
5. **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
fun. (Needs 1.)
5. **Steam Machine support** - only shown on a Valve Steam Machine: the
6. **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.
7. **Steam Machine support** - only shown on a Valve Steam Machine: the
driver for the front LED bar, and the hardware settings in Steam (fan,
TV control over HDMI-CEC).
6. **Update BIOS** - only on a Steam Machine, opt-in and at your own risk:
8. **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)).
@@ -41,17 +44,19 @@ Everything you turn off is put back the way it was.
Open **Konsole** on your Plasma desktop and run:
```bash
curl -fsSL https://github.com/theupriser/cachyos-gamescope-boot/releases/latest/download/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
at the script first? Clone the repository instead:
```bash
git clone https://github.com/theupriser/cachyos-gamescope-boot.git
cd cachyos-gamescope-boot
./setup-gamescope-boot.sh
git clone https://github.com/theupriser/steamify-cachyos.git
cd steamify-cachyos
./steamify.sh
```
Run it as yourself, not as root. You'll see a checklist:
@@ -69,7 +74,8 @@ Run it as yourself, not as root. You'll see a checklist:
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** (on the **Boot
into** row, **Left/Right** choose gamescope or desktop), and press
**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
@@ -79,7 +85,9 @@ 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.
between going back to the menu and restarting now. Quitting then asks
"Restart now? [Y/n]": **n** takes you back to the menu (and the next **q**
asks again); Ctrl+C quits without restarting.
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
@@ -94,9 +102,12 @@ files in `lib/` next to it.
**Power > Switch to Desktop**.
- **Back to gaming mode:** double-click **Return to Gaming Mode** on the
desktop (or log out, if you're not using single user mode).
- After a restart you always start in gaming mode.
- After a restart you start in gaming mode, or on the desktop if you chose
**Boot into: desktop** in the menu. Switching back and forth works the same
either way.
Prefer to decide yourself where your PC starts? Run one of these in Konsole:
Prefer to start where you left off last time? Run one of these in Konsole
(with **Boot into: gamescope**):
```bash
steamos-session-select persistent # start where you left off last time
@@ -348,20 +359,22 @@ gamescope-session, ...) stay installed.
| 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/state.sh` | Undo journal for KDE settings (`kset`/`krevert`) |
| `lib/common.sh` | Output helpers, prompts, backups, plasmashell handling |
| `lib/packages.sh` | Required packages, AUR helper (yay/paru) |
| `lib/boot-session.sh` | Boot into gamescope or the desktop (`steamify-boot-desktop.service`) |
| `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/desktop-shortcut.sh` | Return to Gaming Mode shortcut and its sudoers rule |
| `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/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 |
| `.github/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` |
The single-file version is generated: on every push to `main`, GitHub
+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

+1 -1
View File
@@ -3,7 +3,7 @@
# 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 setup-gamescope-boot.sh; not meant to be run on its own.
# 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,
+62
View File
@@ -0,0 +1,62 @@
#!/bin/bash
# "Boot into: gamescope / desktop" menu item, on top of the SteamOS
# conversion. Gamescope is the default (the conversion's autologin, and
# CachyOS's cachyos-gamescope-autologin resetting to it after a desktop
# session). Desktop: a unit that sets the autologin session to Plasma at
# every boot, before the login manager starts. Switching between the two in
# a running system (Return to Gaming Mode, Steam's Switch to Desktop) works
# the same either way.
# Sourced by steamify.sh; not meant to be run on its own.
BOOT_UNIT_NAME="steamify-boot-desktop.service"
BOOT_UNIT="/etc/systemd/system/$BOOT_UNIT_NAME"
SESSION_SYNC="/usr/local/bin/sync-steamos-session.sh"
boot_status() {
# On = boots into the desktop.
[[ -f "$BOOT_UNIT" ]] && systemctl is-enabled -q "$BOOT_UNIT_NAME" 2>/dev/null
}
boot_enable() {
info "Setting the PC to boot into the desktop..."
# The sync bridge (plasma-login-manager only; not there with SDDM) copies
# the session into the base config; run it right away rather than rely
# on its path unit, which could still be pending when the login manager
# starts.
sudo tee "$BOOT_UNIT" > /dev/null << EOF
[Unit]
Description=Steamify CachyOS: boot into the desktop instead of gaming mode
Before=display-manager.service sddm.service plasmalogin.service
[Service]
Type=oneshot
ExecStart=/usr/lib/steamos/steam-set-session plasma.desktop
ExecStart=/bin/sh -c '[ -x $SESSION_SYNC ] && exec $SESSION_SYNC || true'
[Install]
WantedBy=graphical.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable "$BOOT_UNIT_NAME" || { err "Enabling $BOOT_UNIT_NAME failed."; return 1; }
ok "Boots into the desktop from the next boot on (Return to Gaming Mode still works)."
}
boot_disable() {
info "Setting the PC to boot into gaming mode again..."
sudo systemctl disable "$BOOT_UNIT_NAME" 2>/dev/null
sudo rm -f "$BOOT_UNIT"
sudo systemctl daemon-reload
# Next boot into gamescope, as the conversion sets it up.
sudo /usr/lib/steamos/steam-set-session gamescope-session.desktop
[[ -x "$SESSION_SYNC" ]] && sudo "$SESSION_SYNC"
ok "Boots into gaming mode from the next boot on."
}
boot_choice() {
# The menu row: the chosen option in brackets.
if [[ "$1" == 1 ]]; then
echo "Boot into: gamescope [desktop]"
else
echo "Boot into: [gamescope] desktop"
fi
}
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# 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_dim="\033[2m"
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# "Return to Gaming Mode" desktop shortcut and the sudoers rule it needs.
# Sourced by setup-gamescope-boot.sh; not meant to be run on its own.
# Sourced by steamify.sh; not meant to be run on its own.
create_desktop_shortcut() {
# Determine the home directory of the target user
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
# Booting into gaming mode: autologin into gamescope with SDDM or
# 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"
+55 -11
View File
@@ -1,20 +1,25 @@
#!/bin/bash
# Interactive menu: detects which components are on, lets the user pick what
# 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;
# gaming must come first (single user builds on it).
COMPONENTS=(gaming theme glyphs single machine bios)
COMPONENTS=(gaming boot 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)
# Never preselected on a first run: booting into the desktop is a choice,
# gamescope is the default.
NO_PRESELECT=(boot)
declare -A LABEL=(
[gaming]="SteamOS conversion: boot into gaming mode, Steam on the desktop"
[boot]="Boot into the desktop instead of gaming mode"
[theme]="Install SteamOS theme: Vapor look (cachyos-vapor)"
[glyphs]="Install Steam Deck/Machine icons: Deck button icons in gaming mode"
[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"
[bios]="Update BIOS"
)
@@ -29,6 +34,19 @@ component_available() {
is_action() { [[ " ${ACTIONS[*]} " == *" $1 "* ]]; }
boot_mode() {
# boot_mode 0|1: the "Boot into" choice as a word, for messages; "turn
# off boot into the desktop" reads as the opposite of what was chosen.
if [[ "$1" == 1 ]]; then echo desktop; else echo gamescope; fi
}
menu_visible() {
# Shown in the menu. "Boot into" is a sub-option of the conversion: only
# while the conversion is ticked.
component_available "$1" || return 1
[[ "$1" != boot || "${WANTED[gaming]:-0}" == 1 ]]
}
component_selectable() {
# Greyed out and not tickable when it has nothing to do.
case "$1" in
@@ -48,7 +66,8 @@ detect_components() {
# First run: preselect the full SteamOS experience (never an action).
if [[ "$any" == false ]]; then
for c in "${COMPONENTS[@]}"; do
component_available "$c" && ! is_action "$c" && WANTED[$c]=1
component_available "$c" && ! is_action "$c" &&
[[ " ${NO_PRESELECT[*]} " != *" $c "* ]] && WANTED[$c]=1
done
fi
}
@@ -59,7 +78,9 @@ toggle_component() {
WANTED[$c]=$(( 1 - WANTED[$c] ))
# 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" == gaming && "${WANTED[gaming]}" == 0 ]]; then WANTED[single]=0; fi
if [[ "$c" == gaming && "${WANTED[gaming]}" == 0 ]]; then WANTED[single]=0; WANTED[boot]=0; fi
# Where to boot to is part of the conversion, too.
if [[ "$c" == boot && "${WANTED[boot]}" == 1 ]]; then WANTED[gaming]=1; fi
}
show_menu() {
@@ -68,7 +89,7 @@ show_menu() {
printf " ${c_bold}%-3s %-6s %-6s %s${c_reset}\n" "#" "Now" "Want" "Component"
MENU_ITEMS=()
for c in "${COMPONENTS[@]}"; do
component_available "$c" || continue
menu_visible "$c" || continue
i=$((i + 1)); MENU_ITEMS[$i]=$c
# Pad the plain word, then colour it: colour codes would count as width.
now="off"; [[ "${CURRENT[$c]}" == 1 ]] && now="on"
@@ -76,7 +97,13 @@ show_menu() {
now="$(printf '%-6s' "$now")"
[[ "${CURRENT[$c]}" == 1 ]] && now="${now/on/${c_green}on${c_reset}}"
want="[ ]"; [[ "${WANTED[$c]}" == 1 ]] && want="[x]"
if component_selectable "$c"; then
if [[ "$c" == boot ]]; then
# A choice rather than a checkbox: Now/Want show the mode.
now="gaming"; [[ "${CURRENT[boot]}" == 1 ]] && now="desk"
[[ "${CURRENT[gaming]}" == 1 ]] || now="-"
want="gaming"; [[ "${WANTED[boot]}" == 1 ]] && want="desk"
printf " %-3s %-6s %-6s └ %s\n" "$i" "$now" "$want" "$(boot_choice "${WANTED[boot]}")"
elif 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"
@@ -103,17 +130,26 @@ run_menu() {
draw_menu_tui() {
local cursor="$1" i=0 c box state line
printf '\033[H\033[2J'
echo -e "${c_bold}CachyOS Steam Deck-style Gamescope Boot Wizard${c_reset} v$VERSION"
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
MENU_ITEMS=()
for c in "${COMPONENTS[@]}"; do
component_available "$c" || continue
menu_visible "$c" || continue
MENU_ITEMS[$i]=$c
box="[ ]"; [[ "${WANTED[$c]}" == 1 ]] && box="[${c_green}x${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]}"
if [[ "$c" == boot ]]; then
# A choice rather than a checkbox; Space switches it.
# Indented under the conversion, whose sub-option it is.
line="$(boot_choice "${WANTED[boot]}")"
line=" └ ${line/\[/[${c_green}}"; line="${line/\]/${c_reset}]}"
local mode=gamescope; [[ "${CURRENT[boot]}" == 1 ]] && mode=desktop
state=" (${c_bold}←/→${c_reset} choose)"
[[ "${CURRENT[gaming]}" == 1 ]] && state=" (now: $mode; ${c_bold}←/→${c_reset} choose)"
fi
if ! component_selectable "$c"; then
# Greyed out: nothing to do (e.g. BIOS already up to date).
local mark=" "; (( i == cursor )) && mark=" ${c_cyan}>${c_reset} "
@@ -129,7 +165,8 @@ draw_menu_tui() {
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}Left/Right${c_reset} choose ${c_bold}Enter${c_reset} run"
echo -e " ${c_bold}a${c_reset} run + re-apply what's on ${c_bold}q${c_reset} quit"
}
run_menu_tui() {
@@ -139,6 +176,8 @@ run_menu_tui() {
while true; do
draw_menu_tui "$cursor"
count=${#MENU_ITEMS[@]}
# The list shrinks when the conversion (and its sub-option) is unticked.
if (( cursor >= count )); then cursor=$(( count - 1 )); draw_menu_tui "$cursor"; fi
IFS= read -rsn1 key
if [[ "$key" == $'\e' ]]; then
IFS= read -rsn2 -t 0.05 rest
@@ -148,6 +187,9 @@ run_menu_tui() {
$'\e[A'|k) (( cursor = (cursor + count - 1) % count )) ;;
$'\e[B'|j) (( cursor = (cursor + 1) % count )) ;;
" ") toggle_component "${MENU_ITEMS[$cursor]}" ;;
# On the "Boot into" row: left = gamescope, right = desktop.
$'\e[D'|h) [[ "${MENU_ITEMS[$cursor]}" == boot && "${WANTED[boot]}" == 1 ]] && toggle_component boot ;;
$'\e[C'|l) [[ "${MENU_ITEMS[$cursor]}" == boot && "${WANTED[boot]}" == 0 ]] && toggle_component boot ;;
"") break ;;
a|A) REAPPLY=true; break ;;
q|Q) tput cnorm 2>/dev/null; echo; return 1 ;;
@@ -207,11 +249,13 @@ apply_changes() {
[[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm
for c in "${TO_DISABLE[@]}"; do
echo; echo -e "${c_bold}Turning off: ${LABEL[$c]}${c_reset}"
if [[ "$c" == boot ]]; then echo; echo -e "${c_bold}Boot into: gamescope${c_reset}"
else echo; echo -e "${c_bold}Turning off: ${LABEL[$c]}${c_reset}"; fi
"${c}_disable" || failed+=("$c")
done
for c in "${TO_ENABLE[@]}"; do
if is_action "$c"; then echo; echo -e "${c_bold}Running: ${LABEL[$c]%%:*}${c_reset}"
if [[ "$c" == boot ]]; then echo; echo -e "${c_bold}Boot into: desktop${c_reset}"
elif 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")
done
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# 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() {
# Fully non-interactive install flags for the given AUR helper: no
+1 -1
View File
@@ -3,7 +3,7 @@
# log out, since typing a password with a controller is miserable. Goes
# 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.
# 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() {
[[ "$(kreadconfig6 --file kdeglobals --group "KDE Action Restrictions" --key action/lock_screen)" == false ]]
+1 -1
View File
@@ -3,7 +3,7 @@
# 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
# 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"
UNSET=$'\x01'
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
# Steam in the Plasma desktop session: gamepad UI args, virtual keyboard,
# 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() {
info "Applying non-optional SteamOS environment & keyboard fixes..."
+8 -3
View File
@@ -1,7 +1,7 @@
#!/bin/bash
# "Steam Machine support" menu item, only on Valve Fremont hardware: the
# 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() {
# Valve Steam Machine (Fremont) boards. The leds-valve driver's own DMI
@@ -56,8 +56,13 @@ install_valve_led_driver() {
# 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
# On a fresh system dkms isn't installed yet, so /etc/dkms doesn't exist.
sudo mkdir -p "$(dirname "$LED_DKMS_OVERRIDE")"
if ! 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; then
err "Couldn't write $LED_DKMS_OVERRIDE; without it the LED driver only builds for the running kernel."
return 1
fi
if ! pacman -Qi leds-valve-dkms-git >/dev/null 2>&1; then
info "Installing leds-valve-dkms-git from the AUR via $aur_helper (non-interactive)..."
+1 -1
View File
@@ -6,7 +6,7 @@
# 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 setup-gamescope-boot.sh; not meant to be run on its own.
# 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
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# 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.
VAPOR_LOOKANDFEEL=com.valve.vapor.desktop
VAPOR_DEFAULTS=/usr/share/plasma/look-and-feel/$VAPOR_LOOKANDFEEL/contents/defaults
+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."
}
+41 -24
View File
@@ -1,6 +1,6 @@
#!/bin/bash
#
# setup-gamescope-boot.sh
# 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
@@ -15,18 +15,18 @@
set -uo pipefail
# Release version, see CHANGELOG.md.
VERSION=0.8.0
VERSION=0.10.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 menu; do
for lib in common state packages login-manager single-user steam-desktop steam-machine boot-session 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}CachyOS Steam Deck-style Gamescope Boot Wizard${c_reset} v$VERSION"
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."
@@ -78,13 +78,36 @@ after_run() {
done
}
quit_prompt() {
# On q: when something needs a restart, offer it (default yes); "n" goes
# back to the menu, and the next q asks again. Returns 0 to show the menu
# again, 1 to quit (nothing to restart, or input ended in scripted runs).
local reply
restart_needed || return 1
echo
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."
else
info "The changes take effect after a restart."
fi
read -rp "$(echo -e "${c_bold}Restart now?${c_reset} [Y/n] (n = back to the menu, Ctrl+C = quit without restarting) ")" reply || return 1
case "$reply" in
""|y|Y) restart_now ;;
esac
return 0
}
# 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
if ! run_menu; then
quit_prompt && continue
break
fi
plan_changes
if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then
@@ -95,9 +118,13 @@ while true; do
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_DISABLE[@]}"; do
if [[ "$c" == boot ]]; then echo " - boot into: gamescope (from the next boot)"
else echo " - turn off: ${LABEL[$c]}"; fi
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)"
if [[ "$c" == boot ]]; then echo " - boot into: desktop (from the next boot)"
elif 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; }
@@ -112,7 +139,7 @@ while true; do
apply_changes
# Login manager changes only take effect after a restart.
[[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " == *" gaming "* || " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " == *" single "* ]] &&
[[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " =~ \ (gaming|single|boot)\ ]] &&
RESTART_FOR_LOGIN=true
echo
@@ -120,7 +147,9 @@ while true; do
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
if [[ "$c" == boot ]]; then
[[ "${CURRENT[gaming]}" == 1 ]] && echo " └ boots into: $(boot_mode "${CURRENT[boot]}")"
elif [[ "${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[*]}"
@@ -130,21 +159,9 @@ while true; do
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
if restart_needed; then
# Only reached when input ended (scripted runs): never restart then.
info "Not restarted; the changes take effect after a restart."
else
info "Bye."
fi