From b91fa4bc838ffa16451d5c6a762cf3a7e6a10492 Mon Sep 17 00:00:00 2001 From: Rick Peters Date: Wed, 23 Sep 2026 15:16:46 +0200 Subject: [PATCH] fix: Replace the question flow with a menu that turns components on and off - Menu detects what is on and applies only the changes: SteamOS conversion, SteamOS theme, Steam Deck/Machine icons, single user mode, and Steam Machine support (only on Fremont hardware) - Everything is reversible: KDE settings go through an undo journal (lib/state.sh), system files are restored from their backups - Steam Machine support: LED driver, udev rule giving the user the LED files, steamos-manager for Steam's hardware settings; OpenRGB dropped - Edit Plasma files only while plasmashell is stopped so it can't overwrite them - README and AGENTS.md describe the menu, the component model and the test environment --- AGENTS.md | 60 ++--- README.md | 257 +++++++++++---------- lib/common.sh | 22 ++ lib/desktop-shortcut.sh | 7 + lib/login-manager.sh | 97 +++++--- lib/menu.sh | 118 ++++++++++ lib/packages.sh | 9 +- lib/single-user.sh | 68 ++++++ lib/state.sh | 79 +++++++ lib/steam-desktop.sh | 108 +++------ lib/{led-driver.sh => steam-machine.sh} | 62 +++-- lib/vapor-theme.sh | 293 ++++++++++-------------- setup-gamescope-boot.sh | 160 +++++-------- 13 files changed, 773 insertions(+), 567 deletions(-) create mode 100644 lib/menu.sh create mode 100644 lib/single-user.sh create mode 100644 lib/state.sh rename lib/{led-driver.sh => steam-machine.sh} (62%) diff --git a/AGENTS.md b/AGENTS.md index c209bf4..bbbc351 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,11 +13,24 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine ## Layout - `setup-gamescope-boot.sh` - the only entry point. Pre-flight checks, - sources `lib/*.sh`, then calls the steps in order. No step logic here. + sources `lib/*.sh`, runs the menu and applies the plan. No component + logic here. - `lib/*.sh` - one file per responsibility, each defining functions only - (no top-level side effects). See the table in `README.md`. New - functionality goes in the module it belongs to, or a new module that is - added to the `source` loop in `setup-gamescope-boot.sh`. + (no top-level side effects besides constants). See the table in + `README.md`. +- **Components.** Every menu item `` (listed in `COMPONENTS` and `LABEL` + in `lib/menu.sh`) provides `_status` (return 0 if on, detected from + the system, no state file needed), `_enable` and `_disable`, all + idempotent. `_enable` is also used to re-apply. Optional + `_available` is checked via `component_available` (e.g. `machine` + only on Fremont). Turn-on order is `COMPONENTS` order, turn-off reverse; + `gaming` must stay first. Dependencies live in `toggle_component`. +- **Reversibility.** Every per-user KDE setting a component changes goes + through `kset ` + (`lib/state.sh`), which records the old value once; `_disable` calls + `krevert `. Never call `kwriteconfig6` directly for settings a + component owns. System files are backed up with `backup_file` and restored + on disable. ## Conventions @@ -41,12 +54,11 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine ## Non-obvious behaviour to preserve -- One wizard question in `choose_setup_mode` sets `$SINGLE_USER` and - `$LOGIN_MANAGER`. "Single user, no password" = SteamOS: SDDM plus no lock - screen, user switching or log out (`setup_single_user`; the launcher's - Session dropdown is hidden via kickoff `primaryActions=3` because - restricting `action/logout` also hides Restart/Shut Down). Otherwise the - current login manager stays. Two login-manager paths: +- `apply_changes` sets `$LOGIN_MANAGER` from the menu: `sddm` when single + user mode is wanted, else `plasmalogin`. Toggling single user re-applies + `gaming` so it moves to the other login manager. Single user mode hides + the launcher's Session dropdown via kickoff `primaryActions=3` (restricting + `action/logout` also hides Restart/Shut Down). Two login-manager paths: **sddm** (like SteamOS: `steam-set-session` writes `/etc/sddm.conf.d/zz-steamos-autologin.conf`, which SDDM honours; we add `User=`/`Relogin=` in `10-gamescope-autologin.conf`, and `/etc/sddm.conf` @@ -75,6 +87,9 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine launches). - Live Plasma changes (`qdbus6 ... evaluateScript`, `lookandfeeltool`) only run when `plasmashell` is running; config-file fallbacks cover the rest. +- plasmashell writes its in-memory config back on exit: edit panel/applet/ + wallpaper files only between `stop_plasmashell_for_edit` and + `restart_plasmashell_if_stopped` (`lib/common.sh`). - LED driver: `leds-valve-dkms-git`'s Makefile builds against `uname -r`, so the script builds explicitly for the running kernel and installs headers for every installed kernel first. The module creates @@ -91,21 +106,10 @@ for f in setup-gamescope-boot.sh lib/*.sh; do bash -n "$f"; done shellcheck -S warning setup-gamescope-boot.sh lib/*.sh # if available ``` -Behaviour is best verified in a CachyOS VM (QEMU/KVM, KDE Plasma install, -`plasma-login-manager`) with SSH access, reset from a disk snapshot between -runs. Notes from doing this: - -- Run the script non-interactively by piping answers, e.g. - `printf "\ny\nn\nn\n" | ./setup-gamescope-boot.sh` (user, single user, - theme, reboot). - The prompt order depends on what's already installed. -- Over SSH, export the session environment before running it so the live - Plasma steps work: - `XDG_RUNTIME_DIR=/run/user/$UID WAYLAND_DISPLAY=wayland-0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$UID/bus`. -- `spectacle -b -n -f -o shot.png` takes a screenshot to check the look. - Apps you launch over SSH lack the session's Qt platform theme and look - light; launch them with `systemd-run --user` to judge colors correctly. -- Gamescope usually won't render in a VM (no suitable Vulkan), so test the - session logic with `plasma.desktop` as the autologin session. The - gamescope boot itself and the LED driver can only be verified on real - hardware. +Behaviour is verified in a CachyOS QEMU/KVM test VM. The VM scripts and a +Claude Code skill describing the whole test workflow (snapshots, SSH, running +the wizard with scripted menu input such as `printf '2\n\ny\nn\n'`, the +per-component checks, 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). +Gamescope itself and the real LED bar can only be verified on hardware. diff --git a/README.md b/README.md index 3ffc8fe..d9e48e4 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,22 @@ script fixes that and sets everything up for you. ## What you get -- **Boots into gaming mode** automatically - you never see a login screen, - just like SteamOS. -- **Switch to Desktop** from Steam's power menu works, and so does going - back: use the **Return to Gaming Mode** icon on the desktop, or just log - out. -- **Back to gaming mode after a restart**, like SteamOS. -- **Steam's on-screen keyboard** (Steam + X) also works on the desktop. -- *Optional:* the **SteamOS desktop look** - Valve's own Vapor theme, - wallpaper, dark mode and taskbar. -- *Optional, Steam Machine only:* a driver for the **front LED bar**. +The wizard is a menu: pick what you want, and it turns each part on or off. +Everything you turn off is put back the way it was. + +1. **SteamOS conversion** - boots straight into gaming mode, **Switch to + 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** - Valve's own Vapor look for the desktop: wallpaper, + dark mode and the SteamOS taskbar. +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 + 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 + driver for the front LED bar, and the hardware settings in Steam (fan, + TV control over HDMI-CEC). ## Requirements @@ -37,31 +43,34 @@ cd ./setup-gamescope-boot.sh ``` -Run it as yourself, not as root. It asks for your password once, then asks a -few yes/no questions: +Run it as yourself, not as root. You'll see a menu like this: -- **Single user, no password, like SteamOS?** Recommended for a console. - You never see a login or lock screen, and there's no user switching or - logging out - typing a password with a controller is no fun. This - switches to SDDM, the login manager SteamOS uses. Say no to keep - CachyOS's default login manager and KDE's normal lock screen; gaming mode - still starts automatically. -- whether to install missing packages; -- the LED driver (Steam Machine only) and the SteamOS look. +``` + # Now Want Component + 1 off [x] SteamOS conversion: boot into gaming mode, Steam on the desktop + 2 off [x] Install SteamOS theme: Vapor look, dark mode, SteamOS taskbar + 3 off [x] Install Steam Deck/Machine icons: Deck button icons in gaming mode + 4 off [x] Single user mode: no password, lock screen or log out (SDDM) +``` -At the end it offers to restart. The switch to SDDM takes effect from that -restart. +**Now** shows what's on at the moment, **Want** what you'd like. Type a +number and press Enter to tick or untick it, then press Enter on an empty +line to continue. The wizard shows what it will change, asks your password +once, and at the end offers to restart (needed for changes to how the PC +starts). + +Run it again whenever you like - to change your choices, to turn things off +again, or after a CachyOS update (type `a` in the menu to re-apply +everything that's on). Keep the whole folder: the script needs the files in `lib/` next to it. -It's safe to run again later, for example after a CachyOS update. - ## Using it - **To the desktop:** in gaming mode, open the Steam menu and choose **Power > Switch to Desktop**. - **Back to gaming mode:** double-click **Return to Gaming Mode** on the - desktop (or log out, if you didn't choose single user). + desktop (or log out, if you're not using single user mode). - After a restart you always start in gaming mode. Prefer to decide yourself where your PC starts? Run one of these in Konsole: @@ -86,42 +95,53 @@ That takes you back to the desktop. freshly installed driver often needs a reboot. Still dark? See [Front LED bar](#front-led-bar-steam-machine) under technical details. -**Undo the SteamOS look:** pick another theme under **System Settings > -Colors & Themes > Global Theme**. - -To remove everything the script set up, see -[Removing everything](#removing-everything). +**Undo something:** run the wizard again and untick it. --- ## Technical details -### Single user: SDDM, no locking +### How turning things on and off works -The wizard's first question. Answering yes switches to **SDDM** and turns -off everything that asks for a password or another user, per user and -without touching system files: `action/lock_screen`, `switch_user` and +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.) Answering no restores KDE's defaults and keeps -the current login manager. +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` (disabling the current display manager, active -from the next boot), writes `User=`, `Session=` and `Relogin=true` to +**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. Switching an existing -plasma-login-manager setup to SDDM removes the sync bridge. +`steamos-session-select gamescope`, which logs out, and `Relogin=true` logs +straight back in to gamescope. -Keeping **plasma-login-manager** (CachyOS's default since March 2026) needs -the workarounds below. +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) @@ -152,34 +172,35 @@ The script sets `Session=`, `User=` and `Relogin=true` in the base config, and installs a small systemd path watcher that copies whatever CachyOS's tools write to the conf.d fragment into the base config. -### What the script changes +### What each component changes -In order: +**SteamOS conversion** (`lib/login-manager.sh`, `lib/steam-desktop.sh`, +`lib/desktop-shortcut.sh`): -1. Checks that it runs as the user that should autologin, and asks whether - to switch to SDDM (see above). -2. Installs missing packages: `gamescope-session-cachyos`, `steam`, - `mangohud`, `xterm`, `ttf-liberation`, `wqy-zenhei`, `plasma-keyboard`. -3. *SDDM:* 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`, and removes stray - `zzz-steamos-autologin*.conf` files from manual troubleshooting (never - `zz-steamos-autologin.conf`, which CachyOS's tools own). -4. *plasma-login-manager only:* installs `/usr/local/bin/sync-steamos-session.sh` plus - `sync-steamos-session.path`/`.service`, which keep the base config in sync - with session switches. -5. Sets up Steam for the desktop: `STEAM_GAMEPADUI_ARGS="-gamepadui -steamos3"` - (gamepad UI with Steam Deck glyphs), `KWIN_IM_SHOW_ALWAYS=1` for the - virtual keyboard, and a `steam-desktop-autostart` systemd user service - that starts Steam silently in Plasma only. -6. *(Steam Machine, optional)* the LED driver - see below. -7. *(Optional)* the SteamOS desktop look - see below. -8. Creates the **Return to Gaming Mode** shortcut. On plasma-login-manager it - also adds a narrow sudoers rule (`/etc/sudoers.d/gamescope-session-switch`) - so it can restart the login manager without a password prompt. +- 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. -Re-running doesn't duplicate any settings. Every system file it modifies is -backed up once, next to the original, with a `.bak-gamescope-wizard` suffix. +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 @@ -191,7 +212,7 @@ SteamOS mirror and installs it into `~/.local/share` - nothing system-wide: - Valve's SteamOS desktop defaults, merged into your own config: Noto Sans fonts, no Xwayland input-permission prompt for Steam Input, the window rule that keeps the Steam keyboard on top and out of the taskbar, no - screen locking, no welcome screen, light file indexing; + welcome screen, light file indexing; - the SteamOS panel: solid (not floating), full width, 44 px high, tray icons scaled to fit, date below the time, and the SteamOS launcher icon; - dark mode for GTK, libadwaita and portal-aware apps (Firefox, Flatpaks). @@ -215,30 +236,47 @@ curl -fsL $m/$r/os/x86_64/$r.db | bsdtar -tf - | grep -o '^steamdeck-kde-presets ### Front LED bar (Steam Machine) -Standard desktop kernels, including CachyOS's, lack Valve's `leds-valve` -driver, so the Steam Machine's front LED bar goes dark or "breathes". On -Fremont hardware (DMI `Valve`/`Fremont`) the script offers to: +The **Steam Machine support** component, only shown on Fremont hardware +(DMI `Valve`/`Fremont`, or `OEM`/`F7F` on early units): -- install an AUR helper (`yay`) if neither `yay` nor `paru` is present; -- install the kernel headers for every installed kernel; -- install `leds-valve-dkms-git` from the AUR, build it for the running - kernel, and load it at every boot (`/etc/modules-load.d/leds-valve.conf`); -- optionally install the experimental `openrgb-git`, which can drive the bar. +- 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 the running kernel, 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; +- 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. -The LEDs appear as `/sys/class/leds/valve-leds*`. Steam itself only drives -the bar (e.g. download progress) in real SteamOS Game Mode. +Turning it off removes all of that again (the AUR helper is kept). -The driver's Makefile builds against the running kernel, so after a kernel -update, boot the new kernel and re-run the script (or -`sudo dkms install leds-valve-dkms/0.1 -k "$(uname -r)"`). +The LEDs appear as `/sys/class/leds/valve-leds*`. The driver's Makefile +builds against the running kernel, so after a kernel update, boot the new +kernel and re-apply (`a` in the menu) or run +`sudo dkms install leds-valve-dkms/0.1 -k "$(uname -r)"`. Troubleshooting: ```bash dkms status -ls /sys/class/leds | grep valve +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** are not part of the wizard. Valve ships the Steam Machine +BIOS as `F7F0108.cab` in its `fremont-hw-support` package for fwupd. To +install it by hand (keep the machine on mains power and don't interrupt it): + +```bash +sudo dmidecode -s bios-version # current version +sudo pacman -S fwupd +curl -LO https://steamdeck-packages.steamos.cloud/archlinux-mirror/holo-3.9/os/x86_64/fremont-hw-support-20260807.1-1-any.pkg.tar.zst +mkdir fhw && tar -I zstd -xf fremont-hw-support-*.pkg.tar.zst -C fhw +sudo fwupdmgr install fhw/usr/share/fwupd/remotes.d/fremont/firmware/F7F0108.cab ``` ### Manual session control @@ -255,48 +293,25 @@ immediately, which can turn into a loop - hence the Ctrl+Alt+F3 escape above. ### Removing everything -```bash -# SDDM: go back to plasma-login-manager -sudo rm /etc/sddm.conf.d/10-gamescope-autologin.conf -sudo systemctl disable sddm && sudo systemctl enable plasmalogin - -# plasma-login-manager: restore the original login config -sudo cp /etc/plasmalogin.conf.bak-gamescope-wizard /etc/plasmalogin.conf - -# session sync watcher -sudo systemctl disable --now sync-steamos-session.path -sudo rm /etc/systemd/system/sync-steamos-session.path -sudo rm /etc/systemd/system/sync-steamos-session.service -sudo rm /usr/local/bin/sync-steamos-session.sh -sudo systemctl daemon-reload - -# shortcut and its sudoers rule -sudo rm /etc/sudoers.d/gamescope-session-switch -rm ~/Desktop/"Return to Gaming Mode.desktop" - -# Steam desktop autostart and environment -systemctl --user disable --now steam-desktop-autostart.service -rm ~/.config/systemd/user/steam-desktop-autostart.service -rm ~/.config/environment.d/99-gamescope-steam-glyphs.conf \ - ~/.config/environment.d/99-kde-virtual-keyboard.conf - -# LED driver (Steam Machine) -sudo rm /etc/modules-load.d/leds-valve.conf -sudo pacman -R leds-valve-dkms-git -``` +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 | |---|---| -| `setup-gamescope-boot.sh` | Entry point: user and sudo checks, step order, summary, reboot | -| `lib/common.sh` | Output helpers, yes/no prompts, backups | +| `setup-gamescope-boot.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` | SDDM or plasmalogin choice and autologin, session sync bridge | -| `lib/steam-desktop.sh` | Steam in the Plasma session: gamepad UI flags, virtual keyboard, autostart | -| `lib/led-driver.sh` | Fremont detection, kernel headers, LED driver, OpenRGB | -| `lib/vapor-theme.sh` | Vapor theme, Valve's SteamOS defaults, panel, dark mode | +| `lib/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: Vapor, Valve's defaults, panel, dark mode | +| `lib/single-user.sh` | Single user mode: no lock screen, user switching or log out | +| `lib/steam-machine.sh` | Steam Machine support: LED driver, LED access, steamos-manager | Notes for contributors and AI coding agents are in [`AGENTS.md`](AGENTS.md). diff --git a/lib/common.sh b/lib/common.sh index 3167a32..781c387 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -45,3 +45,25 @@ backup_file() { info "Backed up $f -> ${f}.bak-gamescope-wizard" fi } + +# plasmashell keeps its config in memory and writes it back on exit, so +# edits to its files (panels, applets, wallpaper) must be made while it is +# stopped. It is restarted through systemd so it keeps the session's +# environment (platform theme etc.); a plain --replace from this shell +# may not, which gives a light-themed desktop. +stop_plasmashell_for_edit() { + PLASMASHELL_WAS_RUNNING=false + if pgrep -u "$USER" -x plasmashell >/dev/null; then + PLASMASHELL_WAS_RUNNING=true + systemctl --user stop plasma-plasmashell.service 2>/dev/null + pkill -u "$USER" -x plasmashell && sleep 2 + fi +} + +restart_plasmashell_if_stopped() { + if [[ "${PLASMASHELL_WAS_RUNNING:-false}" == true ]]; then + rm -rf ~/.cache/plasmashell* ~/.cache/org.kde.dirmodel-qml.kcache + systemctl --user start plasma-plasmashell.service 2>/dev/null || + { setsid plasmashell >/dev/null 2>&1 & } + fi +} diff --git a/lib/desktop-shortcut.sh b/lib/desktop-shortcut.sh index 49c6f60..bd85c86 100644 --- a/lib/desktop-shortcut.sh +++ b/lib/desktop-shortcut.sh @@ -68,3 +68,10 @@ EOF ok "Desktop shortcut created successfully." } + +remove_desktop_shortcut() { + local desktop_dir + desktop_dir="$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME/Desktop")" + rm -f "$desktop_dir/Return to Gaming Mode.desktop" + sudo rm -f /etc/sudoers.d/gamescope-session-switch +} diff --git a/lib/login-manager.sh b/lib/login-manager.sh index b0b8d9b..6918f0a 100644 --- a/lib/login-manager.sh +++ b/lib/login-manager.sh @@ -1,58 +1,86 @@ #!/bin/bash -# plasma-login-manager autologin into gamescope, and the session sync bridge. +# 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. BASE_CONF="/etc/plasmalogin.conf" -choose_setup_mode() { - # Step 1: one question decides both the login manager and locking. - # "Single user, no password" is SteamOS: SDDM (which CachyOS's session - # tools support directly, no workarounds) plus no lock screen, user - # switching or log out. Otherwise keep the current login manager - # (CachyOS's default plasma-login-manager gets the sync bridge below) - # and KDE's normal locking. Sets SINGLE_USER and LOGIN_MANAGER. - local current - current="$(systemctl show -p Id --value display-manager 2>/dev/null | sed 's/\.service$//')" - info "Detected display manager: ${current:-none}" - - echo "SteamOS is a single-user console without passwords: no login screen, no" - echo "lock screen, no user switching or logging out - typing a password with a" - echo "controller is no fun. It uses the SDDM login manager for that." - if ask_yn "Single user, no password, like SteamOS? (switches to SDDM)" y; then - SINGLE_USER=true - LOGIN_MANAGER="sddm" - return - fi - - SINGLE_USER=false - if [[ "$current" == "sddm" ]]; then - LOGIN_MANAGER="sddm" - return - fi - LOGIN_MANAGER="plasmalogin" - if [[ "$current" != "plasmalogin" ]]; then - warn "The workarounds are built for plasma-login-manager, but '${current:-unknown}' is active." - ask_yn "Continue anyway?" n || exit 0 - fi +# The "SteamOS conversion" menu item: gaming mode boot, the Return to +# Gaming Mode shortcut and Steam on the desktop (lib/steam-desktop.sh). +gaming_status() { + # Autologin into gamescope is configured for this user. + case "$(current_display_manager)" in + sddm) [[ -f /etc/sddm.conf.d/10-gamescope-autologin.conf ]] ;; + plasmalogin) + sed -n '/^\[Autologin\]/,/^\[/p' "$BASE_CONF" 2>/dev/null | grep -qx "User=$USER" && + [[ -f /etc/systemd/system/sync-steamos-session.path ]] ;; + *) return 1 ;; + esac } -setup_login_manager() { - # Steps 3-8 for whichever login manager was chosen in step 1. +gaming_enable() { + # LOGIN_MANAGER (sddm with single user, else plasmalogin) is set by the + # menu. Re-running with the other value switches over cleanly. + install_required_packages || return 1 case "$LOGIN_MANAGER" in sddm) - switch_to_sddm || exit 1 + switch_to_sddm || return 1 configure_sddm_autologin remove_session_sync ;; plasmalogin) + remove_sddm_autologin + switch_to_plasmalogin || return 1 configure_autologin install_session_sync ;; esac + create_desktop_shortcut + steam_enable +} + +gaming_disable() { + # Back to a normal desktop boot with CachyOS's default login manager. + # Packages (steam, gamescope-session-cachyos, ...) are kept. + info "Turning off booting into gaming mode..." + remove_sddm_autologin + remove_session_sync + restore_plasmalogin_config + switch_to_plasmalogin + remove_desktop_shortcut + steam_disable + ok "The PC boots to the normal login screen again (from the next boot)." +} + +switch_to_plasmalogin() { + [[ "$(current_display_manager)" == plasmalogin ]] && return 0 + info "Switching the login manager to plasma-login-manager..." + sudo pacman -S --needed --noconfirm plasma-login-manager || { err "Installing plasma-login-manager failed."; return 1; } + local current + current="$(systemctl show -p Id --value display-manager 2>/dev/null)" + [[ -n "$current" && "$current" != plasmalogin.service ]] && sudo systemctl disable "$current" + sudo systemctl enable -f plasmalogin.service || { err "Enabling plasmalogin failed."; return 1; } + ok "plasma-login-manager is the login manager from the next boot on." +} + +remove_sddm_autologin() { + sudo rm -f /etc/sddm.conf.d/10-gamescope-autologin.conf +} + +restore_plasmalogin_config() { + # Put back the original /etc/plasmalogin.conf from the first run's + # backup, or at least drop our [Autologin] section. + if [[ -f "${BASE_CONF}.bak-gamescope-wizard" ]]; then + sudo cp -a "${BASE_CONF}.bak-gamescope-wizard" "$BASE_CONF" + elif [[ -f "$BASE_CONF" ]]; then + sudo awk '/^\[Autologin\]/ { skip=1; next } /^\[/ { skip=0 } !skip { print }' "$BASE_CONF" | + sudo tee "${BASE_CONF}.tmp" > /dev/null && sudo mv "${BASE_CONF}.tmp" "$BASE_CONF" + fi } switch_to_sddm() { # Takes effect at the next boot; the running session is left alone. + [[ "$(current_display_manager)" == sddm ]] && return 0 info "Installing and enabling SDDM..." sudo pacman -S --needed --noconfirm sddm || { err "Installing sddm failed."; return 1; } local current @@ -62,7 +90,6 @@ switch_to_sddm() { fi sudo systemctl enable -f sddm.service || { err "Enabling sddm failed."; return 1; } ok "SDDM is the login manager from the next boot on." - echo } configure_sddm_autologin() { diff --git a/lib/menu.sh b/lib/menu.sh new file mode 100644 index 0000000..35145c7 --- /dev/null +++ b/lib/menu.sh @@ -0,0 +1,118 @@ +#!/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. + +# 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) + +declare -A LABEL=( + [gaming]="SteamOS conversion: boot into gaming mode, Steam on the desktop" + [theme]="Install SteamOS theme: Vapor look, dark mode, SteamOS taskbar" + [glyphs]="Install Steam Deck/Machine icons: Deck button icons in gaming mode" + [single]="Single user mode: no password, lock screen or log out (SDDM)" + [machine]="Steam Machine support: LED bar driver, hardware settings in Steam" +) +declare -A CURRENT WANTED + +component_available() { + [[ "$1" != machine ]] || machine_available +} + +detect_components() { + local c any=false + for c in "${COMPONENTS[@]}"; do + component_available "$c" || continue + if "${c}_status"; then CURRENT[$c]=1; any=true; else CURRENT[$c]=0; fi + WANTED[$c]=${CURRENT[$c]} + done + # First run: preselect the full SteamOS experience. + if [[ "$any" == false ]]; then + for c in "${COMPONENTS[@]}"; do + component_available "$c" && WANTED[$c]=1 + done + fi +} + +toggle_component() { + local c="$1" + 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 +} + +show_menu() { + local i=0 c now want + echo + printf " ${c_bold}%-3s %-6s %-6s %s${c_reset}\n" "#" "Now" "Want" "Component" + MENU_ITEMS=() + for c in "${COMPONENTS[@]}"; do + component_available "$c" || continue + i=$((i + 1)); MENU_ITEMS[$i]=$c + now="off"; [[ "${CURRENT[$c]}" == 1 ]] && now="${c_green}on${c_reset} " + want="[ ]"; [[ "${WANTED[$c]}" == 1 ]] && want="[x]" + printf " %-3s %-6b %-6s %s\n" "$i" "$now " "$want" "${LABEL[$c]}" + done + echo +} + +run_menu() { + # Sets REAPPLY. Returns 1 if the user quit. + REAPPLY=false + local reply + while true; do + show_menu + read -rp "$(echo -e "${c_bold}Type a number to toggle, Enter to continue, a = also re-apply what's on, q = quit:${c_reset} ")" reply + case "$reply" in + "") return 0 ;; + a|A) REAPPLY=true; return 0 ;; + q|Q) return 1 ;; + *) + if [[ "$reply" =~ ^[0-9]+$ && -n "${MENU_ITEMS[$reply]:-}" ]]; then + toggle_component "${MENU_ITEMS[$reply]}" + else + warn "Unknown choice: $reply" + fi + ;; + esac + done +} + +plan_changes() { + # Fills TO_DISABLE (reverse order) and TO_ENABLE. Switching single user + # on or off moves gaming mode to the other login manager, so gaming + # mode is re-applied then too. + TO_DISABLE=(); TO_ENABLE=() + local c i + for (( i = ${#COMPONENTS[@]} - 1; i >= 0; i-- )); do + c=${COMPONENTS[$i]} + component_available "$c" || continue + [[ "${CURRENT[$c]}" == 1 && "${WANTED[$c]}" == 0 ]] && TO_DISABLE+=("$c") + done + for c in "${COMPONENTS[@]}"; do + component_available "$c" || continue + [[ "${WANTED[$c]}" == 1 ]] || continue + if [[ "${CURRENT[$c]}" == 0 || "$REAPPLY" == true ]] || + [[ "$c" == gaming && "${CURRENT[single]}" != "${WANTED[single]}" ]]; then + TO_ENABLE+=("$c") + fi + done +} + +apply_changes() { + local c failed=() + LOGIN_MANAGER=plasmalogin + [[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm + + for c in "${TO_DISABLE[@]}"; do + echo; echo -e "${c_bold}Turning off: ${LABEL[$c]}${c_reset}" + "${c}_disable" || failed+=("$c") + done + for c in "${TO_ENABLE[@]}"; do + echo; echo -e "${c_bold}Turning on: ${LABEL[$c]}${c_reset}" + "${c}_enable" || failed+=("$c") + done + FAILED=("${failed[@]}") +} diff --git a/lib/packages.sh b/lib/packages.sh index 85a60c5..5e45478 100644 --- a/lib/packages.sh +++ b/lib/packages.sh @@ -60,13 +60,8 @@ install_required_packages() { mapfile -t MISSING < <(pacman -T gamescope-session-cachyos steam mangohud xterm ttf-liberation wqy-zenhei plasma-keyboard) if [[ ${#MISSING[@]} -gt 0 ]]; then - info "Missing packages: ${MISSING[*]}" - if ask_yn "Install them now?"; then - sudo pacman -S --needed --noconfirm "${MISSING[@]}" || { err "Package installation failed."; exit 1; } - else - err "Cannot continue without these packages." - exit 1 - fi + info "Installing missing packages: ${MISSING[*]}" + sudo pacman -S --needed --noconfirm "${MISSING[@]}" || { err "Package installation failed."; return 1; } else ok "All required packages already installed." fi diff --git a/lib/single-user.sh b/lib/single-user.sh new file mode 100644 index 0000000..6a2237e --- /dev/null +++ b/lib/single-user.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Single user, no password, like SteamOS: no lock screen, user switching or +# 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. + +single_status() { + [[ "$(kreadconfig6 --file kdeglobals --group "KDE Action Restrictions" --key action/lock_screen)" == false ]] +} + +single_enable() { + info "Turning off the lock screen, user switching and logging out..." + stop_plasmashell_for_edit + + # Hides Lock / Switch User in menus. + local action applet + for action in lock_screen switch_user start_new_session; do + kset single kdeglobals "KDE Action Restrictions" "action/$action" false + done + # Never lock on idle or resume. + kset single kscreenlockerrc Daemon Autolock false + kset single kscreenlockerrc Daemon LockOnResume false + kset single kscreenlockerrc Daemon Timeout 0 + # The restrictions don't cover the shortcuts: unbind Meta+L and the + # Ctrl+Alt+Del logout screen (format: current,default,description). + kset single kglobalshortcutsrc ksmserver "Lock Session" $'none,Screensaver\tMeta+L,Lock Session' + kset single kglobalshortcutsrc ksmserver "Log Out" 'none,Ctrl+Alt+Del,Show Logout Screen' + # Launcher: only Sleep / Restart / Shut Down, no Session dropdown (where + # Log Out lives). Restricting action/logout instead would also hide + # Restart and Shut Down. + for applet in $(plasma_applets org.kde.plasma.kickoff); do + local grp="Containments|${applet%%:*}|Applets|${applet#*:}|Configuration|General" + kset single plasma-org.kde.plasma.desktop-appletsrc "$grp" primaryActions 3 + kset single plasma-org.kde.plasma.desktop-appletsrc "$grp" systemFavorites 'suspend,reboot,shutdown' + done + + restart_plasmashell_if_stopped + ok "Single user: no lock screen, user switching or log out." +} + +single_disable() { + info "Restoring the lock screen, user switching and logging out..." + stop_plasmashell_for_edit + if has_journal single; then + krevert single + else + # Set up by a version of this script without the undo journal: + # fall back to KDE's defaults for everything it changes. + local action applet key + for action in lock_screen switch_user start_new_session; do + kwriteconfig6 --file kdeglobals --group "KDE Action Restrictions" --key "action/$action" --delete + done + for key in Autolock LockOnResume Timeout; do + kwriteconfig6 --file kscreenlockerrc --group Daemon --key "$key" --delete + done + kwriteconfig6 --file kglobalshortcutsrc --group ksmserver --key "Lock Session" $'Screensaver\tMeta+L,Screensaver\tMeta+L,Lock Session' + kwriteconfig6 --file kglobalshortcutsrc --group ksmserver --key "Log Out" 'Ctrl+Alt+Del,Ctrl+Alt+Del,Show Logout Screen' + for applet in $(plasma_applets org.kde.plasma.kickoff); do + kwriteconfig6 --file plasma-org.kde.plasma.desktop-appletsrc --group Containments --group "${applet%%:*}" \ + --group Applets --group "${applet#*:}" --group Configuration --group General --key primaryActions --delete + kwriteconfig6 --file plasma-org.kde.plasma.desktop-appletsrc --group Containments --group "${applet%%:*}" \ + --group Applets --group "${applet#*:}" --group Configuration --group General --key systemFavorites --delete + done + fi + restart_plasmashell_if_stopped + ok "KDE's normal lock screen and user switching are back." +} diff --git a/lib/state.sh b/lib/state.sh new file mode 100644 index 0000000..deaa4a4 --- /dev/null +++ b/lib/state.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# Undo journal for per-user KDE settings, so every component can be turned +# 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. + +STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/cachyos-gamescope-boot" +UNSET=$'\x01' + +kset() { + # kset + # kwriteconfig6, recording the old value in the component's journal. + # "--delete" removes the key. + local component="$1" file="$2" groups="$3" key="$4" value="$5" + local journal="$STATE_DIR/$component.journal" old id + local -a gargs=() g + local groups_item + IFS='|' read -ra g <<< "$groups" + for groups_item in "${g[@]}"; do gargs+=(--group "$groups_item"); done + + mkdir -p "$STATE_DIR" + id="$(printf '%s\t%s\t%s' "$file" "$groups" "$key")" + if ! grep -qxF -- "$id" <(cut -f1-3 "$journal" 2>/dev/null); then + old="$(kreadconfig6 --file "$file" "${gargs[@]}" --key "$key" --default "$UNSET")" + printf '%s\t%s\n' "$id" "$(printf '%s' "$old" | base64 -w0)" >> "$journal" + fi + + if [[ "$value" == "--delete" ]]; then + kwriteconfig6 --file "$file" "${gargs[@]}" --key "$key" --delete + else + kwriteconfig6 --file "$file" "${gargs[@]}" --key "$key" "$value" + fi +} + +krevert() { + # krevert : restore every key the component changed, newest + # first, then forget the journal. + local component="$1" + local journal="$STATE_DIR/$component.journal" + local file groups key old groups_item + local -a g + [[ -f "$journal" ]] || return 0 + while IFS=$'\t' read -r file groups key old; do + local -a gargs=() + IFS='|' read -ra g <<< "$groups" + for groups_item in "${g[@]}"; do gargs+=(--group "$groups_item"); done + old="$(printf '%s' "$old" | base64 -d)" + if [[ "$old" == "$UNSET" ]]; then + kwriteconfig6 --file "$file" "${gargs[@]}" --key "$key" --delete + else + kwriteconfig6 --file "$file" "${gargs[@]}" --key "$key" "$old" + fi + done < <(tac "$journal") + rm -f "$journal" +} + +has_journal() { + [[ -s "$STATE_DIR/$1.journal" ]] +} + +state_set() { + # state_set : remember a value for later. + mkdir -p "$STATE_DIR" + kwriteconfig6 --file "$STATE_DIR/$1.state" --group State --key "$2" "$3" +} + +state_get() { + # state_get [default] + kreadconfig6 --file "$STATE_DIR/$1.state" --group State --key "$2" --default "${3:-}" +} + +state_clear() { + rm -f "$STATE_DIR/$1.state" +} + +current_display_manager() { + systemctl show -p Id --value display-manager 2>/dev/null | sed 's/\.service$//' +} diff --git a/lib/steam-desktop.sh b/lib/steam-desktop.sh index 7b81683..a032768 100644 --- a/lib/steam-desktop.sh +++ b/lib/steam-desktop.sh @@ -1,24 +1,14 @@ #!/bin/bash # Steam in the Plasma desktop session: gamepad UI args, virtual keyboard, -# autostart, and no screen locking (like SteamOS). +# autostart. Part of the SteamOS conversion (gaming_enable/gaming_disable). # Sourced by setup-gamescope-boot.sh; not meant to be run on its own. -setup_steam_desktop() { - # Step 8b: always runs. +steam_enable() { info "Applying non-optional SteamOS environment & keyboard fixes..." - local steam_env_dir gs_env systemd_user_dir + local steam_env_dir systemd_user_dir steam_env_dir="$HOME/.config/environment.d" mkdir -p "$steam_env_dir" - # 1. Force Gamescope Steam to boot into Deck mode and load Deck glyphs - echo "STEAM_GAMEPADUI_ARGS=\"-gamepadui -steamos3\"" > "$steam_env_dir/99-gamescope-steam-glyphs.conf" - gs_env="$HOME/.config/gamescope-session/environment" - if [[ -d "$HOME/.config/gamescope-session" ]]; then - touch "$gs_env" - sed -i '/^STEAM_GAMEPADUI_ARGS=/d' "$gs_env" - echo "STEAM_GAMEPADUI_ARGS=\"-gamepadui -steamos3\"" >> "$gs_env" - fi - # 2. Prevent KDE Wayland from blocking the virtual keyboard overlay echo "KWIN_IM_SHOW_ALWAYS=1" > "$steam_env_dir/99-kde-virtual-keyboard.conf" @@ -54,70 +44,36 @@ EOF ok "Steam UI parameters and keyboard autostart deployed successfully." } -setup_single_user() { - # With SINGLE_USER (chosen in step 1), like SteamOS: one user and no - # password prompts, since typing a password with a controller is - # miserable. Hides Lock / Switch User / Log Out - # everywhere (launcher, Meta+L, Ctrl+Alt+Del) and never locks on idle or - # resume; "Return to Gaming Mode" is the way out of the desktop. Declining - # restores KDE's defaults. - local action allow=true - [[ "$SINGLE_USER" == true ]] && allow=false - for action in lock_screen switch_user start_new_session; do - kwriteconfig6 --file kdeglobals --group "KDE Action Restrictions" --key "action/$action" "$allow" - done - if [[ "$allow" == false ]]; then - kwriteconfig6 --file kscreenlockerrc --group Daemon --key Autolock false - kwriteconfig6 --file kscreenlockerrc --group Daemon --key LockOnResume false - kwriteconfig6 --file kscreenlockerrc --group Daemon --key Timeout 0 - # The action restriction hides Lock in menus but not the Meta+L - # shortcut; unbind it (format: current,default,description). - kwriteconfig6 --file kglobalshortcutsrc --group ksmserver --key "Lock Session" \ - $'none,Screensaver\tMeta+L,Lock Session' - kwriteconfig6 --file kglobalshortcutsrc --group ksmserver --key "Log Out" \ - 'none,Ctrl+Alt+Del,Show Logout Screen' - set_launcher_leave_buttons single - ok "Screen locking, user switching and logging out disabled." - else - kwriteconfig6 --file kscreenlockerrc --group Daemon --key Autolock --delete - kwriteconfig6 --file kscreenlockerrc --group Daemon --key LockOnResume --delete - kwriteconfig6 --file kscreenlockerrc --group Daemon --key Timeout --delete - kwriteconfig6 --file kglobalshortcutsrc --group ksmserver --key "Lock Session" \ - $'Screensaver\tMeta+L,Screensaver\tMeta+L,Lock Session' - kwriteconfig6 --file kglobalshortcutsrc --group ksmserver --key "Log Out" \ - 'Ctrl+Alt+Del,Ctrl+Alt+Del,Show Logout Screen' - set_launcher_leave_buttons default - ok "Keeping KDE's normal screen locking and user switching." - fi +steam_disable() { + info "Removing Steam desktop autostart..." + systemctl --user disable --now steam-desktop-autostart.service 2>/dev/null + rm -f "$HOME/.config/systemd/user/steam-desktop-autostart.service" \ + "$HOME/.config/environment.d/99-kde-virtual-keyboard.conf" + systemctl --user daemon-reload + ok "Steam desktop settings removed (takes full effect at next login)." } -set_launcher_leave_buttons() { - # "single": only Sleep / Restart / Shut Down buttons in the launcher, no - # Session dropdown (that's where Log Out lives). Restricting the - # "logout" action instead would also hide Restart and Shut Down. - # "default": Plasma's own buttons. - local mode="$1" applet - for applet in $(plasma_applets org.kde.plasma.kickoff); do - local -a grp=(--file plasma-org.kde.plasma.desktop-appletsrc --group Containments - --group "${applet%%:*}" --group Applets --group "${applet#*:}" - --group Configuration --group General) - if [[ "$mode" == single ]]; then - kwriteconfig6 "${grp[@]}" --key primaryActions 3 - kwriteconfig6 "${grp[@]}" --key systemFavorites 'suspend,reboot,shutdown' - else - kwriteconfig6 "${grp[@]}" --key primaryActions --delete - kwriteconfig6 "${grp[@]}" --key systemFavorites --delete - fi - done - if pgrep -u "$USER" -x plasmashell >/dev/null; then - local js - if [[ "$mode" == single ]]; then - js="w.writeConfig('primaryActions', 3); w.writeConfig('systemFavorites', 'suspend,reboot,shutdown');" - else - js="w.writeConfig('primaryActions', 0); w.writeConfig('systemFavorites', 'suspend,hibernate,reboot,shutdown');" - fi - qdbus6 org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript \ - "panels().forEach(function (p) { p.widgets('org.kde.plasma.kickoff').forEach(function (w) { w.currentConfigGroup = ['General']; $js w.reloadConfig(); }); })" \ - >/dev/null 2>&1 || true +# "Steam Deck/Machine icons" menu item: Steam's gamepad UI in SteamOS 3 +# mode, which shows Steam Deck button glyphs in gaming mode. +glyphs_status() { + [[ -f "$HOME/.config/environment.d/99-gamescope-steam-glyphs.conf" ]] +} + +glyphs_enable() { + local gs_env="$HOME/.config/gamescope-session/environment" + mkdir -p "$HOME/.config/environment.d" + echo 'STEAM_GAMEPADUI_ARGS="-gamepadui -steamos3"' > "$HOME/.config/environment.d/99-gamescope-steam-glyphs.conf" + if [[ -d "$HOME/.config/gamescope-session" ]]; then + touch "$gs_env" + sed -i '/^STEAM_GAMEPADUI_ARGS=/d' "$gs_env" + echo 'STEAM_GAMEPADUI_ARGS="-gamepadui -steamos3"' >> "$gs_env" fi + ok "Steam Deck/Machine icons on (from the next gaming mode start)." +} + +glyphs_disable() { + rm -f "$HOME/.config/environment.d/99-gamescope-steam-glyphs.conf" + [[ -f "$HOME/.config/gamescope-session/environment" ]] && + sed -i '/^STEAM_GAMEPADUI_ARGS=/d' "$HOME/.config/gamescope-session/environment" + ok "Steam's default button icons are back (from the next gaming mode start)." } diff --git a/lib/led-driver.sh b/lib/steam-machine.sh similarity index 62% rename from lib/led-driver.sh rename to lib/steam-machine.sh index 37271d6..389209e 100644 --- a/lib/led-driver.sh +++ b/lib/steam-machine.sh @@ -1,5 +1,6 @@ #!/bin/bash -# Valve Fremont (Steam Machine) front LED bar driver. +# "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. detect_valve_fremont() { @@ -92,22 +93,49 @@ install_valve_led_driver() { warn " ls /sys/class/leds/ | grep valve ; sudo dmesg | grep -i valve" fi - echo - info "Steam only pushes download progress to the LED bar in real SteamOS Game Mode." - if ask_yn "Also install the experimental OpenRGB build (openrgb-git), which can drive the LED bar?" n; then - "$aur_helper" -S "${flags[@]}" openrgb-git && - ok "openrgb-git installed. Launch OpenRGB to detect and configure the front panel." - fi } -setup_led_driver() { - # Step 8c: only on Fremont hardware, and only if the user wants it. - if detect_valve_fremont; then - info "Detected Valve Fremont hardware (Steam Machine)." - if ask_yn "Set up the front-panel LED bar driver (leds-valve) so it isn't stuck dark/breathing?" y; then - ensure_aur_helper || warn "Couldn't set up an AUR helper automatically. You can install one yourself later and run: yay -S leds-valve-dkms-git" - install_valve_led_driver || warn "LED driver setup ran into a problem - see errors above. Your gamescope boot setup above is unaffected." - fi - echo - fi +LED_UDEV_RULE="/etc/udev/rules.d/70-valve-leds-user.rules" + +machine_available() { detect_valve_fremont; } + +machine_status() { + pacman -Qi leds-valve-dkms-git >/dev/null 2>&1 && pacman -Qi steamos-manager >/dev/null 2>&1 +} + +machine_enable() { + ensure_aur_helper || { warn "Couldn't set up an AUR helper automatically. Install yay or paru, then run the wizard again."; return 1; } + install_valve_led_driver || { warn "LED driver setup ran into a problem - see errors above."; return 1; } + + # The LED files are root-only. Steam runs as the user; let it write them + # in case it drives the bar directly (Valve's own privileged-write helper + # doesn't cover the Steam Machine's LED bar). + info "Letting $TARGET_USER control the LED bar..." + sudo tee "$LED_UDEV_RULE" > /dev/null << EOF +# Let the console user (and Steam) control the Steam Machine's front LED bar. +SUBSYSTEM=="leds", KERNEL=="valve-leds*", RUN+="/usr/bin/find /sys%p -maxdepth 1 -type f -exec /usr/bin/chown $TARGET_USER {} +" +EOF + sudo udevadm control --reload + sudo udevadm trigger --subsystem-match=leds --action=add + + # steamos-manager is what Steam in gaming mode talks to for hardware + # settings (fan, HDMI-CEC TV control, performance); it knows the Steam + # Machine from its DMI data. + info "Installing steamos-manager (Steam Machine hardware settings in Steam)..." + sudo pacman -S --needed --noconfirm steamos-manager || { err "Installing steamos-manager failed."; return 1; } + sudo systemctl enable --now steamos-manager.service + systemctl --user enable steamos-manager.service 2>/dev/null + ok "Steam Machine support on." +} + +machine_disable() { + info "Removing Steam Machine support..." + systemctl --user disable --now steamos-manager.service 2>/dev/null + sudo systemctl disable --now steamos-manager.service 2>/dev/null + sudo pacman -Rns --noconfirm steamos-manager 2>/dev/null + sudo rm -f "$LED_UDEV_RULE" /etc/modules-load.d/leds-valve.conf + sudo udevadm control --reload + sudo modprobe -r leds-valve 2>/dev/null + sudo pacman -Rns --noconfirm leds-valve-dkms-git 2>/dev/null + ok "Steam Machine support removed (the AUR helper, if installed, is kept)." } diff --git a/lib/vapor-theme.sh b/lib/vapor-theme.sh index dad7ea1..4c59874 100644 --- a/lib/vapor-theme.sh +++ b/lib/vapor-theme.sh @@ -4,22 +4,22 @@ merge_kde_config() { # Merge a KDE-style ini file (e.g. one of Valve's /etc/xdg defaults) into - # the user's own config with kwriteconfig6, so SteamOS defaults win over + # the user's own config (journaled, see lib/state.sh), so SteamOS defaults win over # the distro's without touching package-owned files in /etc/xdg. # Nested groups ("[A][B]") are supported; immutable "[$i]" groups and # comments are skipped. local src="$1" target="$2" line key value - local -a groups=() + local groups="" local skip=0 while IFS= read -r line || [[ -n "$line" ]]; do line="${line%$'\r'}" [[ -z "$line" || "$line" == \#* ]] && continue if [[ "$line" == \[* ]]; then - skip=0; groups=() + skip=0; groups="" [[ "$line" == *'[$'* ]] && { skip=1; continue; } local rest="$line" while [[ "$rest" =~ ^\[([^]]*)\](.*)$ ]]; do - groups+=("--group" "${BASH_REMATCH[1]}") + groups+="${groups:+|}${BASH_REMATCH[1]}" rest="${BASH_REMATCH[2]}" done continue @@ -27,7 +27,7 @@ merge_kde_config() { (( skip )) && continue [[ "$line" == *=* ]] || continue key="${line%%=*}"; value="${line#*=}" - kwriteconfig6 --file "$target" "${groups[@]}" --key "$key" "$value" + kset theme "$target" "$groups" "$key" "$value" done < "$src" } @@ -113,15 +113,19 @@ install_vapor_theme() { [[ -f "${tmp_dir}/etc/xdg/$f" ]] && merge_kde_config "${tmp_dir}/etc/xdg/$f" "$f" done [[ -f "${tmp_dir}/etc/xdg/kded5rc" ]] && merge_kde_config "${tmp_dir}/etc/xdg/kded5rc" kded6rc - kwriteconfig6 --file konsolerc --group "Desktop Entry" --key DefaultProfile Vapor.profile + kset theme konsolerc "Desktop Entry" DefaultProfile Vapor.profile mkdir -p ~/.config/gtk-3.0 ~/.config/gtk-4.0 - kwriteconfig6 --file ~/.config/gtk-3.0/settings.ini --group Settings --key gtk-theme-name Vapor - kwriteconfig6 --file ~/.config/gtk-4.0/settings.ini --group Settings --key gtk-theme-name Vapor + kset theme ~/.config/gtk-3.0/settings.ini Settings gtk-theme-name Vapor + kset theme ~/.config/gtk-4.0/settings.ini Settings gtk-theme-name Vapor # Vapor is a dark theme: tell GTK, libadwaita and portal-aware apps # (Firefox, Steam's web views, Flatpaks) to use their dark variants too. - kwriteconfig6 --file ~/.config/gtk-3.0/settings.ini --group Settings --key gtk-application-prefer-dark-theme true - kwriteconfig6 --file ~/.config/gtk-4.0/settings.ini --group Settings --key gtk-application-prefer-dark-theme true + kset theme ~/.config/gtk-3.0/settings.ini Settings gtk-application-prefer-dark-theme true + kset theme ~/.config/gtk-4.0/settings.ini Settings gtk-application-prefer-dark-theme true if command -v gsettings >/dev/null 2>&1; then + [[ -n "$(state_get theme gsettings_scheme)" ]] || + state_set theme gsettings_scheme "$(gsettings get org.gnome.desktop.interface color-scheme 2>/dev/null)" + [[ -n "$(state_get theme gsettings_gtk)" ]] || + state_set theme gsettings_gtk "$(gsettings get org.gnome.desktop.interface gtk-theme 2>/dev/null)" gsettings set org.gnome.desktop.interface color-scheme prefer-dark 2>/dev/null || true gsettings set org.gnome.desktop.interface gtk-theme Vapor 2>/dev/null || true fi @@ -130,138 +134,52 @@ install_vapor_theme() { info "Cleaning up temporary files..." rm -rf "$tmp_dir" - # Launcher icon + # Remember what to go back to when the theme is turned off again. + [[ -n "$(state_get theme lookandfeel)" ]] || + state_set theme lookandfeel "$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage \ + --default "$(kreadconfig6 --file /etc/xdg/kdeglobals --group KDE --key LookAndFeelPackage --default org.kde.breeze.desktop)")" + [[ -n "$(state_get theme colorscheme)" ]] || + state_set theme colorscheme "$(kreadconfig6 --file kdeglobals --group General --key ColorScheme --default BreezeLight)" + + local appletsrc="plasma-org.kde.plasma.desktop-appletsrc" applet + # Same launcher icon SteamOS sets (Vapor Deck plasmoid setup script). - local icon_name="distributor-logo-steamdeck" - local conf_file="$HOME/.config/plasma-org.kde.plasma.desktop-appletsrc" + for applet in $(plasma_applets org.kde.plasma.kickoff); do + kset theme "$appletsrc" "Containments|${applet%%:*}|Applets|${applet#*:}|Configuration|General" icon distributor-logo-steamdeck + done + # SteamOS scales tray icons to the panel height. Valve's setup script + # targets Plasma 5's separate tray containment; in Plasma 6 the setting + # lives on the systemtray applet itself. + for applet in $(plasma_applets org.kde.plasma.systemtray); do + kset theme "$appletsrc" "Containments|${applet%%:*}|Applets|${applet#*:}|General" scaleIconsToFit true + done + # SteamOS shows the date below the time. + for applet in $(plasma_applets org.kde.plasma.digitalclock); do + kset theme "$appletsrc" "Containments|${applet%%:*}|Applets|${applet#*:}|Configuration|Appearance" dateDisplayFormat BelowTime + done - if ! find ~/.local/share/icons /usr/share/icons -iname "${icon_name}.*" 2>/dev/null | grep -q .; then - warn "Could not find '${icon_name}' installed anywhere under ~/.local/share/icons or /usr/share/icons." - warn "The launcher icon will be set anyway, but it may show as a broken icon until the file is installed." - fi - - if [[ -f "$conf_file" ]]; then - info "Looking for the Application Launcher applet (Kickoff/Kicker) to set the custom icon..." - local matches=() - local current_section="" - local current_containment="" - local current_applet="" - - while IFS= read -r line; do - if [[ "$line" =~ ^\[Containments\]\[([0-9]+)\]\[Applets\]\[([0-9]+)\]$ ]]; then - current_containment="${BASH_REMATCH[1]}" - current_applet="${BASH_REMATCH[2]}" - current_section="applet_root" - continue - fi - if [[ "$line" =~ ^\[ ]]; then - current_section="" - continue - fi - if [[ "$current_section" == "applet_root" && "$line" =~ ^plugin=(org\.kde\.plasma\.(kickoff|kicker|simplemenu|homerun|application-menu))$ ]]; then - matches+=( "${current_containment}:${current_applet}:${BASH_REMATCH[1]}" ) - current_section="" - fi - done < "$conf_file" - - if [[ ${#matches[@]} -gt 0 ]]; then - for m in "${matches[@]}"; do - IFS=':' read -r containment applet plugin <<< "$m" - info "Setting launcher icon on Containment $containment / Applet $applet ($plugin)..." - kwriteconfig6 \ - --file "$conf_file" \ - --group Containments --group "$containment" \ - --group Applets --group "$applet" \ - --group Configuration --group General \ - --key icon "$icon_name" - ok "Icon set to '$icon_name'." - done - else - warn "No compatible Application Launcher applet found in panel config. Skipping icon assignment." - fi - else - warn "$conf_file not found. Skipping menu icon configuration." - fi - - # SteamOS scales system tray icons to the panel height. Valve's setup - # script targets Plasma 5's separate tray containment; in Plasma 6 the - # setting lives on the systemtray applet itself. - if [[ -f "$conf_file" ]]; then - local tray - for tray in $(awk ' - /^\[Containments\]\[[0-9]+\]\[Applets\]\[[0-9]+\]$/ { split($0, p, /[][]+/); sect = p[3] ":" p[5] } - /^\[/ && !/\]\[Applets\]\[[0-9]+\]$/ { sect = "" } - /^plugin=org\.kde\.plasma\.systemtray$/ && sect != "" { print sect } - ' "$conf_file"); do - kwriteconfig6 --file "$conf_file" --group Containments --group "${tray%%:*}" \ - --group Applets --group "${tray#*:}" --group General \ - --key scaleIconsToFit true + # Wallpaper: Valve ships them as flat JPGs in usr/share/wallpapers. + local wallpaper="$HOME/.local/share/wallpapers/Steam Deck Logo Default.jpg" containment + if [[ -f "$wallpaper" ]]; then + for containment in $(grep -oP '^\[Containments\]\[\K[0-9]+(?=\]$)' ~/.config/"$appletsrc" 2>/dev/null); do + grep -q "^\[Containments\]\[$containment\]\[Wallpaper\]" ~/.config/"$appletsrc" || continue + kset theme "$appletsrc" "Containments|$containment|Wallpaper|org.kde.image|General" Image "file://$wallpaper" done fi - # SteamOS shows the date below the time in the panel clock. - if [[ -f "$conf_file" ]]; then - local clock - for clock in $(awk ' - /^\[Containments\]\[[0-9]+\]\[Applets\]\[[0-9]+\]$/ { split($0, p, /[][]+/); sect = p[3] ":" p[5] } - /^\[/ && !/\]\[Applets\]\[[0-9]+\]$/ { sect = "" } - /^plugin=org\.kde\.plasma\.digitalclock$/ && sect != "" { print sect } - ' "$conf_file"); do - kwriteconfig6 --file "$conf_file" --group Containments --group "${clock%%:*}" \ - --group Applets --group "${clock#*:}" --group Configuration --group Appearance \ - --key dateDisplayFormat BelowTime - done - fi + # Vapor color scheme and Plasma style; the theme's own defaults use + # breeze-dark icons (there is no "Vapor" icon theme). + kset theme kdeglobals KDE LookAndFeelPackage com.valve.vapor.desktop + kset theme kdeglobals Icons Theme breeze-dark + kset theme kdeglobals General ColorScheme Vapor + kset theme plasmarc Theme name Vapor - # Wallpaper - # Valve ships the wallpapers as flat JPGs in usr/share/wallpapers. - local wallpaper_path="$HOME/.local/share/wallpapers/Steam Deck Logo Default.jpg" - - if [[ -f "$wallpaper_path" ]]; then - info "Applying official Steam Deck Vapor wallpaper via D-Bus script..." - local dbus_cmd=" - var allDesktops = desktops(); - for (var i = 0; i < allDesktops.length; i++) { - var d = allDesktops[i]; - d.currentConfigGroup = ['Wallpaper', 'org.kde.image', 'General']; - d.writeConfig('Image', 'file://${wallpaper_path}'); - } - " - if ! pgrep -u "$USER" -x plasmashell >/dev/null; then - : - elif command -v qdbus6 >/dev/null 2>&1; then - qdbus6 org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript "$dbus_cmd" >/dev/null 2>&1 || true - else - qdbus org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript "$dbus_cmd" >/dev/null 2>&1 || true - fi - - if [[ -f "$conf_file" ]]; then - local desktop_sections=$(grep -E '^\[Containments\]\[[0-9]+\]$' "$conf_file" || true) - while read -r section; do - if [[ -n "$section" ]]; then - local clean_sec=$(echo "$section" | tr -d '[]') - kwriteconfig6 --file "$conf_file" --group "$clean_sec" --group Wallpaper --group org.kde.image --group General --key Image "file://${wallpaper_path}" - fi - done <<< "$desktop_sections" - fi - ok "Wallpaper path pushed to session config." - else - warn "Could not find extracted Vapor wallpaper file. Skipping." - fi - - # Activate the Vapor global theme and color scheme, then restart - # plasmashell once so it picks everything up (restarting first races - # with the settings being written). The theme's own defaults use - # breeze-dark icons; there is no "Vapor" icon theme. - kwriteconfig6 --file kdeglobals --group Icons --key Theme "breeze-dark" - kwriteconfig6 --file kdeglobals --group General --key ColorScheme "Vapor" - kwriteconfig6 --file plasmarc --group Theme --key name "Vapor" - - # Same panel change for the next login, in case Plasma isn't running now. + # SteamOS uses a solid, full-width bottom panel at Plasma's stock 44px + # height (CachyOS ships a floating 30px one). local panel for panel in $(grep -oP '^\[PlasmaViews\]\[Panel \K[0-9]+(?=\]$)' ~/.config/plasmashellrc 2>/dev/null); do - kwriteconfig6 --file plasmashellrc --group PlasmaViews --group "Panel $panel" --key floating 0 - kwriteconfig6 --file plasmashellrc --group PlasmaViews --group "Panel $panel" --group Defaults --key thickness 44 + kset theme plasmashellrc "PlasmaViews|Panel $panel" floating 0 + kset theme plasmashellrc "PlasmaViews|Panel $panel|Defaults" thickness 44 done gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor 2>/dev/null || true @@ -269,56 +187,83 @@ install_vapor_theme() { # Live changes need a running Plasma session; otherwise they are picked # up from the config files above at the next Plasma login. - if pgrep -u "$USER" -x plasmashell >/dev/null; then + if [[ "${PLASMASHELL_WAS_RUNNING:-false}" == true ]]; then info "Applying the theme to the running Plasma session..." lookandfeeltool -a com.valve.vapor.desktop >/dev/null 2>&1 || true # lookandfeeltool leaves the distro's color scheme active; apply it explicitly. plasma-apply-colorscheme Vapor >/dev/null 2>&1 || true qdbus6 org.kde.kded6 /modules/gtkconfig org.kde.gtkconfig.setGtkTheme Vapor >/dev/null 2>&1 || true - qdbus6 org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript \ - 'panels().forEach(function (p) { - p.widgets("org.kde.plasma.systemtray").forEach(function (w) { w.currentConfigGroup = ["General"]; w.writeConfig("scaleIconsToFit", true); }); - p.widgets("org.kde.plasma.digitalclock").forEach(function (w) { w.currentConfigGroup = ["Appearance"]; w.writeConfig("dateDisplayFormat", "BelowTime"); }); - })' \ - >/dev/null 2>&1 || true - # SteamOS uses a solid, full-width bottom panel at Plasma's stock - # 44px height (CachyOS ships a floating 30px one). - qdbus6 org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript \ - 'panels().forEach(function (p) { p.floating = false; p.lengthMode = "fill"; p.height = 44; })' \ - >/dev/null 2>&1 || true - sleep 2 # let plasmashell flush the scripted changes before replacing it - rm -rf ~/.cache/plasmashell* ~/.cache/org.kde.dirmodel-qml.kcache qdbus6 org.kde.KWin /KWin org.kde.KWin.reconfigure >/dev/null 2>&1 || true - # Restart through systemd so plasmashell keeps the session's environment - # (platform theme etc.); a plain --replace from this shell may not. - if systemctl --user cat plasma-plasmashell.service >/dev/null 2>&1; then - # Also takes over from a stray instance started by an earlier --replace. - systemctl --user stop plasma-plasmashell.service - pkill -u "$USER" -x plasmashell && sleep 2 - systemctl --user start plasma-plasmashell.service - else - setsid plasmashell --replace >/dev/null 2>&1 & - fi else info "No running Plasma session; the theme applies at your next desktop login." fi - - ok "Vapor theme assets installed. Apply it under System Settings > Appearance" - ok "> Global Theme > Vapor (Steam Deck), next time you're in a Plasma session." + ok "SteamOS desktop look installed." } -setup_vapor_theme() { - # Step 9 (optional): ask, make sure curl/zstd exist, then install. - echo - if ask_yn "Also install Valve's official Vapor (Steam Deck) KDE theme for your desktop session?" n; then - if ! command -v curl >/dev/null 2>&1; then - warn "curl not found, installing it first..." - sudo pacman -S --needed --noconfirm curl - fi - if ! command -v unzstd >/dev/null 2>&1; then - warn "zstd not found, installing it first..." - sudo pacman -S --needed --noconfirm zstd - fi - install_vapor_theme || warn "Vapor theme install ran into a problem - see errors above. Your gamescope boot setup above is unaffected." +theme_status() { + [[ "$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage)" == com.valve.vapor.desktop ]] +} + +theme_enable() { + command -v curl >/dev/null 2>&1 || sudo pacman -S --needed --noconfirm curl + command -v unzstd >/dev/null 2>&1 || sudo pacman -S --needed --noconfirm zstd + # Stop plasmashell first: the panel and wallpaper edits go to files it + # would otherwise overwrite with its in-memory config on exit. + stop_plasmashell_for_edit + install_vapor_theme + local rc=$? + restart_plasmashell_if_stopped + (( rc == 0 )) || warn "Vapor theme install ran into a problem - see errors above." + return $rc +} + +theme_disable() { + info "Restoring the previous desktop look..." + local lookandfeel colorscheme + lookandfeel="$(state_get theme lookandfeel org.kde.breeze.desktop)" + colorscheme="$(state_get theme colorscheme BreezeLight)" + + stop_plasmashell_for_edit + if has_journal theme; then + krevert theme + else + # Installed by a version of this script without the undo journal: + # undo the main settings it made. + kwriteconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage --delete + kwriteconfig6 --file kdeglobals --group General --key ColorScheme --delete + kwriteconfig6 --file kdeglobals --group Icons --key Theme --delete + kwriteconfig6 --file plasmarc --group Theme --key name --delete + local f + for f in ~/.config/gtk-3.0/settings.ini ~/.config/gtk-4.0/settings.ini; do + [[ -f "$f" ]] || continue + kwriteconfig6 --file "$f" --group Settings --key gtk-theme-name --delete + kwriteconfig6 --file "$f" --group Settings --key gtk-application-prefer-dark-theme --delete + done + kwriteconfig6 --file konsolerc --group "Desktop Entry" --key DefaultProfile --delete fi + if command -v gsettings >/dev/null 2>&1; then + local v + v="$(state_get theme gsettings_scheme)" + if [[ -n "$v" ]]; then gsettings set org.gnome.desktop.interface color-scheme "$v" 2>/dev/null + else gsettings reset org.gnome.desktop.interface color-scheme 2>/dev/null; fi + v="$(state_get theme gsettings_gtk)" + if [[ -n "$v" ]]; then gsettings set org.gnome.desktop.interface gtk-theme "$v" 2>/dev/null + else gsettings reset org.gnome.desktop.interface gtk-theme 2>/dev/null; fi + fi + rm -rf ~/.local/share/plasma/look-and-feel/com.valve.vapor.desktop \ + ~/.local/share/plasma/desktoptheme/Vapor \ + ~/.local/share/themes/Vapor \ + ~/.local/share/color-schemes/Vapor.colors ~/.local/share/color-schemes/VGUI.colors \ + ~/.local/share/konsole/Vapor.profile ~/.local/share/konsole/Vapor.colorscheme \ + ~/.local/share/wallpapers/Steam\ Deck\ Logo*.jpg ~/.local/share/wallpapers/Troll.jpg + rm -f ~/.gtkrc-2.0 + kbuildsycoca6 --noincremental 2>/dev/null || true + + if [[ -n "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ]]; then + lookandfeeltool -a "$lookandfeel" >/dev/null 2>&1 || true + plasma-apply-colorscheme "$colorscheme" >/dev/null 2>&1 || true + fi + restart_plasmashell_if_stopped + state_clear theme + ok "Previous desktop look restored." } diff --git a/setup-gamescope-boot.sh b/setup-gamescope-boot.sh index b13e4f6..3e9476f 100755 --- a/setup-gamescope-boot.sh +++ b/setup-gamescope-boot.sh @@ -2,46 +2,21 @@ # # setup-gamescope-boot.sh # -# Wizard to configure a CachyOS (KDE Plasma + plasma-login-manager) install -# to always boot into a Steam Deck-style gamescope session, with the -# ability to switch to Plasma desktop and back, Deck-style, and have it -# reset to gamescope on the next boot/logout. +# Wizard that makes a CachyOS (KDE Plasma) install behave like SteamOS: +# boot into a Steam Deck-style gamescope session, switch to the Plasma +# desktop and back, and reset to gamescope on the next boot/logout. # -# Covers three real bugs found on CachyOS as of Sep 2026: -# 1. steam-set-session never writes User= to plasmalogin's autologin -# config, so plasma-login-manager never actually autologs in. -# 2. steam-set-session fails outright if /etc/plasmalogin.conf.d is -# missing, breaking "Switch to Desktop" from inside gamescope. -# 3. /etc/plasmalogin.conf (the base config) hardcodes Session=plasma -# and takes priority over anything written to -# /etc/plasmalogin.conf.d/*.conf, so session switches never stick -# without a bridge that copies the conf.d value back into the base -# file, plus Relogin=true so ending a session re-triggers autologin -# instead of dropping to the greeter. -# -# Safe to re-run: it is idempotent and backs up files before editing. -# -# Automatically configures a permanent background systemd autostart for Steam -# and Wayland overrides so the Steam Controller virtual keyboard (Steam+X) -# always works in desktop mode. -# -# Optionally also offers to install the official Valve "Vapor" KDE Plasma -# theme (colors, icons, wallpapers, Plasma look-and-feel package) used on -# real SteamOS, pulled directly from Valve's own package mirror, so the -# desktop side matches the gamescope side visually. -# -# On Valve Fremont hardware (DMI sys_vendor=Valve, product_name=Fremont -- -# i.e. the Steam Machine), also offers to install the -# leds-valve DKMS driver from the AUR so the front LED bar is exposed under -# /sys/class/leds instead of sitting dark or "breathing" under a standard -# desktop kernel, plus the option to pull in an experimental OpenRGB build -# that has native support for driving it. +# It opens with a menu that detects which components are on and turns +# them on or off to match what the user picks; turning one off restores +# what was there before (system files from .bak-gamescope-wizard backups, +# KDE settings from the undo journal in lib/state.sh). Components live in +# lib/, see README.md. Safe to re-run. set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -for lib in common packages login-manager steam-desktop led-driver vapor-theme desktop-shortcut; do +for lib in common state packages login-manager single-user steam-desktop steam-machine vapor-theme desktop-shortcut menu; do # shellcheck source=/dev/null source "$SCRIPT_DIR/lib/$lib.sh" done @@ -49,92 +24,59 @@ done require_root_helper echo -e "${c_bold}CachyOS Steam Deck-style Gamescope Boot Wizard${c_reset}" -echo "This sets your machine up to always boot into gamescope (like SteamOS)," -echo "with working Switch-to-Desktop and switch-back, surviving reboots." -echo +echo "Turn the SteamOS-style parts on or off. The menu shows what is on now;" +echo "anything you turn off is put back the way it was." if ! command -v pacman >/dev/null 2>&1; then err "This doesn't look like an Arch/CachyOS system (no pacman found). Aborting." exit 1 fi -# Ask for the sudo password once up front and keep it fresh, instead of -# prompting at random points during the run. +# Everything per-user (autologin user, Steam, theme, shortcut) is for the +# user running the script. +TARGET_USER="$(id -un)" + +detect_components +run_menu || { info "Nothing changed."; exit 0; } +plan_changes + +if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then + ok "Everything is already the way you want it." + exit 0 +fi + +echo +echo -e "${c_bold}This will:${c_reset}" +for c in "${TO_DISABLE[@]}"; do echo " - turn off: ${LABEL[$c]}"; done +for c in "${TO_ENABLE[@]}"; do + if [[ "${CURRENT[$c]}" == 1 ]]; then echo " - re-apply: ${LABEL[$c]}"; else echo " - turn on: ${LABEL[$c]}"; fi +done +ask_yn "Go ahead?" y || { info "Nothing changed."; exit 0; } + +# Ask for the sudo password once and keep it fresh, instead of prompting at +# random points during the run. sudo -n true 2>/dev/null || sudo -v || exit 1 while true; do sudo -n true; sleep 50; kill -0 "$$" 2>/dev/null || exit; done 2>/dev/null & -TARGET_USER="${SUDO_USER:-$USER}" -read -rp "$(echo -e "${c_bold}Which user should autologin into gamescope?${c_reset} [${TARGET_USER}] ")" input_user -TARGET_USER="${input_user:-$TARGET_USER}" +apply_changes -if ! id "$TARGET_USER" >/dev/null 2>&1; then - err "User '$TARGET_USER' does not exist on this system." - exit 1 +echo +detect_components +echo -e "${c_bold}Done. Current state:${c_reset}" +for c in "${COMPONENTS[@]}"; do + component_available "$c" || continue + if [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " off ${LABEL[$c]}"; fi +done +if [[ ${#FAILED[@]} -gt 0 ]]; then + warn "These had problems (see above): ${FAILED[*]}" fi -if [[ "$TARGET_USER" != "$(id -un)" ]]; then - # Environment, systemd user units, theme and shortcut are all written - # into the invoking user's home and user session. - err "Run this script as '$TARGET_USER' itself so per-user settings land in the right home." - exit 1 -fi -ok "Using user: $TARGET_USER" echo -choose_setup_mode -echo - -install_required_packages -echo - -setup_login_manager - -setup_steam_desktop -echo - -setup_single_user -echo - -setup_led_driver - -setup_vapor_theme -echo - -create_desktop_shortcut -echo - -# ---------- 11. Summary + reboot ---------- - -echo -e "${c_bold}Setup complete.${c_reset}" -echo "What this did:" -echo " - Installed gamescope-session-cachyos, steam, mangohud and friends (if missing)" -if [[ "$LOGIN_MANAGER" == "sddm" ]]; then - echo " - Made SDDM the login manager, autologging '$TARGET_USER' into gamescope" - echo " with Relogin=true, like SteamOS (active from the next boot)" -else - echo " - Created /etc/plasmalogin.conf.d (fixes Switch-to-Desktop crash)" - echo " - Set $BASE_CONF to autologin '$TARGET_USER' into gamescope, with Relogin=true" - echo " - Installed a sync bridge + systemd watcher so Steam's Switch-to-Desktop" - echo " (and cachyos-gamescope-autologin.service resetting back to gamescope" - echo " on logout) both actually take effect" - echo " - Added /etc/sudoers.d/gamescope-session-switch so the desktop shortcut works" -fi -echo " - Configured permanent silent Steam autostart so Steam+X works everywhere" -echo " - Injected -steamos3 flag to force original Steam Deck overlay glyphs" -echo " - Optionally installed Valve's Vapor (Steam Deck) KDE theme, if you chose to" -echo " - On Valve Fremont hardware, optionally set up the leds-valve front LED bar driver" -echo -echo "Backups of any files this script modified were saved with a" -echo ".bak-gamescope-wizard suffix next to the original." -echo -echo "To manually flip sessions any time:" -echo " steamos-session-select gamescope # boot straight into gamescope now" -echo " steamos-session-select plasma # boot straight into desktop now" -echo " steamos-session-select persistent # remember last-used session across reboots" -echo " steamos-session-select oneshot # always start in gamescope regardless (default Deck behavior)" -echo - -if ask_yn "Reboot now to test it?" n; then - sudo reboot -else - info "Skipping reboot. Run 'sudo reboot' whenever you're ready to test." +# Login manager changes only take effect after a restart. +if [[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " == *" gaming "* || " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " == *" single "* ]]; then + if ask_yn "Restart now so the changes take effect?" n; then + sudo reboot + else + info "Restart whenever you're ready." + fi fi