Compare commits

..
18 Commits
Author SHA1 Message Date
theupriser 41422e5bb4 chore: Version 1.0.0 2026-09-25 08:44:49 +02:00
theupriser 6c2e203563 fix: No second sudo password prompt while installing yay
makepkg -si installs with sudo -k, which forgets the cached password.
Build with makepkg, install with sudo pacman -U.
2026-09-25 08:35:54 +02:00
theupriser 1522e5632d feat: No more KDE wallet password prompts in single user mode
Valve's empty, password-less wallet, like SteamOS. The user's wallet is
moved aside and restored when single user mode is off; the single user
wallet is kept and reused next time.
2026-09-25 08:27:50 +02:00
theupriser 1d994ae359 docs: Add LICENSE.md (MIT) 2026-09-25 08:22:11 +02:00
theupriser 700f3e514b docs: README links to the files it mentions; drop setup-gamescope-boot.sh
The release no longer publishes the bundle under its pre-0.9.0 name.
2026-09-25 08:18:40 +02:00
theupriser d78652c862 docs: Shorter README, with a Steam Machine section
How it works moved to TECHNICAL.md, problems to TROUBLESHOOTING.md.
2026-09-25 08:17:07 +02:00
theupriser 727e6a2302 feat: Kernel files from our own release, checked before install
The kernel-7.1.6-1 release is the first download source. Every file must
match the SHA-256 in the script and have a valid CachyOS signature.
2026-09-25 08:08:28 +02:00
theupriser c4bd1ca39c feat: Pin the kernel to 7.1.6-1 on a Steam Machine
Sub-option of Steam Machine support, ticked along with it: newer CachyOS
kernels make the Steam Machine reboot instead of shutting down. Packages
are kept in /var/cache/steamify/kernel and reinstalled from there; else
pacman's cache, the CachyOS archive or mirror, or PINNED_KERNEL_URL.
2026-09-25 07:58:02 +02:00
theupriser a449a9e467 Enhance kernel installation and header management
Updated kernel installation process to include header symlink setup and improved logging messages.
2026-09-24 22:01:54 +02:00
theupriser 388b1286c9 Implement kernel installation and locking function
Adds a function to install and pin a specific CachyOS kernel with verification and locking.
2026-09-24 21:39:15 +02:00
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
14 changed files with 929 additions and 398 deletions

No files matched your search

+2 -6
View File
@@ -5,8 +5,6 @@
# release are moved to it, so both of these always get the newest version: # 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/latest/download/steamify.sh | bash
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/download/latest/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. # Pull requests only build and check it, and warn when VERSION is already released.
name: Single-file build name: Single-file build
@@ -30,8 +28,6 @@ jobs:
- name: Build - name: Build
run: | run: |
.github/tools/bundle.sh dist/steamify.sh .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: |
@@ -77,7 +73,7 @@ jobs:
## Changes in v$VERSION ## Changes in v$VERSION
$changes" $changes"
gh release create "v$VERSION" dist/steamify.sh dist/setup-gamescope-boot.sh assets/steam-gaming-settings.svg \ gh release create "v$VERSION" dist/steamify.sh assets/steam-gaming-settings.svg \
--target "$GITHUB_SHA" --title "Steamify CachyOS v$VERSION" --notes "$notes" --latest --target "$GITHUB_SHA" --title "Steamify CachyOS v$VERSION" --notes "$notes" --latest
# "latest" follows the newest version tag, for the older install URL # "latest" follows the newest version tag, for the older install URL
@@ -90,4 +86,4 @@ jobs:
else else
gh release create latest --title "Steamify CachyOS - latest (v$VERSION)" --notes "$latest_notes" --latest=false gh release create latest --title "Steamify CachyOS - latest (v$VERSION)" --notes "$latest_notes" --latest=false
fi fi
gh release upload latest dist/steamify.sh dist/setup-gamescope-boot.sh assets/steam-gaming-settings.svg --clobber gh release upload latest dist/steamify.sh assets/steam-gaming-settings.svg --clobber
+15 -3
View File
@@ -17,7 +17,7 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
logic here. logic here.
- `lib/*.sh` - one file per responsibility, each defining functions only - `lib/*.sh` - one file per responsibility, each defining functions only
(no top-level side effects besides constants). See the table in (no top-level side effects besides constants). See the table in
`README.md`. `TECHNICAL.md`.
- **Components.** Every menu item `<id>` (listed in `COMPONENTS` and `LABEL` - **Components.** Every menu item `<id>` (listed in `COMPONENTS` and `LABEL`
in `lib/menu.sh`) provides `<id>_status` (return 0 if on, detected from in `lib/menu.sh`) provides `<id>_status` (return 0 if on, detected from
the system, no state file needed), `<id>_enable` and `<id>_disable`, all the system, no state file needed), `<id>_enable` and `<id>_disable`, all
@@ -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 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 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*`.
- `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 - `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 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. by `a`, not listed in the state overview, and `bios_status` is always off.
@@ -147,8 +157,10 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
device check, never flashes) and a faked version (dev-env device check, never flashes) and a faked version (dev-env
`BIOS_VERSION=F7F0107 ./run.sh --fremont`). `BIOS_VERSION=F7F0107 ./run.sh --fremont`).
- The entry point loops: menu, run, "back to the menu" (or `[m]`/`[r]` when a - 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 restart is needed), until `q`. When a restart is needed, `q` asks "Restart
end. Scripted input that runs out ends the loop like `q`. 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 - Steamify shortcut (`launcher`): the icon runs `curl | bash` of
`releases/latest/download/steamify.sh` in Konsole, so it's `releases/latest/download/steamify.sh` in Konsole, so it's
always the newest release. Its icon, `assets/steam-gaming-settings.svg` always the newest release. Its icon, `assets/steam-gaming-settings.svg`
+69 -2
View File
@@ -5,7 +5,74 @@ All notable changes, per version and per commit. Versions follow
`steamify.sh`. Versions before 0.7.0 were numbered afterwards, `steamify.sh`. Versions before 0.7.0 were numbered afterwards,
one per merged pull request. one per merged pull request.
## 0.9.0 - 2026-09-24 ## 1.0.0 - 2026-09-25
- **feat: Pin the kernel to 7.1.6-1 on a Steam Machine**
- New sub-option under Steam Machine support, ticked along with it (opt
out): newer CachyOS kernels make the Steam Machine reboot instead of
shutting down. Installs `linux-cachyos` and `-headers` 7.1.6-1 and adds
them to `IgnorePkg`; unticking removes the pin and updates the system.
- The packages are kept in `/var/cache/steamify/kernel` and reinstalled
from there without downloading; else from pacman's cache, the CachyOS
archive, the CachyOS mirror, or `PINNED_KERNEL_URL`.
- First download source: the `kernel-7.1.6-1` release of this repo, a
one-off release that always has these files.
- Every file is checked against its SHA-256 (in the script) and its
CachyOS signature before it's installed; a bad file is deleted.
- Installed kernel headers are no longer updated by the wizard, so the
pinned kernel keeps matching headers.
- The BIOS item moves down one (9 on a Steam Machine).
- **docs: Shorter README, with a Steam Machine section**
- How it works moved to `TECHNICAL.md`, problems to `TROUBLESHOOTING.md`;
the README links to them and to the files it mentions.
- **feat: No more KDE wallet password prompts in single user mode**
- Uses Valve's empty, password-less wallet, like SteamOS; apps such as
Brave no longer ask for the wallet password after autologin.
- Your own wallet is moved to `kdewallet.kwl.bak-steamify` and comes back,
unchanged, when single user mode is off; the single user wallet is kept
and reused next time.
- Moved from the SteamOS theme, where it only helped without any wallet.
- **fix: No second sudo password prompt while installing yay**
- `makepkg -si` installs with `sudo -k`, which forgets the cached password;
yay is now built with makepkg and installed with `sudo pacman -U`.
- **docs: LICENSE.md** with the MIT license the README already named.
- **chore: No more `setup-gamescope-boot.sh` release asset**
- Only `steamify.sh` is published; the old name from before 0.9.0 is gone.
## 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 The wizard is now called **Steamify CachyOS**, and can put a shortcut to
itself on the desktop. itself on the desktop.
@@ -32,7 +99,7 @@ itself on the desktop.
bundle under both names, so `.../download/setup-gamescope-boot.sh` keeps bundle under both names, so `.../download/setup-gamescope-boot.sh` keeps
working. The Steamify shortcut runs `steamify.sh`. working. The Steamify shortcut runs `steamify.sh`.
- `b0553ae` **docs: Shorter README intro** - `b0553ae` **docs: Shorter README intro**
- **feat: The shortcut's window closes itself after a countdown** - `afc05b0` **feat: The shortcut's window closes itself after a countdown**
- After the wizard ends, a 10-second countdown closes the Konsole window - 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. (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 - A failed download now counts as an error (`pipefail`) instead of running
+24
View File
@@ -0,0 +1,24 @@
# MIT License
Copyright (c) 2026 Rick Peters
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The kernel packages in the `kernel-7.1.6-1` release are unmodified CachyOS
packages under their own license (GPL-2.0); see that release for the source.
+60 -338
View File
@@ -5,30 +5,45 @@ 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.
- **One command**, then a simple checklist: pick what you want.
- **Everything is undoable**: untick something and it's put back the way it was.
- **Made for the Steam Machine**, and works on any CachyOS KDE desktop PC.
## What you get ## What you get
The wizard is a menu: pick what you want, and it turns each part on or off. 1. **SteamOS conversion** - boots straight into gaming mode. **Switch to
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). Steam's on-screen keyboard (Steam + X) works
1. **SteamOS conversion** - boots straight into gaming mode, **Switch to on the desktop too.
Desktop** in Steam works and so does going back (the **Return to Gaming - **Boot into: gamescope / desktop** - where the PC starts. Gaming mode is
Mode** icon on the desktop), you're back in gaming mode after a restart, the default.
and Steam's on-screen keyboard (Steam + X) also works on the desktop. 2. **SteamOS theme** - the Vapor look of SteamOS for the desktop, with
2. **SteamOS theme** - the Vapor look for the desktop (CachyOS's **Add to Steam** in right-click menus
`cachyos-vapor` package). ([details](TECHNICAL.md#steamos-desktop-look)).
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, and no
user switching or logging out. Typing a password with a controller is no KDE wallet password prompts (e.g. from Brave). Typing a password with a
fun. (Needs 1.) controller is no fun
5. **Steamify shortcut** - a "Steamify CachyOS" icon on the desktop and in ([details](TECHNICAL.md#single-user-mode-sddm-no-locking)).
the app launcher (the Steam logo with a gear) that opens the newest version 5. **Steamify shortcut** - an icon on the desktop and in the launcher that
of Steamify in Konsole, so you don't need the install command again. opens the newest Steamify, 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, ## Steam Machine
TV control over HDMI-CEC).
7. **Update BIOS** - only on a Steam Machine, opt-in and at your own risk: On a Valve Steam Machine the menu has two more items:
installs the newest Steam Machine BIOS from Valve (see
[BIOS updates](#bios-updates-steam-machine)). - **Steam Machine support** - the front **LED bar** works, Steam's
**hardware settings** (fan, TV control over HDMI-CEC) work, and the power
button puts it to sleep like a console.
- **Pin the kernel to 7.1.6-1** - on by default: with newer CachyOS kernels
the Steam Machine reboots instead of shutting down. Untick it once CachyOS
fixes that. The kernel comes from its own
[release](https://github.com/theupriser/steamify-cachyos/releases/tag/kernel-7.1.6-1),
checked against CachyOS's signature
([details](TECHNICAL.md#kernel-pin-steam-machine)).
- **Update BIOS** - installs Valve's newest Steam Machine BIOS. Never ticked
by default, at your own risk, and only after two warnings
([details](TECHNICAL.md#bios-updates-steam-machine)).
## Requirements ## Requirements
@@ -43,353 +58,60 @@ Open **Konsole** on your Plasma desktop and run:
curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify.sh | bash curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify.sh | bash
``` ```
(Before 0.9.0 the script was called `setup-gamescope-boot.sh`; that name Prefer to look at the script first? Clone the repository and run
still works in the download URL.) That downloads and runs the latest [`./steamify.sh`](steamify.sh) (keep the whole folder: it needs
single-file version, and works in any [`lib/`](lib/)).
shell, including fish (CachyOS's default). Prefer to keep a copy, or to look
at the script first? Clone the repository instead:
```bash You'll see a checklist:
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:
``` ```
[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)
└ Boot into: [gamescope] desktop
> [x] Install SteamOS theme: Vapor look (cachyos-vapor) (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)
[x] Steamify shortcut: desktop icon to run this again (now: off)
Kernels (> = running; controller = Steam controller driver, LEDs = LED bar driver built) Up/Down move Space select Left/Right choose Enter run
6.18.52-1-cachyos-lts linux-cachyos-lts headers yes controller yes a run + re-apply what's on q quit
> 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
``` ```
Move with the **arrow keys**, tick or untick with **Space**, and press Tick with **Space**, run with **Enter**. It shows what it will change, asks
**Enter** to run. "now:" shows what's on at the moment. Below the list, every your password once, and offers to restart at the end. Below the list you see
installed kernel is shown with its headers and Steam controller driver; on a every installed kernel, so after an update it's easy to check that
Steam Machine also whether the LED bar driver is built for it, and whether everything is in place.
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
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 Run it again whenever you like: to change your choices, or after a CachyOS
in one go; quit with **q**. When something needs a restart, you choose update (press `a` to re-apply what's on).
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
again, or after a CachyOS update (press `a` in the menu to re-apply
everything that's on).
If you cloned the repository, keep the whole folder: the script needs the
files in `lib/` next to it.
## Using it ## Using it
- **To the desktop:** in gaming mode, open the Steam menu and choose - **To the desktop:** in gaming mode, open the Steam menu and choose
**Power > Switch to Desktop**. **Power > Switch to Desktop**.
- **Back to gaming mode:** double-click **Return to Gaming Mode** on the - **Back to gaming mode:** double-click **Return to Gaming Mode** on the
desktop (or log out, if you're not using single user mode). desktop.
- After a restart you always start in gaming mode. - **Remove it all:** run the wizard and untick everything. You get the
normal CachyOS login screen back; Steam stays installed.
Prefer to decide yourself where your PC starts? Run one of these in Konsole: Prefer to start where you left off last time?
```bash ```bash
steamos-session-select persistent # start where you left off last time steamos-session-select persistent # start where you left off last time
steamos-session-select oneshot # always start in gaming mode (default) steamos-session-select oneshot # always start in gaming mode (default)
``` ```
## If something goes wrong ## Something wrong?
**Gaming mode shows a black screen or keeps restarting.** Press Stuck on a black screen in gaming mode: press **Ctrl+Alt+F3**, log in, and run
**Ctrl+Alt+F3**, log in with your username and password, and run:
```bash ```bash
sudo /usr/lib/steamos/steam-set-session plasma.desktop && sudo systemctl restart display-manager sudo /usr/lib/steamos/steam-set-session plasma.desktop && sudo systemctl restart display-manager
``` ```
That takes you back to the desktop. More in [TROUBLESHOOTING.md](TROUBLESHOOTING.md). How it all works:
[TECHNICAL.md](TECHNICAL.md). Changes per version: [CHANGELOG.md](CHANGELOG.md).
**The LED bar on the Steam Machine stays dark.** Restart the PC once more; a Contributing (or an AI agent)? Read [AGENTS.md](AGENTS.md).
freshly installed driver often needs a reboot. Still dark? See
[Front LED bar](#front-led-bar-steam-machine) under technical details.
**Undo something:** run the wizard again and untick it.
---
## Technical details
### How turning things on and off works
At startup every component checks whether it is on (`*_status` in `lib/`).
You choose; the wizard then turns off what you unticked (in reverse order)
and turns on what you ticked. To be able to undo:
- **System files** are backed up once, next to the original, with a
`.bak-gamescope-wizard` suffix, and restored when you turn the component
off.
- **KDE settings** in your home directory are recorded with their previous
value the first time the wizard changes them, in an undo journal under
`~/.local/state/cachyos-gamescope-boot/`. Turning the component off writes
the old values back (or removes keys that didn't exist before). Setups
made by older versions of the script, without a journal, fall back to
KDE's defaults.
- **Packages** installed for the conversion (Steam, gamescope-session, ...)
are kept when you turn it off; Steam Machine support removes its own.
### Single user mode: SDDM, no locking
Turning it on switches to **SDDM** and turns off everything that asks for a
password or another user, per user: `action/lock_screen`, `switch_user` and
`start_new_session` restrictions in `kdeglobals`, no automatic locking
(`kscreenlockerrc`), Meta+L and Ctrl+Alt+Del unbound, and the launcher shows
only Sleep / Restart / Shut Down (kickoff `primaryActions=3`), which hides
the Session dropdown with Log Out. (Restricting `action/logout` would also
hide Restart and Shut Down.) Turning it off restores all of that and moves
the conversion back to plasma-login-manager.
**SDDM** is what SteamOS uses, and CachyOS's `steam-set-session` supports it
directly: it writes `/etc/sddm.conf.d/zz-steamos-autologin.conf`, which SDDM
honours. The script installs and enables `sddm` (active from the next boot),
writes `User=`, `Session=` and `Relogin=true` to
`/etc/sddm.conf.d/10-gamescope-autologin.conf`, and removes any
`[Autologin]` from `/etc/sddm.conf` (read last, so it would override both).
No sync bridge or sudoers rule is needed; the shortcut just runs
`steamos-session-select gamescope`, which logs out, and `Relogin=true` logs
straight back in to gamescope.
Without single user mode, the conversion uses **plasma-login-manager**
(CachyOS's default since March 2026), which needs the workarounds below.
### Why this is needed (plasma-login-manager)
CachyOS's `gamescope-session-cachyos` package ships `steamos-session-select`,
which is meant to work exactly like it does on real SteamOS. Since the March
2026 release, CachyOS uses `plasma-login-manager` instead of SDDM, and three
things get in the way:
1. **No `User=` written to the autologin config.**
`steam-set-session` (called by `steamos-session-select`) only writes a
`Session=` key. `plasma-login-manager` needs both `Session=` and `User=`
to autologin at all; without `User=` it shows the greeter every boot.
2. **Missing `/etc/plasmalogin.conf.d` directory.**
Without it, `steam-set-session` fails, and Steam's "Switch to Desktop"
hangs forever. *(Fixed upstream in `gamescope-session-cachyos` 1.1.6; the
script still creates the directory for older versions.)*
3. **The base `/etc/plasmalogin.conf` wins over conf.d, and CachyOS ships it
with `Session=plasma`.**
Every tool that changes sessions (`steamos-session-select`, Steam's power
menu, and `cachyos-gamescope-autologin.service`, which resets you to
gamescope after a desktop session) only writes
`/etc/plasmalogin.conf.d/zz-steamos-autologin.conf`. The base file takes
priority, so none of those switches stick.
The script sets `Session=`, `User=` and `Relogin=true` in the base config,
and installs a small systemd path watcher that copies whatever CachyOS's
tools write to the conf.d fragment into the base config.
### What each component changes
**SteamOS conversion** (`lib/login-manager.sh`, `lib/steam-desktop.sh`,
`lib/desktop-shortcut.sh`):
- installs missing packages: `gamescope-session-cachyos`, `steam`,
`mangohud`, `xterm`, `ttf-liberation`, `wqy-zenhei`, `plasma-keyboard`;
- *SDDM (single user mode):* installs/enables SDDM and writes its autologin
config;
- *plasma-login-manager:* creates `/etc/plasmalogin.conf.d`, backs up and
rewrites the `[Autologin]` section of `/etc/plasmalogin.conf`, removes
stray `zzz-steamos-autologin*.conf` files from manual troubleshooting
(never `zz-steamos-autologin.conf`, which CachyOS's tools own), and
installs `/usr/local/bin/sync-steamos-session.sh` plus
`sync-steamos-session.path`/`.service`;
- `KWIN_IM_SHOW_ALWAYS=1` for the virtual keyboard and a
`steam-desktop-autostart` systemd user service that starts Steam silently
in Plasma only;
- the **Return to Gaming Mode** shortcut; on plasma-login-manager with a
narrow sudoers rule (`/etc/sudoers.d/gamescope-session-switch`) so it can
restart the login manager without a password prompt.
Turning it off restores `/etc/plasmalogin.conf` from its backup, removes the
SDDM autologin, sync bridge, shortcut, sudoers rule and Steam autostart, and
switches back to plasma-login-manager.
**Steam Deck/Machine icons:** `STEAM_GAMEPADUI_ARGS="-gamepadui -steamos3"`
in `~/.config/environment.d/` (and gamescope-session's own environment
file), which makes Steam show Steam Deck button glyphs in gaming mode.
### SteamOS desktop look
Installs CachyOS's `cachyos-vapor` package (the SteamOS Vapor theme the
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.
It also adds the SteamOS desktop extras that `cachyos-vapor` doesn't ship,
taken from the newest `steamdeck-kde-presets` on Valve's SteamOS mirror
(checksum verified) and installed to `/usr/local`:
- **Add to Steam** in the right-click menu of apps, AppImages and `.exe`
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.
Turning it off restores your previous look and panel layout, and removes `cachyos-vapor` again
if the wizard installed it (and nothing else, like `cachyos-handheld`, needs it).
### Front LED bar (Steam Machine)
The **Steam Machine support** component, only shown on Fremont hardware
(DMI `Valve`/`Fremont`, or `OEM`/`F7F` on early units):
- installs an AUR helper (`yay`) if neither `yay` nor `paru` is present;
- installs the kernel headers for every installed kernel;
- installs `leds-valve-dkms-git` from the AUR - Valve's own driver from the
SteamOS kernel - builds it for every installed kernel (so the LTS kernel
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
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
uses for hardware settings (fan, HDMI-CEC, performance); it recognises the
Steam Machine from its DMI data.
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
builds against the running kernel (`uname -r`) instead of the kernel DKMS
builds for, so the wizard adds a DKMS override
(`/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:
```bash
dkms status
ls -l /sys/class/leds/valve-leds*/
sudo dmesg | grep -i valve
cat /var/lib/dkms/leds-valve-dkms/0.1/build/make.log
journalctl --user -b | grep -i led
```
### BIOS updates (Steam Machine)
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):
```
[ ] 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
```bash
steamos-session-select gamescope # switch to gamescope right now
steamos-session-select plasma # switch to desktop right now
steamos-session-select persistent # remember the last-used session across reboots
steamos-session-select oneshot # always start in gamescope (default, like a Deck)
```
With `Relogin=true`, a gamescope session that fails to start is restarted
immediately, which can turn into a loop - hence the Ctrl+Alt+F3 escape above.
### Removing everything
Run the wizard and untick everything. Afterwards the PC boots to the normal
CachyOS login screen again; packages installed for the conversion (Steam,
gamescope-session, ...) stay installed.
### Project layout
| File | Responsibility |
|---|---|
| `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/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/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
Actions runs `.github/tools/bundle.sh`, checks the result with `bash -n` and
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
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).
Notes for contributors and AI coding agents are in [`AGENTS.md`](AGENTS.md).
### Caveats
- This works around CachyOS/`plasma-login-manager` behavior as of September
2026. If CachyOS fixes `steam-set-session` upstream, parts of this script
become unnecessary - please open an issue or PR if you notice that.
- Tested on a Valve Steam Machine running CachyOS Desktop edition. It should
work on any CachyOS desktop install using `plasma-login-manager`, but
hasn't been tested on Steam Deck/Legion Go hardware.
### Related upstream reports
- [CachyOS/gamescope-session#9](https://github.com/CachyOS/gamescope-session/issues/9) - "Switch to Desktop" hang due to missing `/etc/plasmalogin.conf.d`
## License ## License
MIT - do whatever you want with it. MIT - do whatever you want with it. See [LICENSE.md](LICENSE.md).
+285
View File
@@ -0,0 +1,285 @@
# Technical details
How Steamify CachyOS works under the hood. For using it, see
[README.md](README.md); for problems, [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
## How turning things on and off works
At startup every component checks whether it is on (`*_status` in `lib/`).
You choose; the wizard then turns off what you unticked (in reverse order)
and turns on what you ticked. To be able to undo:
- **System files** are backed up once, next to the original, with a
`.bak-gamescope-wizard` suffix, and restored when you turn the component
off.
- **KDE settings** in your home directory are recorded with their previous
value the first time the wizard changes them, in an undo journal under
`~/.local/state/cachyos-gamescope-boot/`. Turning the component off writes
the old values back (or removes keys that didn't exist before). Setups
made by older versions of the script, without a journal, fall back to
KDE's defaults.
- **Packages** installed for the conversion (Steam, gamescope-session, ...)
are kept when you turn it off; Steam Machine support removes its own.
## Single user mode: SDDM, no locking
Turning it on switches to **SDDM** and turns off everything that asks for a
password or another user, per user: `action/lock_screen`, `switch_user` and
`start_new_session` restrictions in `kdeglobals`, no automatic locking
(`kscreenlockerrc`), Meta+L and Ctrl+Alt+Del unbound, and the launcher shows
only Sleep / Restart / Shut Down (kickoff `primaryActions=3`), which hides
the Session dropdown with Log Out. (Restricting `action/logout` would also
hide Restart and Shut Down.) Turning it off restores all of that and moves
the conversion back to plasma-login-manager.
**KDE wallet.** KDE normally unlocks your wallet with the password you log in
with; with autologin nobody types it, so apps that keep passwords in it (Brave,
for example) ask for the wallet password. Single user mode does what SteamOS
does: it uses Valve's empty wallet without a password (from
`steamdeck-kde-presets`, checksum verified). Your own wallet in
`~/.local/share/kwalletd/` is moved to `kdewallet.kwl.bak-steamify` (and
`.salt`). Turning single user mode off puts it back, unchanged; the single
user wallet, with anything saved in it meanwhile, is kept as
`kdewallet.kwl.steamify-single-user` and used again the next time it's on.
Passwords aren't shared between the two wallets.
**SDDM** is what SteamOS uses, and CachyOS's `steam-set-session` supports it
directly: it writes `/etc/sddm.conf.d/zz-steamos-autologin.conf`, which SDDM
honours. The script installs and enables `sddm` (active from the next boot),
writes `User=`, `Session=` and `Relogin=true` to
`/etc/sddm.conf.d/10-gamescope-autologin.conf`, and removes any
`[Autologin]` from `/etc/sddm.conf` (read last, so it would override both).
No sync bridge or sudoers rule is needed; the shortcut just runs
`steamos-session-select gamescope`, which logs out, and `Relogin=true` logs
straight back in to gamescope.
Without single user mode, the conversion uses **plasma-login-manager**
(CachyOS's default since March 2026), which needs the workarounds below.
## Why this is needed (plasma-login-manager)
CachyOS's `gamescope-session-cachyos` package ships `steamos-session-select`,
which is meant to work exactly like it does on real SteamOS. Since the March
2026 release, CachyOS uses `plasma-login-manager` instead of SDDM, and three
things get in the way:
1. **No `User=` written to the autologin config.**
`steam-set-session` (called by `steamos-session-select`) only writes a
`Session=` key. `plasma-login-manager` needs both `Session=` and `User=`
to autologin at all; without `User=` it shows the greeter every boot.
2. **Missing `/etc/plasmalogin.conf.d` directory.**
Without it, `steam-set-session` fails, and Steam's "Switch to Desktop"
hangs forever. *(Fixed upstream in `gamescope-session-cachyos` 1.1.6; the
script still creates the directory for older versions.)*
3. **The base `/etc/plasmalogin.conf` wins over conf.d, and CachyOS ships it
with `Session=plasma`.**
Every tool that changes sessions (`steamos-session-select`, Steam's power
menu, and `cachyos-gamescope-autologin.service`, which resets you to
gamescope after a desktop session) only writes
`/etc/plasmalogin.conf.d/zz-steamos-autologin.conf`. The base file takes
priority, so none of those switches stick.
The script sets `Session=`, `User=` and `Relogin=true` in the base config,
and installs a small systemd path watcher that copies whatever CachyOS's
tools write to the conf.d fragment into the base config.
## What each component changes
**SteamOS conversion** (`lib/login-manager.sh`, `lib/steam-desktop.sh`,
`lib/desktop-shortcut.sh`):
- installs missing packages: `gamescope-session-cachyos`, `steam`,
`mangohud`, `xterm`, `ttf-liberation`, `wqy-zenhei`, `plasma-keyboard`;
- *SDDM (single user mode):* installs/enables SDDM and writes its autologin
config;
- *plasma-login-manager:* creates `/etc/plasmalogin.conf.d`, backs up and
rewrites the `[Autologin]` section of `/etc/plasmalogin.conf`, removes
stray `zzz-steamos-autologin*.conf` files from manual troubleshooting
(never `zz-steamos-autologin.conf`, which CachyOS's tools own), and
installs `/usr/local/bin/sync-steamos-session.sh` plus
`sync-steamos-session.path`/`.service`;
- `KWIN_IM_SHOW_ALWAYS=1` for the virtual keyboard and a
`steam-desktop-autostart` systemd user service that starts Steam silently
in Plasma only;
- the **Return to Gaming Mode** shortcut; on plasma-login-manager with a
narrow sudoers rule (`/etc/sudoers.d/gamescope-session-switch`) so it can
restart the login manager without a password prompt.
Turning it off restores `/etc/plasmalogin.conf` from its backup, removes the
SDDM autologin, sync bridge, shortcut, sudoers rule and Steam autostart, and
switches back to plasma-login-manager.
**Steam Deck/Machine icons:** `STEAM_GAMEPADUI_ARGS="-gamepadui -steamos3"`
in `~/.config/environment.d/` (and gamescope-session's own environment
file), which makes Steam show Steam Deck button glyphs in gaming mode.
## SteamOS desktop look
Installs CachyOS's `cachyos-vapor` package (the SteamOS Vapor theme the
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.
It also adds the SteamOS desktop extras that `cachyos-vapor` doesn't ship,
taken from the newest `steamdeck-kde-presets` on Valve's SteamOS mirror
(checksum verified) and installed to `/usr/local`:
- **Add to Steam** in the right-click menu of apps, AppImages and `.exe`
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.
Turning it off restores your previous look and panel layout, and removes `cachyos-vapor` again
if the wizard installed it (and nothing else, like `cachyos-handheld`, needs it).
## Front LED bar (Steam Machine)
The **Steam Machine support** component, only shown on Fremont hardware
(DMI `Valve`/`Fremont`, or `OEM`/`F7F` on early units):
- installs an AUR helper (`yay`) if neither `yay` nor `paru` is present;
- installs the kernel headers for every installed kernel;
- installs `leds-valve-dkms-git` from the AUR - Valve's own driver from the
SteamOS kernel - builds it for every installed kernel (so the LTS kernel
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
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
uses for hardware settings (fan, HDMI-CEC, performance); it recognises the
Steam Machine from its DMI data.
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
builds against the running kernel (`uname -r`) instead of the kernel DKMS
builds for, so the wizard adds a DKMS override
(`/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.
## Kernel pin (Steam Machine)
With CachyOS kernels newer than 7.1.6 a Steam Machine reboots instead of
shutting down. The **Pin the kernel** sub-option (ticked along with Steam
Machine support) installs `linux-cachyos` and `linux-cachyos-headers`
7.1.6-1 and adds them to `IgnorePkg` in `/etc/pacman.conf`, so updates skip
them. DKMS builds the LED driver for it; restart to boot it.
The packages (and their signatures, which pacman checks) are kept in
`/var/cache/steamify/kernel`, so re-applying needs no download. Missing
files are taken from pacman's cache, else downloaded from this repo's
`kernel-7.1.6-1` release, then `archive.cachyos.org`, then
`mirror.cachyos.org` (which only has the current kernel). Every file must
match the SHA-256 in the script and have a valid CachyOS signature; a bad
one is deleted, so the next run downloads it again. Set `PINNED_KERNEL_URL`
to a directory URL with the files to try another source first, or drop them
into the kernel directory yourself.
Unticking it removes the pin and runs `sudo pacman -Syu`, which brings the
kernel back to CachyOS's current version; the files stay for next time.
## BIOS updates (Steam Machine)
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):
```
[ ] 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
```bash
steamos-session-select gamescope # switch to gamescope right now
steamos-session-select plasma # switch to desktop right now
steamos-session-select persistent # remember the last-used session across reboots
steamos-session-select oneshot # always start in gamescope (default, like a Deck)
```
With `Relogin=true`, a gamescope session that fails to start is restarted
immediately, which can turn into a loop - see
[TROUBLESHOOTING.md](TROUBLESHOOTING.md) for the way out.
## Project layout
| File | Responsibility |
|---|---|
| `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; kernel pin |
| `.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
Actions runs `.github/tools/bundle.sh`, checks the result with `bash -n` and
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
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).
Notes for contributors and AI coding agents are in [`AGENTS.md`](AGENTS.md).
## Caveats
- This works around CachyOS/`plasma-login-manager` behavior as of September
2026. If CachyOS fixes `steam-set-session` upstream, parts of this script
become unnecessary - please open an issue or PR if you notice that.
- Tested on a Valve Steam Machine running CachyOS Desktop edition. It should
work on any CachyOS desktop install using `plasma-login-manager`, but
hasn't been tested on Steam Deck/Legion Go hardware.
## Related upstream reports
- [CachyOS/gamescope-session#9](https://github.com/CachyOS/gamescope-session/issues/9) - "Switch to Desktop" hang due to missing `/etc/plasmalogin.conf.d`
+57
View File
@@ -0,0 +1,57 @@
# Troubleshooting
See [TECHNICAL.md](TECHNICAL.md) for how each part works.
## Gaming mode shows a black screen or keeps restarting
Press **Ctrl+Alt+F3**, log in with your username and password, and run:
```bash
sudo /usr/lib/steamos/steam-set-session plasma.desktop && sudo systemctl restart display-manager
```
That takes you back to the desktop. Gamescope sessions that fail to start are
restarted right away (`Relogin=true`), which is why it can loop.
## Something is set up wrong
Run the wizard again and press `a` in the menu to re-apply everything that's
on (also useful after a CachyOS update). To undo a part, untick it.
## The LED bar on the Steam Machine stays dark
Restart once more; a freshly installed driver often needs a reboot. The menu
shows per kernel whether the LED driver is built, and whether it's loaded.
Still dark:
```bash
dkms status
ls -l /sys/class/leds/valve-leds*/
sudo dmesg | grep -i valve
cat /var/lib/dkms/leds-valve-dkms/0.1/build/make.log
journalctl --user -b | grep -i led
```
## The Steam Machine still reboots when I shut it down
Check that the pinned kernel is installed and running:
```bash
uname -r # should be 7.1.6-1-cachyos
pacman -Q linux-cachyos linux-cachyos-headers
grep '^IgnorePkg' /etc/pacman.conf # should list both packages
```
Not running it yet? Restart. Not installed? Run the wizard and make sure
**Pin the kernel** under Steam Machine support is ticked. If you have more
kernels installed (e.g. the LTS kernel), pick `linux-cachyos` in the boot menu.
## The kernel pin can't download or verify the kernel
The wizard tries this repo's `kernel-7.1.6-1` release, then the CachyOS
archive and mirror. A file that fails its checksum or CachyOS signature is
deleted, so running the wizard again downloads it once more. Offline? Put
the four files from the
[kernel-7.1.6-1 release](https://github.com/theupriser/steamify-cachyos/releases/tag/kernel-7.1.6-1)
in `/var/cache/steamify/kernel` and run it again.
+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
}
+63 -10
View File
@@ -5,31 +5,51 @@
# 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 launcher machine bios) COMPONENTS=(gaming boot theme glyphs single launcher machine kpin bios)
# One-off actions rather than on/off components: never preselected, never # One-off actions rather than on/off components: never preselected, never
# re-applied, not listed as on or off. # re-applied, not listed as on or off.
ACTIONS=(bios) ACTIONS=(bios)
# Sub-options, shown indented under their parent and only while it's ticked.
declare -A PARENT=([boot]=gaming [kpin]=machine)
# Never preselected on a first run: booting into the desktop is a choice,
# gamescope is the default.
NO_PRESELECT=(boot)
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"
[boot]="Boot into the desktop instead of gaming mode"
[theme]="Install SteamOS theme: Vapor look (cachyos-vapor)" [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" [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"
[kpin]="Pin the kernel to $PINNED_KERNEL_VER (fixes rebooting after shutdown)"
[bios]="Update BIOS" [bios]="Update BIOS"
) )
declare -A CURRENT WANTED declare -A CURRENT WANTED
component_available() { component_available() {
case "$1" in case "$1" in
machine) machine_available ;; machine|kpin) machine_available ;;
bios) bios_available ;; bios) bios_available ;;
esac esac
} }
is_action() { [[ " ${ACTIONS[*]} " == *" $1 "* ]]; } 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. A sub-option ("Boot into", the kernel pin) only
# while its parent is ticked.
component_available "$1" || return 1
[[ -z "${PARENT[$1]:-}" || "${WANTED[${PARENT[$1]}]:-0}" == 1 ]]
}
component_selectable() { component_selectable() {
# Greyed out and not tickable when it has nothing to do. # Greyed out and not tickable when it has nothing to do.
case "$1" in case "$1" in
@@ -49,7 +69,8 @@ detect_components() {
# First run: preselect the full SteamOS experience (never an action). # 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" && ! is_action "$c" && WANTED[$c]=1 component_available "$c" && ! is_action "$c" &&
[[ " ${NO_PRESELECT[*]} " != *" $c "* ]] && WANTED[$c]=1
done done
fi fi
} }
@@ -60,7 +81,12 @@ toggle_component() {
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
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
# The kernel pin is opt-out: ticked along with Steam Machine support.
if [[ "$c" == machine ]]; then WANTED[kpin]=${WANTED[machine]}; fi
if [[ "$c" == kpin && "${WANTED[kpin]}" == 1 ]]; then WANTED[machine]=1; fi
} }
show_menu() { show_menu() {
@@ -69,7 +95,7 @@ show_menu() {
printf " ${c_bold}%-3s %-6s %-6s %s${c_reset}\n" "#" "Now" "Want" "Component" printf " ${c_bold}%-3s %-6s %-6s %s${c_reset}\n" "#" "Now" "Want" "Component"
MENU_ITEMS=() MENU_ITEMS=()
for c in "${COMPONENTS[@]}"; do for c in "${COMPONENTS[@]}"; do
component_available "$c" || continue menu_visible "$c" || continue
i=$((i + 1)); MENU_ITEMS[$i]=$c i=$((i + 1)); MENU_ITEMS[$i]=$c
# Pad the plain word, then colour it: colour codes would count as width. # Pad the plain word, then colour it: colour codes would count as width.
now="off"; [[ "${CURRENT[$c]}" == 1 ]] && now="on" now="off"; [[ "${CURRENT[$c]}" == 1 ]] && now="on"
@@ -77,7 +103,15 @@ show_menu() {
now="$(printf '%-6s' "$now")" now="$(printf '%-6s' "$now")"
[[ "${CURRENT[$c]}" == 1 ]] && now="${now/on/${c_green}on${c_reset}}" [[ "${CURRENT[$c]}" == 1 ]] && now="${now/on/${c_green}on${c_reset}}"
want="[ ]"; [[ "${WANTED[$c]}" == 1 ]] && want="[x]" 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 [[ -n "${PARENT[$c]:-}" ]]; then
printf " %-3s %b %-6s └ %s\n" "$i" "$now" "$want" "${LABEL[$c]}"
elif component_selectable "$c"; then
printf " %-3s %b %-6s %s\n" "$i" "$now" "$want" "${LABEL[$c]}" printf " %-3s %b %-6s %s\n" "$i" "$now" "$want" "${LABEL[$c]}"
else else
printf " %b%-3s %-6s %-6s %s (not available)%b\n" "$c_dim" "$i" "$now" "$want" "${LABEL[$c]}" "$c_reset" printf " %b%-3s %-6s %-6s %s (not available)%b\n" "$c_dim" "$i" "$now" "$want" "${LABEL[$c]}" "$c_reset"
@@ -109,12 +143,23 @@ draw_menu_tui() {
echo echo
MENU_ITEMS=() MENU_ITEMS=()
for c in "${COMPONENTS[@]}"; do for c in "${COMPONENTS[@]}"; do
component_available "$c" || continue menu_visible "$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=" (now: off)"; [[ "${CURRENT[$c]}" == 1 ]] && state=" (now: ${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)" is_action "$c" && state=" (opt-in, runs once)"
line="$box ${LABEL[$c]}" 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)"
elif [[ -n "${PARENT[$c]:-}" ]]; then
line=" └ $line"
fi
if ! component_selectable "$c"; then if ! component_selectable "$c"; then
# Greyed out: nothing to do (e.g. BIOS already up to date). # Greyed out: nothing to do (e.g. BIOS already up to date).
local mark=" "; (( i == cursor )) && mark=" ${c_cyan}>${c_reset} " local mark=" "; (( i == cursor )) && mark=" ${c_cyan}>${c_reset} "
@@ -130,7 +175,8 @@ draw_menu_tui() {
echo echo
echo -e "$KERNEL_OVERVIEW" echo -e "$KERNEL_OVERVIEW"
echo 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() { run_menu_tui() {
@@ -140,6 +186,8 @@ run_menu_tui() {
while true; do while true; do
draw_menu_tui "$cursor" draw_menu_tui "$cursor"
count=${#MENU_ITEMS[@]} 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 IFS= read -rsn1 key
if [[ "$key" == $'\e' ]]; then if [[ "$key" == $'\e' ]]; then
IFS= read -rsn2 -t 0.05 rest IFS= read -rsn2 -t 0.05 rest
@@ -149,6 +197,9 @@ run_menu_tui() {
$'\e[A'|k) (( cursor = (cursor + count - 1) % count )) ;; $'\e[A'|k) (( cursor = (cursor + count - 1) % count )) ;;
$'\e[B'|j) (( cursor = (cursor + 1) % count )) ;; $'\e[B'|j) (( cursor = (cursor + 1) % count )) ;;
" ") toggle_component "${MENU_ITEMS[$cursor]}" ;; " ") 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 ;; "") break ;;
a|A) REAPPLY=true; break ;; a|A) REAPPLY=true; break ;;
q|Q) tput cnorm 2>/dev/null; echo; return 1 ;; q|Q) tput cnorm 2>/dev/null; echo; return 1 ;;
@@ -208,11 +259,13 @@ apply_changes() {
[[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm [[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm
for c in "${TO_DISABLE[@]}"; do 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") "${c}_disable" || failed+=("$c")
done done
for c in "${TO_ENABLE[@]}"; do 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 else echo; echo -e "${c_bold}Turning on: ${LABEL[$c]}${c_reset}"; fi
"${c}_enable" || failed+=("$c") "${c}_enable" || failed+=("$c")
done done
+6 -1
View File
@@ -41,8 +41,13 @@ bootstrap_yay() {
return 1 return 1
fi fi
# Build only, then install it ourselves: makepkg -i runs `sudo -k pacman`,
# which forgets the cached sudo password and asks for it again.
info "Building and installing yay (this runs makepkg as your user, not root)..." info "Building and installing yay (this runs makepkg as your user, not root)..."
if (cd "$tmp_dir/yay-bin" && makepkg -si --noconfirm); then local -a pkgs
if (cd "$tmp_dir/yay-bin" && makepkg --noconfirm) &&
mapfile -t pkgs < <(cd "$tmp_dir/yay-bin" && makepkg --packagelist | grep -v -- '-debug-') &&
sudo pacman -U --noconfirm "${pkgs[@]}"; then
ok "yay installed." ok "yay installed."
rm -rf "$tmp_dir" rm -rf "$tmp_dir"
return 0 return 0
+74
View File
@@ -9,6 +9,78 @@ 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 ]]
} }
WALLET_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/kwalletd"
# The user's own wallet while single user mode is on, and ours after.
WALLET_BACKUP=".bak-steamify"
WALLET_OURS=".steamify-single-user"
stop_kwallet() {
# The wallet daemon keeps the file open and would write it back.
local d
for d in kwalletd6 ksecretd; do
pkill -u "$USER" -x "$d" 2>/dev/null && sleep 1
done
return 0
}
single_wallet_enable() {
# Like SteamOS: an empty, password-less wallet (Valve's own file), so
# nothing asks for a wallet password (e.g. Brave at start): with
# autologin nobody typed the login password that unlocks the usual one.
# The user's wallet is moved aside and comes back when this is off. The
# wallet from an earlier time in single user mode is reused, with
# whatever was saved in it then.
local tmp f
if [[ -f "$WALLET_DIR/kdewallet.kwl$WALLET_OURS" ]]; then
stop_kwallet
for f in kdewallet.kwl kdewallet.salt; do
[[ -f "$WALLET_DIR/$f" && ! -e "$WALLET_DIR/$f$WALLET_BACKUP" ]] &&
mv "$WALLET_DIR/$f" "$WALLET_DIR/$f$WALLET_BACKUP"
[[ -f "$WALLET_DIR/$f$WALLET_OURS" ]] && mv -f "$WALLET_DIR/$f$WALLET_OURS" "$WALLET_DIR/$f"
done
kset single kwalletrc Wallet "First Use" false
ok "Single user mode's wallet (no password) is back; your own is kept as kdewallet.kwl$WALLET_BACKUP."
return 0
fi
tmp="$(mktemp -d)"
if ! fetch_valve_presets "$tmp"; then
rm -rf "$tmp"
warn "Couldn't get Valve's empty wallet; apps may still ask for the wallet password."
return 0
fi
if [[ -f "$WALLET_DIR/kdewallet.kwl" ]] &&
cmp -s "$WALLET_DIR/kdewallet.kwl" "$tmp/usr/share/kwalletd/kdewallet.kwl"; then
rm -rf "$tmp"
return 0
fi
stop_kwallet
mkdir -p "$WALLET_DIR"
for f in kdewallet.kwl kdewallet.salt; do
# Never overwrite an earlier backup: that one is the user's.
[[ -f "$WALLET_DIR/$f" && ! -e "$WALLET_DIR/$f$WALLET_BACKUP" ]] &&
mv "$WALLET_DIR/$f" "$WALLET_DIR/$f$WALLET_BACKUP"
install -m 600 "$tmp/usr/share/kwalletd/$f" "$WALLET_DIR/$f"
done
rm -rf "$tmp"
kset single kwalletrc Wallet "First Use" false
[[ -f "$WALLET_DIR/kdewallet.kwl$WALLET_BACKUP" ]] &&
info "Your wallet is kept as $WALLET_DIR/kdewallet.kwl$WALLET_BACKUP and comes back when single user mode is off."
ok "Empty wallet without a password: apps no longer ask for the wallet password."
}
single_wallet_disable() {
# The user's wallet back; ours (maybe with passwords saved since) is
# kept next to it.
[[ -f "$WALLET_DIR/kdewallet.kwl$WALLET_BACKUP" ]] || return 0
stop_kwallet
local f
for f in kdewallet.kwl kdewallet.salt; do
[[ -f "$WALLET_DIR/$f" ]] && mv -f "$WALLET_DIR/$f" "$WALLET_DIR/$f$WALLET_OURS"
[[ -f "$WALLET_DIR/$f$WALLET_BACKUP" ]] && mv "$WALLET_DIR/$f$WALLET_BACKUP" "$WALLET_DIR/$f"
done
ok "Your own wallet is back (the empty one is kept as kdewallet.kwl$WALLET_OURS)."
}
single_launcher() { single_launcher() {
# Launcher: only Sleep / Restart / Shut Down, no Session dropdown (where # Launcher: only Sleep / Restart / Shut Down, no Session dropdown (where
# Log Out lives). Restricting action/logout instead would also hide # Log Out lives). Restricting action/logout instead would also hide
@@ -42,6 +114,7 @@ single_enable() {
single_launcher single_launcher
restart_plasmashell_if_stopped restart_plasmashell_if_stopped
single_wallet_enable
ok "Single user: no lock screen, user switching or log out." ok "Single user: no lock screen, user switching or log out."
} }
@@ -70,5 +143,6 @@ single_disable() {
done done
fi fi
restart_plasmashell_if_stopped restart_plasmashell_if_stopped
single_wallet_disable
ok "KDE's normal lock screen and user switching are back." ok "KDE's normal lock screen and user switching are back."
} }
+167 -3
View File
@@ -14,16 +14,175 @@ detect_valve_fremont() {
[[ "$vendor" == "OEM" && "$product" == "F7F" ]] [[ "$vendor" == "OEM" && "$product" == "F7F" ]]
} }
# Kernel pinned on a Steam Machine: with newer linux-cachyos releases it
# reboots instead of shutting down. The packages are kept in
# PINNED_KERNEL_DIR, so re-applying (or reinstalling after an update slipped
# through) needs no download.
PINNED_KERNEL_VER="7.1.6-1"
PINNED_KERNEL_KVER="7.1.6-1-cachyos"
PINNED_KERNEL_PKGS=(linux-cachyos linux-cachyos-headers)
PINNED_KERNEL_DIR="${PINNED_KERNEL_DIR:-/var/cache/steamify/kernel}"
# SHA-256 of each package, so a file from any source is the one reviewed
# here; its CachyOS signature is checked as well.
declare -A PINNED_KERNEL_SHA256=(
[linux-cachyos]=417fcd07102192b86e78e92ed7171d5a49378e9f57faea3fa2c7c541e9f68d08
[linux-cachyos-headers]=d069866a11d9092746e1d5133cefd8924f027da2c9bf5af38d30b3aed6ded9bf
)
# Tried in order, after the kernel dir and pacman's cache. Our own release
# always has these files; the archive keeps every release; the mirror only
# the current one. PINNED_KERNEL_URL puts another source (a directory
# holding the files) in front.
PINNED_KERNEL_SOURCES=(
${PINNED_KERNEL_URL:+"$PINNED_KERNEL_URL"}
"https://github.com/theupriser/steamify-cachyos/releases/download/kernel-$PINNED_KERNEL_VER"
"https://archive.cachyos.org/archive/cachyos"
"https://mirror.cachyos.org/repo/x86_64/cachyos"
)
pinned_kernel_installed() {
local p
for p in "${PINNED_KERNEL_PKGS[@]}"; do
[[ "$(pacman -Q "$p" 2>/dev/null)" == "$p $PINNED_KERNEL_VER" ]] || return 1
done
}
fetch_pinned_kernel_file() {
# $1 = file name. Local kernel dir first, then pacman's cache, then the
# download sources. Downloads go to a .part file so a broken one is never
# mistaken for a finished one.
local f="$1" dest="$PINNED_KERNEL_DIR/$1" src
[[ -s "$dest" ]] && return 0
if [[ -s "/var/cache/pacman/pkg/$f" ]]; then
sudo cp "/var/cache/pacman/pkg/$f" "$dest" && return 0
fi
for src in "${PINNED_KERNEL_SOURCES[@]}"; do
if sudo curl -fL --retry 2 --connect-timeout 15 -o "$dest.part" "$src/$f" 2>/dev/null; then
sudo mv "$dest.part" "$dest"
return 0
fi
warn "Couldn't get $f from $src, trying the next source..."
done
sudo rm -f "$dest.part"
return 1
}
verify_pinned_kernel_pkg() {
# $1 = package name, $2 = file. Both checks must pass: the SHA-256 from
# this script, and the CachyOS signature (pacman on its own installs a
# local file without a .sig: LocalFileSigLevel = Optional).
local sum
sum="$(sha256sum "$2" | cut -d' ' -f1)"
if [[ "$sum" != "${PINNED_KERNEL_SHA256[$1]}" ]]; then
err "$(basename "$2") doesn't match its expected checksum."
return 1
fi
if ! sudo pacman-key --verify "$2.sig" "$2" >/dev/null 2>&1; then
err "$(basename "$2") doesn't have a valid CachyOS signature."
return 1
fi
}
pin_kernel_in_pacman_conf() {
# Adds our packages to IgnorePkg in [options], keeping what's there.
local p
for p in "${PINNED_KERNEL_PKGS[@]}"; do
grep -Eq "^IgnorePkg\s*=.*(\s|=)$p(\s|$)" /etc/pacman.conf && continue
if grep -Eq '^IgnorePkg\s*=' /etc/pacman.conf; then
sudo sed -i -E "0,/^IgnorePkg\s*=/s/^(IgnorePkg\s*=.*)$/\1 $p/" /etc/pacman.conf
else
sudo sed -i -E "0,/^\[options\]/s//[options]\nIgnorePkg = $p/" /etc/pacman.conf
fi
done
}
unpin_kernel_in_pacman_conf() {
local p
for p in "${PINNED_KERNEL_PKGS[@]}"; do
sudo sed -i -E "/^IgnorePkg\s*=/s/\s$p(\s|$)/\1/" /etc/pacman.conf
done
# Drop the line if nothing is left on it.
sudo sed -i -E '/^IgnorePkg\s*=\s*$/d' /etc/pacman.conf
}
install_pinned_kernel() {
sudo mkdir -p "$PINNED_KERNEL_DIR"
pin_kernel_in_pacman_conf
if pinned_kernel_installed; then
ok "Kernel $PINNED_KERNEL_VER already installed and pinned."
return 0
fi
local p f files=()
info "Getting kernel $PINNED_KERNEL_VER (kept in $PINNED_KERNEL_DIR)..."
for p in "${PINNED_KERNEL_PKGS[@]}"; do
f="$p-$PINNED_KERNEL_VER-x86_64.pkg.tar.zst"
fetch_pinned_kernel_file "$f" && fetch_pinned_kernel_file "$f.sig" ||
{ err "Couldn't download $f from any source."; return 1; }
if ! verify_pinned_kernel_pkg "$p" "$PINNED_KERNEL_DIR/$f"; then
# Removed, so the next run fetches it again.
sudo rm -f "$PINNED_KERNEL_DIR/$f" "$PINNED_KERNEL_DIR/$f.sig"
err "Removed it; run the wizard again to download it once more."
return 1
fi
files+=("$PINNED_KERNEL_DIR/$f")
done
# pacman checks each package against the .sig next to it.
info "Installing kernel $PINNED_KERNEL_VER..."
if ! sudo pacman -U --noconfirm "${files[@]}"; then
err "Installing kernel $PINNED_KERNEL_VER failed."
return 1
fi
[[ "$(uname -r)" != "$PINNED_KERNEL_KVER" ]] && RESTART_FOR_LOGIN=true
ok "Kernel $PINNED_KERNEL_VER installed and pinned (restart to use it)."
}
remove_kernel_pin() {
# Back to CachyOS's current kernel. The packages stay in
# PINNED_KERNEL_DIR, so turning support on again doesn't download.
unpin_kernel_in_pacman_conf
pinned_kernel_installed || return 0
info "Updating the kernel back to CachyOS's current version..."
sudo pacman -Syu --noconfirm ||
{ warn "Updating the kernel failed; run: sudo pacman -Syu"; return 0; }
RESTART_FOR_LOGIN=true
}
# "Pin the kernel" menu item, a sub-option of Steam Machine support.
kpin_status() {
pinned_kernel_installed && grep -Eq '^IgnorePkg\s*=.*\slinux-cachyos(\s|$)' /etc/pacman.conf
}
kpin_enable() {
install_pinned_kernel || return 1
# The DKMS pacman hook built leds-valve for the new headers; make sure.
if pacman -Qi leds-valve-dkms-git >/dev/null 2>&1 &&
! dkms status -k "$PINNED_KERNEL_KVER" leds-valve-dkms 2>/dev/null | grep -q installed; then
sudo dkms install leds-valve-dkms/0.1 -k "$PINNED_KERNEL_KVER" ||
warn "Building the LED driver for $PINNED_KERNEL_KVER failed."
fi
}
kpin_disable() { remove_kernel_pin; }
install_kernel_headers() { install_kernel_headers() {
# DKMS can only build leds-valve against kernels whose headers are # DKMS can only build leds-valve against kernels whose headers are
# installed. CachyOS ships several kernels (linux-cachyos, -lts, -bore, # installed. CachyOS ships several kernels (linux-cachyos, -lts, -bore,
# ...); install the matching -headers package for every one present. # ...); install the matching -headers package for every one present.
local -a kernels headers=() local -a kernels headers=()
mapfile -t kernels < <(pacman -Qqo /usr/lib/modules/*/pkgbase 2>/dev/null | sort -u)
local k local k
mapfile -t kernels < <(pacman -Qqo /usr/lib/modules/*/pkgbase 2>/dev/null | sort -u)
# Installed headers are left alone: -S would update them past a pinned
# kernel (linux-cachyos-headers on a Steam Machine).
local have=0
for k in "${kernels[@]}"; do for k in "${kernels[@]}"; do
pacman -Q "${k}-headers" >/dev/null 2>&1 && { have=$((have + 1)); continue; }
pacman -Si "${k}-headers" >/dev/null 2>&1 && headers+=("${k}-headers") pacman -Si "${k}-headers" >/dev/null 2>&1 && headers+=("${k}-headers")
done done
if [[ ${#headers[@]} -eq 0 && $have -gt 0 ]]; then
ok "Kernel headers already installed."
return 0
fi
if [[ ${#headers[@]} -eq 0 ]]; then if [[ ${#headers[@]} -eq 0 ]]; then
warn "Couldn't work out which kernel headers package you need." warn "Couldn't work out which kernel headers package you need."
warn "Install the -headers package for your kernel (e.g. linux-cachyos-headers) yourself." warn "Install the -headers package for your kernel (e.g. linux-cachyos-headers) yourself."
@@ -56,8 +215,13 @@ install_valve_led_driver() {
# got the running kernel's tree and failed (wrong kernel, and gcc against # 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 # a clang-built tree). DKMS reads this override after the package's
# dkms.conf; written before the install so its own build works too. # 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." \ # On a fresh system dkms isn't installed yet, so /etc/dkms doesn't exist.
'MAKE[0]="make KVERSION=${kernelver}"' | sudo tee "$LED_DKMS_OVERRIDE" >/dev/null 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 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)..."
+2 -12
View File
@@ -3,7 +3,7 @@
# newest Valve steamdeck-kde-presets package: "Add to Steam" (file # newest Valve steamdeck-kde-presets package: "Add to Steam" (file
# right-click menu and launcher), Nested Desktop (Plasma as a game inside # right-click menu and launcher), Nested Desktop (Plasma as a game inside
# gaming mode), the "Return to Gaming Mode" icon, the Steam Keyboard window # gaming mode), the "Return to Gaming Mode" icon, the Steam Keyboard window
# rule and an empty KWallet. Part of the SteamOS theme component # rule. Part of the SteamOS theme component
# (lib/vapor-theme.sh). System files go to /usr/local (nothing # (lib/vapor-theme.sh). System files go to /usr/local (nothing
# package-owned); per-user settings go through the undo journal. # package-owned); per-user settings go through the undo journal.
# Sourced by steamify.sh; not meant to be run on its own. # Sourced by steamify.sh; not meant to be run on its own.
@@ -75,15 +75,6 @@ extras_enable() {
sudo cp -r "$src/share/applications/steam/holo-nested-desktop" "$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" 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" rm -rf "$tmp_dir"
sudo gtk-update-icon-cache -q -f -t /usr/local/share/icons/hicolor 2>/dev/null || true sudo gtk-update-icon-cache -q -f -t /usr/local/share/icons/hicolor 2>/dev/null || true
@@ -122,7 +113,6 @@ extras_disable() {
for pkg in kdialog zstd curl; do for pkg in kdialog zstd curl; do
[[ -n "$(state_get theme "installed_$pkg")" ]] && sudo pacman -R --noconfirm "$pkg" >/dev/null 2>&1 [[ -n "$(state_get theme "installed_$pkg")" ]] && sudo pacman -R --noconfirm "$pkg" >/dev/null 2>&1
done done
# The keyboard rule and kwalletrc are reverted by the theme's journal. # The keyboard rule is reverted by the theme's journal.
# The wallet itself stays: secrets may have been saved in it since.
return 0 return 0
} }
+43 -23
View File
@@ -10,16 +10,16 @@
# them on or off to match what the user picks; turning one off restores # 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, # what was there before (system files from .bak-gamescope-wizard backups,
# KDE settings from the undo journal in lib/state.sh). Components live in # KDE settings from the undo journal in lib/state.sh). Components live in
# lib/, see README.md. Safe to re-run. # lib/, see TECHNICAL.md. Safe to re-run.
set -uo pipefail set -uo pipefail
# Release version, see CHANGELOG.md. # Release version, see CHANGELOG.md.
VERSION=0.9.0 VERSION=1.0.0
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 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 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 # shellcheck source=/dev/null
source "$SCRIPT_DIR/lib/$lib.sh" source "$SCRIPT_DIR/lib/$lib.sh"
done done
@@ -78,13 +78,36 @@ after_run() {
done 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 # 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. # the user quits; the restart question comes then, once, for everything.
RESTART_FOR_LOGIN=false RESTART_FOR_LOGIN=false
SUDO_KEEPALIVE=false SUDO_KEEPALIVE=false
while true; do while true; do
detect_components detect_components
run_menu || break if ! run_menu; then
quit_prompt && continue
break
fi
plan_changes plan_changes
if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then
@@ -95,9 +118,13 @@ while true; do
echo echo
echo -e "${c_bold}This will:${c_reset}" 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 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 elif [[ "${CURRENT[$c]}" == 1 ]]; then echo " - re-apply: ${LABEL[$c]}"; else echo " - turn on: ${LABEL[$c]}"; fi
done done
ask_yn "Go ahead?" y || { info "Nothing changed."; continue; } ask_yn "Go ahead?" y || { info "Nothing changed."; continue; }
@@ -112,7 +139,7 @@ while true; do
apply_changes apply_changes
# Login manager changes only take effect after a restart. # 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|kpin)\ ]] &&
RESTART_FOR_LOGIN=true RESTART_FOR_LOGIN=true
echo echo
@@ -120,7 +147,12 @@ while true; do
echo -e "${c_bold}Done. Current state:${c_reset}" echo -e "${c_bold}Done. Current state:${c_reset}"
for c in "${COMPONENTS[@]}"; do for c in "${COMPONENTS[@]}"; do
component_available "$c" && ! is_action "$c" || continue 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 [[ -n "${PARENT[$c]:-}" ]]; then
[[ "${CURRENT[${PARENT[$c]}]}" == 1 ]] || continue
if [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " └ ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " └ off ${LABEL[$c]}"; fi
elif [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " off ${LABEL[$c]}"; fi
done done
if [[ ${#FAILED[@]} -gt 0 ]]; then if [[ ${#FAILED[@]} -gt 0 ]]; then
warn "These had problems (see above): ${FAILED[*]}" warn "These had problems (see above): ${FAILED[*]}"
@@ -130,21 +162,9 @@ while true; do
done done
echo echo
# A staged BIOS update is written during the restart. if restart_needed; then
if [[ -n "${BIOS_NEEDS_RESTART:-}" ]]; then # Only reached when input ended (scripted runs): never restart then.
warn "The BIOS update is written during the next restart. Keep the power on and" info "Not restarted; the changes take effect after a restart."
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 else
info "Bye." info "Bye."
fi fi