Compare commits

..
176 Commits
Author SHA1 Message Date
theupriser 3ec82fb0e8 Merge pull request #80 from theupriser/feature/nvidia-docs
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 25s
docs: NVIDIA as it is now
2026-10-13 20:35:32 +02:00
theupriserandClaude Sonnet 5.5 9d36fcee6b docs: NVIDIA as it is now (README, TECHNICAL, CHANGELOG)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 20:33:51 +02:00
theupriser 5fc815c356 Merge pull request #79 from theupriser/feat/nvidia-sleep-lib32
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 14s
NVIDIA: patched gamescope for the SteamOS conversion, suspend memory, 32-bit libs (2.11.2)
2026-10-13 20:26:23 +02:00
theupriserandClaude Sonnet 5.5 452955ae68 Version 2.12.0; drop the NVIDIA screenshot (NVIDIA PCs get the same menu)
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 20:24:32 +02:00
theupriserandClaude Sonnet 5.5 27263411ba gamescope-nvidia: glob instead of ls | grep (shellcheck SC2010)
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 20:23:04 +02:00
theupriserandClaude Sonnet 5.5 b112082704 NVIDIA setup: preflight (pacman lock, free space), failure marker with notification, reminder in the menu
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 20:16:43 +02:00
theupriserandClaude Sonnet 5.5 89a66f3159 Docs: per-CPU-level gamescope builds
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 20:16:09 +02:00
theupriserandClaude Sonnet 5.5 e738297c7f Smoke test: pass when gamescope ran the program (it segfaults on headless exit)
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 20:11:59 +02:00
theupriserandClaude Sonnet 5.5 f1cdf313ad Patched gamescope: one build per CPU level (znver4/v4/v3/generic), smoke test after install, pin 3.16.31-3
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 20:10:33 +02:00
theupriserandClaude Sonnet 5.5 59e941e8e4 feat: the gaming session on NVIDIA runs on the display's own mode, Steam's UI at its size; pin 3.16.31-2
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 19:59:15 +02:00
theupriserandClaude Sonnet 5.5 d4bb4c4d92 fix: the conversion removes single user mode's desktop autologin, which pinned the Plasma session
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 19:09:28 +02:00
theupriserandClaude Sonnet 5.5 77715f9f73 fix: the skipped sub-option is reported as an app event, and counts as not applied
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 19:06:10 +02:00
theupriserandClaude Sonnet 5.5 9c15d14c4a fix: sub-options are skipped when their parent failed
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 19:05:56 +02:00
theupriserandClaude Sonnet 5.5 4c614a2f38 fix: keep python-hidapi: a placeholder provides python-hid for jupiter-hw-support
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 19:03:23 +02:00
theupriserandClaude Sonnet 5.5 43fa06eb3c feat: the newest patched gamescope release first, the pinned one as the fallback, a newer release shows as an update
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:58:51 +02:00
theupriserandClaude Sonnet 5.5 2e64fde7d5 feat: the patched gamescope comes from the pinned GitHub release (sha256), else the copy on the ISO, else a local build
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:47:29 +02:00
theupriserandClaude Sonnet 5.5 f234c727b1 docs: credit Valve's kernel for the power-off fix and CEC driver, list the code of other projects with its licenses
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:44:43 +02:00
theupriserandClaude Sonnet 5.5 124b424d3d fix: the Steam Machine's CEC driver comes from a carried, checksum-pinned copy when the GitHub download fails
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:44:15 +02:00
theupriserandClaude Sonnet 5.5 06ffc8bc8b fix: build the patched gamescope in the user's cache folder, not /tmp (a tmpfs in the installer's chroot)
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:37:21 +02:00
theupriserandClaude Sonnet 5.5 5b5181f33e feat: Steamify carries the patched gamescope's recipe and patch, no download of a separate repository
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:36:21 +02:00
theupriserandClaude Sonnet 5.5 03f9e87cf2 feat: moving from Gaming on NVIDIA keeps how Steam started: Big Picture boots into gaming mode, otherwise the desktop
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:30:05 +02:00
theupriserandClaude Sonnet 5.5 4325d80f09 feat: a PC that had Gaming on NVIDIA on gets the SteamOS conversion ticked, as an update
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:28:43 +02:00
theupriserandClaude Sonnet 5.5 f3cd053ea0 feat: Gaming on NVIDIA is not offered any more, the SteamOS conversion covers NVIDIA PCs
Sync git.upriser.nl mirror / sync (push) Skipped
The conversion installs the patched gamescope on every NVIDIA PC and sets up the kernel parameters, suspend memory and 32-bit libraries on a supported card; it turns off an existing Gaming on NVIDIA.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:27:13 +02:00
theupriserandClaude Sonnet 5.5 2811ecce1c feat: on a supported NVIDIA card the SteamOS conversion replaces Gaming on NVIDIA
Sync git.upriser.nl mirror / sync (push) Skipped
The conversion also sets up what Gaming on NVIDIA did there (kernel parameters, early modules, suspend memory, 32-bit libraries). Gaming on NVIDIA is only offered on older NVIDIA cards, or while it is on.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:24:54 +02:00
theupriserandClaude Sonnet 5.5 515019770f fix: on NVIDIA the SteamOS conversion is a choice, Gaming on NVIDIA stays preselected; supported cards are GTX 16 / RTX 20 and newer
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:19:21 +02:00
theupriserandClaude Sonnet 5.5 0ba94b23d8 docs: NVIDIA section of TECHNICAL.md follows the patched gamescope
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:18:27 +02:00
theupriserandClaude Sonnet 5.5 127b8cf88b feat: SteamOS conversion on NVIDIA with a patched gamescope (GBM scan-out)
Sync git.upriser.nl mirror / sync (push) Skipped
The conversion is offered on supported NVIDIA cards again; it builds and installs steamify-gamescope-nvidia and sets gamescope_drm_gbm_scanout=1. It and Gaming on NVIDIA exclude each other.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:18:09 +02:00
theupriserandClaude Sonnet 5.5 4d60dd227d chore: keep the gamescope GBM scan-out patch (NVIDIA picture fix) in patches/
Sync git.upriser.nl mirror / sync (push) Skipped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkpbsMw4rQyBsxDG6ebHK2
2026-10-13 18:05:01 +02:00
theupriserandClaude Sonnet 5.5 ce4bc67e8c feat: Gaming on NVIDIA keeps video memory over suspend and installs the 32-bit libraries
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 15:23:21 +02:00
theupriser cab1a9792f Merge pull request #78 from theupriser/release/2.11.1
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 12s
Release 2.11.1: README screenshots for the 2.11 menu
2026-10-02 15:03:56 +02:00
theupriser 708df2f7d8 Merge pull request #77 from theupriser/bugfix/readme-screenshots
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 13s
docs: new README screenshots (2.11 menu), the silent option and the icons in the README
2026-10-02 15:03:27 +02:00
theupriser cef5c8d175 docs: changelog hash for the README screenshots 2026-10-02 15:01:49 +02:00
theupriser d70aeb8ac8 docs: new README screenshots (2.11 menu on a Steam Machine and an NVIDIA PC), the silent option and the icons in the README 2026-10-02 15:01:49 +02:00
theupriser 40d468cf83 chore: bump VERSION to 2.11.1 2026-10-02 14:45:32 +02:00
theupriser 2bd6e98ce0 Merge pull request #76 from theupriser/release/2.11.0
Single-file build / bundle (push) Successful in 15s
Sync git.upriser.nl mirror / sync (push) Skipped
Release 2.11.0: Start Steam silently in desktop mode, Steam Deck icons under the conversion, installer fixes
2026-10-02 14:39:46 +02:00
theupriser 93a26a0d16 Merge pull request #75 from theupriser/chore/tests-to-dev
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 15s
chore: the tests live in steamify-cachyos-dev; changelog hashes for 2.11.0
2026-10-02 14:38:12 +02:00
theupriser f05078d932 docs: changelog hashes for 2.11.0, and the tests move
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 14:37:42 +02:00
theupriser 4d387b2f86 chore: the tests live in steamify-cachyos-dev, remove tests/ 2026-10-02 14:37:02 +02:00
theupriser 19089a1eda Merge pull request #74 from theupriser/feature/silent-steam-start
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 12s
feat: Start Steam silently in desktop mode (opt-in), Steam Deck icons under the conversion, installer fixes
2026-10-02 14:34:56 +02:00
theupriser 2f7b53f64b fix: the Steam Machine serial number step is not fatal in the installer chroot (read-only /sys); the tmpfiles.d file applies at boot
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 12:22:20 +02:00
theupriser bd8f387cf5 fix: STEAMIFY_NO_DOWNLOAD finds the bundle through BASH_SOURCE (a bundle has no SCRIPT_DIR: the install failed with an unbound variable)
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 11:38:25 +02:00
theupriser 45a23aff34 feat: STEAMIFY_NO_DOWNLOAD: the first login runs the ISO's Steamify bundle instead of the newest release
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 11:11:18 +02:00
theupriser f3810a9fdd fix: the SteamOS theme drops the blank Discover pin from the taskbar (also on the first login of an ISO install)
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 10:52:04 +02:00
theupriser 188799e951 fix: the SteamOS conversion no longer lists the silent start in what it changes (it is its own opt-in option)
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 10:43:25 +02:00
theupriser 079e725ba8 feat: "Start Steam silently in desktop mode" is opt-in (it was always on); an existing systemd unit counts as on and is migrated by the update
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 10:16:40 +02:00
theupriser 7869b472be feat: Steam Deck/Machine icons is a sub-option of the SteamOS conversion (it only changes gaming mode)
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 10:15:59 +02:00
theupriser fbdad5a785 feat: the silent option is Steam's own autostart entry (~/.config/autostart/steam.desktop with -silent), so its "Run Steam when my computer starts" follows it; the systemd unit is gone
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 09:28:03 +02:00
theupriser 349492423e feat: "Start Steam silently in desktop mode" is always shown, also on a Steam Machine
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 09:10:26 +02:00
theupriser 613149a807 docs: changelog for the silent start option
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-01 23:04:18 +02:00
theupriser 2e51d1d3f3 feat: "Start Steam silently" sub-option for the SteamOS conversion (silent) and Gaming on NVIDIA (nvsilent) 2026-10-01 23:04:18 +02:00
theupriser e5110ada40 chore: bump VERSION to 2.11.0 2026-10-01 23:02:37 +02:00
theupriser 1074514b83 Merge pull request #73 from theupriser/release/2.10.0
Single-file build / bundle (push) Successful in 14s
Sync git.upriser.nl mirror / sync (push) Skipped
Release 2.10.0
2026-10-01 21:55:23 +02:00
theupriser 2c004322af Merge pull request #72 from theupriser/bugfix/changelog-2.10.0-trim
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 24s
docs: trim the 2.10.0 changelog
2026-10-01 21:54:20 +02:00
theupriser 3915e1a96f docs: trim the 2.10.0 changelog to the user-facing changes 2026-10-01 21:54:13 +02:00
theupriser 098dc3d0d8 chore: remove .no-release-yet, 2.10.0 is ready 2026-10-01 21:54:11 +02:00
theupriser 48352b5601 Merge bugfix/nvidia-shellcheck into release/2.10.0
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 11s
2026-10-01 21:43:21 +02:00
theupriser 05b8f11c19 docs: changelog hashes for 2.10.0 2026-10-01 21:43:21 +02:00
theupriser 8b96e2d8a9 fix: shellcheck of the bundle: nvidia_enable's local args clashed with an array of another module (renamed unit_args) 2026-10-01 21:43:21 +02:00
theupriser f1fb18ec81 docs: changelog for 2.10.0, one entry per commit with its hash 2026-10-01 21:41:02 +02:00
theupriser 1987fa903b Merge feature/extended-controller-support into release/2.10.0 2026-10-01 21:29:42 +02:00
theupriser b7dc931a4f Merge feature/nvidia-gaming-fix into release/2.10.0
# Conflicts:
#	CHANGELOG.md
2026-10-01 21:29:42 +02:00
theupriser 3d20a7edb0 chore: Version 2.10.0 2026-10-01 21:29:34 +02:00
theupriser f1983a9caf docs: README screenshots for the Steam Machine and for NVIDIA PCs, side by side 2026-10-01 21:24:36 +02:00
theupriser 01a77b7dc7 feat: new opt-in options show the new badge without being ticked 2026-10-01 21:11:51 +02:00
theupriser 83248a8478 feat: extended controller support: Xbox wireless dongle (xone) and Xbox Bluetooth (xpadneo) 2026-10-01 21:11:51 +02:00
theupriser 5811f5c8a4 fix: Add as non-Steam game isn't offered on NVIDIA PCs (no gaming mode there)
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-01 21:11:46 +02:00
theupriser 71bb2e7aca chore: Version 2.10.0 2026-10-01 21:05:05 +02:00
theupriser d0e4d8cf79 feat: Big Picture on NVIDIA starts on an empty Plasma session, so restored windows don't cover it 2026-10-01 19:19:51 +02:00
theupriser d1373e28a6 feat: single user mode on NVIDIA PCs: SDDM autologin into the Plasma session, without the conversion 2026-10-01 19:13:46 +02:00
theupriser d6db4f6f2d feat: "Gaming on NVIDIA" replaces the SteamOS conversion on NVIDIA PCs
gamescope's gaming mode shows a corrupted picture on NVIDIA (NVIDIA bug 5240452), also nested in KDE and in a KWin-only
session. With an NVIDIA GPU the conversion (gaming, boot, single, glyphs) is hidden unless already on, and nvidia takes its
place: Steam installed when missing and started at login on the Plasma desktop (user unit), with the sub-option bigpicture
for Big Picture or the normal window. The kernel parameters and early NVIDIA modules (RTX 20 series or newer) are kept as
part of it. tests/nvidia-test.sh covers the component and the menu rules against a fake GPU.
2026-10-01 19:04:58 +02:00
theupriser a5e9015d93 fix: NVIDIA fix handles Limine's += command line, underscore spelling; hardware test sets SCRIPT_DIR 2026-10-01 17:19:23 +02:00
theupriser ae238ff383 chore: .no-release-yet: this NVIDIA branch is not ready to be released
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-01 15:40:02 +02:00
theupriser c1217bcea3 feat: NVIDIA fix only for RTX 20 series or newer, with the VRAM booster's chwd legacy check (vram_nvidia_legacy_id) 2026-10-01 15:20:02 +02:00
theupriser f3d67fbe52 fix: never install an empty NVIDIA hook or script (a failed patch_file piped into install succeeded with no input) 2026-10-01 15:07:16 +02:00
theupriser 84cf0959e4 feat: NVIDIA modules in the initramfs only while every kernel has them, decided again by a pacman hook at every kernel change 2026-10-01 14:42:06 +02:00
theupriser 7caefb7da6 test: nvidia-hardware-test.sh to check, apply and visually verify the NVIDIA fix on a real PC 2026-10-01 13:20:01 +02:00
theupriser 453656a0a2 fix: NVIDIA fix checks the edit and the generated boot entries for every boot loader (limine.conf, systemd-boot entries, grub.cfg) 2026-10-01 13:07:07 +02:00
theupriser 6aa1377e16 fix: NVIDIA fix on Limine edits the existing cmdline line; early modules only when every kernel has them
Found by running it in a VM: the appended += line became literal text on the
kernel command line, and a kernel without the modules made limine-mkinitcpio
skip its boot entry.
2026-10-01 12:48:56 +02:00
theupriser 6bcae8af18 test: NVIDIA fix against a fake RTX 5080, stub modinfo and temp boot loader files 2026-10-01 11:07:32 +02:00
theupriser 3cbeb58211 docs: AGENTS.md: never work on main or a release branch, hardware-specific fixes, git identity 2026-10-01 10:54:49 +02:00
theupriser 83561bb214 feat: NVIDIA fix for gaming mode (kernel parameters and early modules)
Untested on hardware; .no-release-yet keeps it out of the release.
2026-10-01 10:54:27 +02:00
theupriser ca027cb607 chore: Version 2.9.7 2026-10-01 10:54:16 +02:00
theupriser d93f9ea56c Merge pull request #71 from theupriser/release/2.9.6
Single-file build / bundle (push) Successful in 12s
Sync git.upriser.nl mirror / sync (push) Skipped
Release/2.9.6
2026-09-30 13:34:33 +02:00
theupriser 761f921e40 Merge pull request #70 from theupriser/bugfix/iso-steamify-ref
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 13s
fix(ci): a release branch builds its dev ISO with its own Steamify
2026-09-30 12:46:20 +02:00
theupriser 7a54a2fa59 fix(ci): a release branch builds its dev ISO with its own Steamify (steamify_ref), not the newest release 2026-09-30 12:45:14 +02:00
theupriser 9eab3978a9 docs: changelog hashes for 2.9.6
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 11s
2026-09-30 12:33:28 +02:00
theupriser 9602d9d00b Merge pull request #69 from theupriser/bugfix/iso-dispatch-ref
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 14s
fix(ci): main starts a real ISO release, a release branch a dev ISO
2026-09-30 11:41:40 +02:00
theupriser 9a2965d339 fix(ci): the ISO workflow always runs from master; main asks for a release ISO, a release branch for a dev ISO
Sync git.upriser.nl mirror / sync (push) Skipped
2026-09-30 11:32:36 +02:00
theupriser c42d5454bf fix(ci): a version published from main starts a real ISO release, a release branch a dev ISO 2026-09-30 11:31:37 +02:00
theupriser b3bed43854 chore: Version 2.9.6 2026-09-30 11:31:25 +02:00
theupriser 3e79451244 Merge pull request #68 from theupriser/release/2.9.5
Single-file build / bundle (push) Successful in 12s
Sync git.upriser.nl mirror / sync (push) Skipped
chore: Version 2.9.5
2026-09-30 11:27:45 +02:00
theupriser 971e792054 chore: Version 2.9.5
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 12s
2026-09-30 11:26:15 +02:00
theupriser 6557320c81 Merge pull request #67 from theupriser/release/2.9.5
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 13s
Release/2.9.5
2026-09-30 11:21:53 +02:00
theupriser 692e3fcbad Merge pull request #65 from theupriser/feature/git-upriser-sync
Sync git.upriser.nl mirror / sync (push) Skipped
Single-file build / bundle (push) Successful in 11s
feat(ci): sync the git.upriser.nl mirror at once
2026-09-30 11:20:48 +02:00
theupriser 70ecb563fd ci: trigger a mirror sync
Sync git.upriser.nl mirror / sync (push) Skipped
2026-09-30 10:55:58 +02:00
theupriser a7276cfd9a ci: sync the git.upriser.nl mirror on every branch and tag
Sync git.upriser.nl mirror / sync (push) Skipped
2026-09-30 08:54:29 +02:00
theupriser b3549d4eea ci: sync the mirror on release branch pushes too (Gitea's vmtest.yml runs on them) 2026-09-30 08:50:13 +02:00
theupriser a232a55bef feat(ci): sync the git.upriser.nl mirror on every push to main and every tag
New git-upriser-sync.yml calls the mirror-sync API (secrets GIT_UPRISER_URL, GIT_UPRISER_TOKEN; retried; skipped on Gitea). bundle.yml dispatches the renamed iso-1-github-tag.yml in the right repo name; docs follow.
2026-09-30 08:12:06 +02:00
theupriser 397b3cb607 Merge pull request #64 from theupriser/release/2.9.4
Single-file build / bundle (push) Successful in 13s
Release/2.9.4
2026-09-30 07:53:56 +02:00
theupriser bda1d3ebbe fix: Merge docs/iso-repo-rename into release/2.9.4
Single-file build / bundle (push) Successful in 12s
2026-09-30 07:47:55 +02:00
theupriser 3676c64c90 docs: link the renamed steamify-cachyos-live-iso repo
- README and AGENTS.md point at theupriser/steamify-cachyos-live-iso
2026-09-30 07:47:55 +02:00
theupriser d44f29956a Merge pull request #63 from theupriser/release/2.9.4
Single-file build / bundle (push) Successful in 13s
Release 2.9.4
2026-09-29 23:08:59 +02:00
theupriser 2858d13429 docs: changelog hash for 2.9.4 2026-09-29 23:08:20 +02:00
theupriser 48cf3c7c7c Merge pull request #62 from theupriser/feature/docs-ci-and-cec-cache
docs: the CEC driver cache and the CI/ISO release flow
2026-09-29 23:08:12 +02:00
theupriser 619ef1f714 docs: AGENTS.md and TECHNICAL.md: the CEC driver cache, one workflow for GitHub and a Gitea mirror, a release starts the Steamify ISO's release 2026-09-29 23:07:28 +02:00
theupriser b6240539e6 chore: Version 2.9.4 2026-09-29 23:07:08 +02:00
theupriser 7d6df21627 Merge pull request #61 from theupriser/release/2.9.3
Single-file build / bundle (push) Successful in 13s
Release 2.9.3
2026-09-29 22:04:49 +02:00
theupriser 3cad70dcc4 docs: changelog hash for 2.9.3
Single-file build / bundle (push) Successful in 24s
2026-09-29 22:03:09 +02:00
theupriser b8742a5642 Merge pull request #60 from theupriser/feature/iso-release-on-steamify-release
feat(ci): a new Steamify release starts the Steamify ISO's release
2026-09-29 21:57:25 +02:00
theupriser 7f5d29c997 feat(ci): a new Steamify release starts the Steamify ISO's release (iso-release.yml in the ISO repo, feat/steamify; secret ISO_DISPATCH_TOKEN) 2026-09-29 21:56:42 +02:00
theupriser cfe3ce7bd0 chore: Version 2.9.3 2026-09-29 21:55:45 +02:00
theupriser 3e4133c14a Merge pull request #58 from theupriser/release/2.9.2
Single-file build / bundle (push) Successful in 14s
Release/2.9.2
2026-09-29 20:31:12 +02:00
theupriser c4b8db85dc Merge pull request #57 from theupriser/feature/gitea-release
Single-file build / bundle (push) Successful in 13s
feat(ci): a Gitea mirror publishes its own releases
2026-09-29 20:30:00 +02:00
theupriser 350011877b feat(ci): a Gitea mirror publishes its own release per version (never overwrites one, notes from the changelog) 2026-09-29 20:29:19 +02:00
theupriser b1d79d48a6 Merge pull request #55 from theupriser/release/2.9.2
Single-file build / bundle (push) Successful in 20s
chore: Version 2.9.2
2026-09-29 20:25:44 +02:00
theupriser 5b42036786 Merge pull request #56 from theupriser/bugfix/ci-shellcheck-gitea
fix(ci): the build also runs on a Gitea mirror
2026-09-29 20:25:10 +02:00
theupriser 4a64232fad fix(ci): Gitea gets christopherhx/gitea-upload-artifact@v4 (GitHub fails a workflow that mentions upload-artifact@v3) 2026-09-29 20:24:23 +02:00
theupriser 4ec92f4d89 fix(ci): the build also runs on a Gitea mirror: shellcheck through ludeeus/action-shellcheck (brings its own binary), the artifact with upload-artifact@v3 there, releases only on GitHub 2026-09-29 20:23:20 +02:00
theupriser fe4f3f43be chore: Version 2.9.2 2026-09-29 20:23:04 +02:00
theupriser 4bacc3b2e6 Merge pull request #54 from theupriser/release/2.9.1
Release/2.9.1
2026-09-29 19:51:54 +02:00
theupriser cc0ca1c35e Merge pull request #53 from theupriser/bugfix/cec-driver-cache
fix: HDMI-CEC: Valve's CEC driver is downloaded once and cached
2026-09-29 19:26:24 +02:00
theupriser 580cc71b57 docs: changelog hashes 2026-09-29 19:26:16 +02:00
theupriser c87e6c763f docs: changelog for the CEC driver cache 2026-09-29 19:19:05 +02:00
theupriser fc05725a91 fix: HDMI-CEC: an older cached CEC driver is removed when a new pin is downloaded 2026-09-29 19:18:55 +02:00
theupriser 90bc5e4408 fix: HDMI-CEC: Valve's CEC driver is downloaded once and kept in /var/cache/steamify (GitHub rate-limits repeated downloads with HTTP 429, which left CEC off) 2026-09-29 19:16:06 +02:00
theupriser d99ff652dc chore: Version 2.9.1 2026-09-29 19:15:42 +02:00
theupriser 2aa3068b60 Merge pull request #51 from theupriser/release/2.9.0
Release 2.9.0: docs (KDE Plasma only)
2026-09-28 20:42:12 +02:00
theupriser 1a6ddcfc0d Merge pull request #52 from theupriser/feature/changelog-hash-fix
docs: changelog hash fix
2026-09-28 19:48:45 +02:00
theupriser 11668db91c docs: changelog hash for 66ea63c (was the merge commit's) 2026-09-28 19:48:36 +02:00
theupriser 3316c1de28 Merge pull request #50 from theupriser/feature/changelog-hashes
docs: changelog hashes
2026-09-28 19:47:55 +02:00
theupriser d9365dac81 docs: changelog hashes 2026-09-28 19:47:47 +02:00
theupriser 853a3926ee Merge pull request #49 from theupriser/feature/docs-not-supported
docs: other desktops are not supported
2026-09-28 19:47:35 +02:00
theupriser 66ea63cf7e docs: KDE Plasma only: other desktops and display managers "are not supported" (README, was "don't work yet") 2026-09-28 19:47:28 +02:00
theupriser f560f332a9 Merge pull request #48 from theupriser/feature/docs-kde-only
docs: KDE Plasma only
2026-09-28 19:46:43 +02:00
theupriser 9b86cd1a6c docs: KDE Plasma only: other desktops don't work yet (README) 2026-09-28 19:46:36 +02:00
theupriser a72a4def71 Merge pull request #47 from theupriser/release/2.9.0
Release 2.9.0: docs
2026-09-28 19:38:41 +02:00
theupriser ea051c77f6 docs: changelog hash for d73e71d 2026-09-28 19:38:10 +02:00
theupriser 4e829c6111 Merge pull request #46 from theupriser/feature/docs-2.9.0
docs: 2.9.0 in README, TECHNICAL and TROUBLESHOOTING
2026-09-28 19:37:15 +02:00
theupriser d73e71d4eb docs: README, TECHNICAL and TROUBLESHOOTING for 2.9.0: Steam's System settings, serial number and Wi-Fi backend, --defaults --list, the Steamify ISO 2026-09-28 19:37:08 +02:00
theupriser 8f2d2be2aa Merge pull request #45 from theupriser/release/2.9.0
Release 2.9.0
2026-09-28 19:35:41 +02:00
theupriser 53b72c8e33 Merge pull request #44 from theupriser/feature/steamos-version
feat: Steam's OS Version steamos-3.9, OS Codename steam-machine
2026-09-28 19:31:35 +02:00
theupriser 3b4f3b2313 docs: changelog hash for a72d848 2026-09-28 19:31:28 +02:00
theupriser a72d848e78 feat: Steam's OS Version is the SteamOS release Steamify follows (steamos-3.9), its OS Codename steam-machine; About this System keeps "CachyOS Linux" (UseOSReleaseVersion) 2026-09-28 19:28:10 +02:00
theupriser 3a58c72d16 Merge pull request #43 from theupriser/bugfix/variant-version
fix: Steam's OS Variant shows Steamify's version
2026-09-28 19:22:31 +02:00
theupriser 27a40702ce fix: Steam's OS Variant shows Steamify's version (VARIANT_ID=steamify-2.9.0; Steam shows VARIANT_ID, not VARIANT) 2026-09-28 19:22:25 +02:00
theupriser 8ab066f034 Merge pull request #42 from theupriser/bugfix/no-version-id
fix: No VERSION_ID in os-release (About this System showed CachyOS Linux 2.9.0)
2026-09-28 19:17:10 +02:00
theupriser 44bfe071d6 fix: No VERSION_ID in os-release: About this System said "CachyOS Linux 2.9.0"; the version is in VARIANT ("Steamify 2.9.0") 2026-09-28 19:17:01 +02:00
theupriser e281d5e879 Merge pull request #41 from theupriser/feature/os-codename
feat: Steamify as OS variant, SteamOS release as codename; the OS name stays CachyOS
2026-09-28 19:14:50 +02:00
theupriser 435f69c151 feat: Steam's System settings: the OS name stays CachyOS (no renaming, also not in About this System), Steamify <version> as the variant, the SteamOS release it follows as the codename (steamos-3.9) 2026-09-28 19:14:43 +02:00
theupriser 9319cbca1e Merge pull request #40 from theupriser/bugfix/progress-checkmarks
fix(ui): The progress screen's checkmarks stand upright
2026-09-28 19:05:09 +02:00
theupriser cf6bee1c86 Merge release/2.9.0 into bugfix/progress-checkmarks 2026-09-28 19:05:02 +02:00
theupriser 16487ba00c Merge pull request #39 from theupriser/feature/steam-system-info
feat: Steam's System settings and About this System like on SteamOS
2026-09-28 19:04:29 +02:00
theupriser 07a94cd274 fix(ui): The progress screen's ✓ and ! stand upright: only a dot on the circle spins while a step runs (they kept the spinner's last angle) 2026-09-28 19:03:56 +02:00
theupriser ce71b34b0d feat: Steam's System settings and About this System like on SteamOS: OS name, Steamify as OS variant and version, serial number and Wi-Fi backend on a Steam Machine 2026-09-28 19:03:54 +02:00
theupriser d30d505f7d chore: Version 2.9.0 2026-09-28 19:03:28 +02:00
theupriser 9755cc3ac3 Merge pull request #38 from theupriser/bugfix/changelog-2.8.0-list
docs: CHANGELOG: 2.8.0's --defaults --list entry was missing
2026-09-28 13:02:11 +02:00
theupriser 6f26e74229 docs: CHANGELOG: 2.8.0's --defaults --list entry was missing 2026-09-28 13:01:00 +02:00
theupriser 2a01583b8f Merge pull request #37 from theupriser/release/2.8.0
Release 2.8.0
2026-09-28 12:46:15 +02:00
theupriser c40aa1f7d9 Merge pull request #36 from theupriser/feature/defaults-list
feat: steamify.sh --defaults --list
2026-09-28 12:45:27 +02:00
theupriser d204efb59d feat: steamify.sh --defaults --list: what --defaults can set up here, as JSON, for an installer page 2026-09-28 11:40:10 +02:00
theupriser 12f6cc0b3a chore: Version 2.8.0 2026-09-28 11:35:47 +02:00
theupriser b01e2c5221 Merge pull request #35 from theupriser/release/2.7.0
Release 2.7.0
2026-09-28 10:57:45 +02:00
theupriser affb5f79b0 Merge pull request #34 from theupriser/bugfix/theme-off-single-launcher
fix: theme off no longer brings back single user's launcher restriction
2026-09-28 10:53:19 +02:00
theupriser 58e43d4c4a fix: turning the theme off no longer brings back single user's launcher restriction when single user was turned off in between 2026-09-28 10:52:28 +02:00
theupriser 23f265b02f Merge pull request #33 from theupriser/feature/remove-v2-ui-preview
chore: remove the v2 app preview
2026-09-28 10:47:10 +02:00
theupriser ced4a45d2f chore: the v2 app preview is gone (it is the released app): no preview pre-release step in CI, no mention in steamify-app.sh 2026-09-28 10:46:23 +02:00
theupriser 6ad686a8b2 fix: Merge branch 'feature/gh-required' into release/2.7.0 2026-09-28 10:19:06 +02:00
theupriser b1adacbcb3 docs: gh is required for the release-branch pull requests; ask the user to install it 2026-09-28 10:18:59 +02:00
theupriser 8dd2db3a97 fix: Merge branch 'feature/release-branches' into release/2.7.0 2026-09-28 10:18:06 +02:00
theupriser 501c0ba425 docs: gh or git for merging into the release branch 2026-09-28 10:17:59 +02:00
theupriser bee18b76a8 docs: the agent merges feature/bugfix branches into the release branch; the release into main stays the user's 2026-09-28 10:17:41 +02:00
theupriser eadbdda271 docs: release branches (release/X.Y.Z, feature/ and bugfix/ branches into it, the release into main); CI builds release/** pushes 2026-09-28 10:14:20 +02:00
theupriser c4ec755a4a feat: --defaults --options <ids> (exactly these on) replaces --skip; steamify.sh --boot gamescope|desktop on its own switches where an installed conversion starts 2026-09-28 10:06:45 +02:00
theupriser 14aa7c0b54 fix: the app's progress screen says "Boot into desktop", not "Boot into Boot into" 2026-09-28 10:06:32 +02:00
theupriser 5bf9da411d fix: HDMI-CEC enables cec-audio-control.socket (the TV remote's volume keys); existing installs get it as an update 2026-09-28 10:06:23 +02:00
theupriser 4f6e6fd0aa feat: --defaults takes --skip <ids> and --boot gamescope|desktop, for the Steam Machine ISO's installer pages 2026-09-27 21:59:08 +02:00
44 changed files with 3896 additions and 139 deletions

No files matched your search

+112 -23
View File
@@ -5,18 +5,16 @@
# release are moved to it, so both of these always get the newest version:
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify.sh | bash
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/download/latest/steamify.sh | bash
# Each release also has the app (v2): steamify-app.tar.gz (ui/, steamify.sh,
# Each release also has the app: steamify-app.tar.gz (ui/, steamify.sh,
# lib/, patches/, services/, the icon) and its launcher, run with:
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify-app.sh | bash
# A push to feat/v2-ui (the app, not released yet) updates the pre-release
# v2-ui-preview instead, whose launcher gets that build:
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/download/v2-ui-preview/steamify-app.sh | bash
# Pushes to release/** build and check it like pull requests; only main publishes.
# Pull requests only build and check it, and warn when VERSION is already released.
name: Single-file build
on:
push:
branches: [main, feat/v2-ui]
branches: [main, 'release/**']
pull_request:
workflow_dispatch:
@@ -42,12 +40,39 @@ jobs:
- name: Check
run: |
bash -n dist/steamify.sh
# SC2034/SC2154: variables used across the inlined modules.
shellcheck -S warning -e SC2034,SC2154 dist/steamify.sh
shellcheck -S warning dist/steamify-app.sh
python3 -m py_compile ui/steamify-ui
# shellcheck through an action that brings its own binary: GitHub's runner has it installed,
# Gitea's act_runner image doesn't. One file per run, as before (the exceptions are the bundle's).
- name: shellcheck the bundle
uses: ludeeus/action-shellcheck@2.0.0
env:
# SC2034/SC2154: variables used across the inlined modules.
SHELLCHECK_OPTS: -e SC2034 -e SC2154
with:
severity: warning
scandir: dist
ignore_names: steamify-app.sh
- name: shellcheck the app launcher
uses: ludeeus/action-shellcheck@2.0.0
with:
severity: warning
scandir: dist
ignore_names: steamify.sh
# The build as an artifact on GitHub and on the mirror (Gitea); releases stay GitHub's.
# Gitea's artifact server doesn't speak upload-artifact@v4's protocol, and GitHub fails a workflow
# that merely mentions the deprecated @v3: Gitea gets christopherhx's fork of v4 for Gitea.
- uses: actions/upload-artifact@v4
if: github.server_url == 'https://github.com'
with:
name: steamify
path: |
dist/steamify.sh
dist/steamify-app.tar.gz
dist/steamify-app.sh
- uses: christopherhx/gitea-upload-artifact@v4
if: github.server_url != 'https://github.com'
with:
name: steamify
path: |
@@ -57,6 +82,7 @@ jobs:
- name: Version
id: version
if: github.server_url == 'https://github.com'
env:
GH_TOKEN: ${{ github.token }}
run: |
@@ -71,6 +97,7 @@ jobs:
- name: Publish the v${{ steps.version.outputs.version }} release
if: >-
github.server_url == 'https://github.com' &&
github.event_name != 'pull_request' && github.ref == 'refs/heads/main' &&
steps.version.outputs.released == 'false'
env:
@@ -107,22 +134,84 @@ jobs:
fi
gh release upload latest dist/steamify.sh dist/steamify-app.sh dist/steamify-app.tar.gz assets/steam-gaming-settings.svg --clobber
- name: Publish the v2-ui-preview pre-release
if: github.event_name == 'push' && github.ref == 'refs/heads/feat/v2-ui'
# The mirror (Gitea) publishes its own release per version too, the same way: never overwrites one,
# notes from the changelog. gh can't talk to Gitea: its API for the check, a Gitea release action to
# publish. The token is the run's own (repository write); a secret RELEASE_TOKEN overrides it.
- name: Version (mirror)
id: mirror_version
if: github.server_url != 'https://github.com'
env:
GH_TOKEN: ${{ github.token }}
TOKEN: ${{ secrets.RELEASE_TOKEN || github.token }}
run: |
# Its launcher downloads this pre-release, not the newest release.
sed -i 's/STEAMIFY_RELEASE:-latest/STEAMIFY_RELEASE:-v2-ui-preview/' dist/steamify-app.sh
grep -q 'STEAMIFY_RELEASE:-v2-ui-preview' dist/steamify-app.sh
git tag -f v2-ui-preview "$GITHUB_SHA" && git push -f origin v2-ui-preview
notes="Preview of the Steamify app (v2) from feat/v2-ui ($(git describe --always), $(git log -1 --format='%cs %s')). Not a release.
Run it with:
curl -fsSL https://github.com/${{ github.repository }}/releases/download/v2-ui-preview/steamify-app.sh | bash"
if gh release view v2-ui-preview >/dev/null 2>&1; then
gh release edit v2-ui-preview --title "Steamify app (v2) preview" --notes "$notes" --prerelease --latest=false
version="$(grep -oP '^VERSION=\K.*' steamify.sh)"
echo "version=$version" >> "$GITHUB_OUTPUT"
if curl -fsS -o /dev/null -H "Authorization: token $TOKEN" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/tags/v$version"; then
echo "released=true" >> "$GITHUB_OUTPUT"
echo "::warning::v$version is already released on this server and won't be overwritten."
else
gh release create v2-ui-preview --target "$GITHUB_SHA" --title "Steamify app (v2) preview" --notes "$notes" --prerelease --latest=false
echo "released=false" >> "$GITHUB_OUTPUT"
fi
gh release upload v2-ui-preview dist/steamify-app.sh dist/steamify-app.tar.gz --clobber
- name: Release notes (mirror)
if: >-
github.server_url != 'https://github.com' &&
github.event_name != 'pull_request' && github.ref == 'refs/heads/main' &&
steps.mirror_version.outputs.released == 'false'
env:
VERSION: ${{ steps.mirror_version.outputs.version }}
run: |
changes="$(awk -v v="$VERSION" '
$0 ~ "^## " { if (found) exit; if (index($0, "## " v " ") == 1) { found = 1; next } }
found' CHANGELOG.md)"
cat > dist/release-notes.md <<NOTES
Single-file build of v$VERSION ($(git describe --always), $(git log -1 --format=%cs)).
Run this version with:
curl -fsSL ${{ github.server_url }}/${{ github.repository }}/releases/download/v$VERSION/steamify.sh | bash
## Changes in v$VERSION
$changes
NOTES
- name: Publish the v${{ steps.mirror_version.outputs.version }} release (mirror)
if: >-
github.server_url != 'https://github.com' &&
github.event_name != 'pull_request' && github.ref == 'refs/heads/main' &&
steps.mirror_version.outputs.released == 'false'
uses: akkuman/gitea-release-action@v1.3.7
with:
token: ${{ secrets.RELEASE_TOKEN || github.token }}
tag_name: v${{ steps.mirror_version.outputs.version }}
target_commitish: ${{ github.sha }}
name: Steamify CachyOS v${{ steps.mirror_version.outputs.version }}
body_path: dist/release-notes.md
files: |
dist/steamify.sh
dist/steamify-app.sh
dist/steamify-app.tar.gz
assets/steam-gaming-settings.svg
# A new version starts the Steamify ISO's release of the ISO repo: its iso-1-github-tag.yml (always run from
# master) names the ISO after the newest Steamify release, tags it, and the Gitea mirror builds and attaches the ISO.
# The branch here decides the kind of build (the workflow's input `kind`):
# main (publishes the release) -> release: a real ISO (tag v<steamify>-<date>)
# release/** (a version not yet released) -> dev: a pre-release (tag v<steamify>-dev.<date>)
# Needs a fine-grained token for that repo (Actions: read and write) as the secret ISO_DISPATCH_TOKEN;
# without it, this is skipped.
- name: Start the Steamify ISO's release
if: >-
github.server_url == 'https://github.com' &&
github.event_name != 'pull_request' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) &&
steps.version.outputs.released == 'false'
env:
GH_TOKEN: ${{ secrets.ISO_DISPATCH_TOKEN }}
run: |
if [ -z "$GH_TOKEN" ]; then
echo "::notice::No ISO_DISPATCH_TOKEN: the Steamify ISO isn't released with this version."; exit 0
fi
# A release branch builds the ISO with its own code (and VERSION): steamify_ref. main builds the release it just published.
if [ "$GITHUB_REF" = refs/heads/main ]; then kind=release; sref=""; else kind=dev; sref="$GITHUB_REF_NAME"; fi
gh workflow run iso-1-github-tag.yml -R theupriser/steamify-cachyos-live-iso --ref master -f kind="$kind" -f steamify_ref="$sref"
echo "Started the Steamify ISO's release (theupriser/steamify-cachyos-live-iso, master, kind $kind, Steamify ${sref:-the newest release})"
+26
View File
@@ -0,0 +1,26 @@
name: Sync git.upriser.nl mirror
# Trigger the Gitea pull-mirror immediately instead of waiting for its interval,
# so Gitea builds/tests right after any branch or tag lands on GitHub (the mirror has no sync interval of its own).
on:
push:
branches: ['**']
tags: ['*']
workflow_dispatch:
jobs:
sync:
# Gitea also reads .github/workflows on the mirror; only run on GitHub
if: github.server_url == 'https://github.com'
runs-on: ubuntu-latest
steps:
- name: Trigger mirror-sync
env:
GITEA_URL: ${{ secrets.GIT_UPRISER_URL }}
GIT_UPRISER_TOKEN: ${{ secrets.GIT_UPRISER_TOKEN }}
# Gitea "owner/repo"; defaults to the same path as on GitHub
GITEA_REPO: ${{ vars.GITEA_REPO || github.repository }}
run: |
curl --fail-with-body -sS --retry 5 --retry-delay 10 --retry-all-errors -X POST \
-H "Authorization: token ${GIT_UPRISER_TOKEN}" \
"${GITEA_URL%/}/api/v1/repos/${GITEA_REPO}/mirror-sync"
+2
View File
@@ -0,0 +1,2 @@
Not tested on hardware yet: the whole Steamify flow for the patched gamescope on NVIDIA (apply, reboot, session).
Delete this file when it was, after asking the user (AGENTS.md).
+93 -8
View File
@@ -48,12 +48,23 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
`<id>_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`.
- **NVIDIA** (`lib/gamescope-nvidia.sh`, `lib/nvidia.sh`): since 2.12.0 the SteamOS conversion (`gaming` and its options) runs on
NVIDIA PCs with a patched gamescope (GBM scan-out: gamescope#2309; Steam's UI kept at the display's size), downloaded as the
newest release of theupriser/steamify-gamescope-nvidia, with the pinned package (`GSN_VERSION`, `GSN_SHA256`), the copy the ISO
leaves, and a build here (`patches/gamescope-nvidia.PKGBUILD` + the two patches) as the fallback. "Gaming on NVIDIA" (`nvidia`,
`bigpicture`, `nvsilent`) isn't offered any more (`nvidia_available`); turning the conversion on turns it off (`nvidia_swap_out`).
Builds exist per CPU level (`gsn_cpu_variant`: znver4, v4, v3, generic; `GSN_SHA256` is a map; the installed one must pass `gsn_smoke_test`, else generic). A new pin = the release's tag + each variant's sha256 (from `SHA256SUMS`) in `lib/gamescope-nvidia.sh` and the PKGBUILD's pkgver/pkgrel, copied from the
repository (the patches in `patches/` must stay identical to that repository's). Steam must keep running with `-steamos3`
(without it games don't launch in gaming mode and the settings pages go): never "fix" the UI size by dropping flags or by
`XWAYLAND_COUNT`. Tested on an RTX 5080 only; keep the branch behind `.no-release-yet` until the whole flow ran on hardware.
- **Feature versions.** Set `FEATURE_VERSION[<id>]` (`lib/menu.sh`) to the new
`VERSION` whenever what `<id>_enable` sets up changes (a new component gets
one too): installs recorded with an older version
are ticked and re-applied ("update" in the plan and the app). New default
options are ticked for installs whose parent (top-level: `gaming`) is on
(`feature_new`, shown as "new").
(`feature_new`, shown as "new"). New opt-in options (`NO_PRESELECT`) get the
same "new" badge without being ticked (`feature_new_optin`). Both count "Gaming
on NVIDIA" as a parent where the conversion isn't offered.
Options shown but left unticked in a confirmed run are recorded as
`off` (`feature_record_unticked`), or `feature_new` would tick them again.
Status still comes from the system; don't add ad-hoc `<id>_repair` checks
@@ -68,6 +79,13 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
the entry point's `for lib in ...; do` source loop and wraps everything
after that loop in `main()`. Keep that loop on one line, keep all logic in
functions, and don't rely on `SCRIPT_DIR` for anything but sourcing.
The workflow is one file for GitHub and a Gitea mirror (`github.server_url`): build and check run on both
(shellcheck through `ludeeus/action-shellcheck`, which brings its own binary; the artifact with
`upload-artifact@v4` on GitHub and `christopherhx/gitea-upload-artifact@v4` elsewhere: never mention
`upload-artifact@v3`, GitHub fails a workflow for a deprecated version even in a skipped step); the
release is `gh` on GitHub and `akkuman/gitea-release-action` on Gitea. A new release on GitHub also starts the
Steamify ISO's release (`gh workflow run iso-1-github-tag.yml` in steamify-cachyos-live-iso, secret
`ISO_DISPATCH_TOKEN`; skipped without it): the ISO repo's GitHub tag names it, the Gitea mirror builds it.
CI (`.github/workflows/bundle.yml`) publishes the bundle as release
`v$VERSION` on pushes to `main`; an existing version is never overwritten,
so bump `VERSION` for every release.
@@ -98,15 +116,55 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
+ subject; a commit can't contain its own hash, so fill it in with the
next commit). The section heading must be `## <VERSION> - <date>`: CI
cuts the release notes out of the changelog by that heading.
- Every PR that should be released bumps `VERSION` and adds its section.
A push to `main` publishes release `v$VERSION` (tag + bundle + that
- **Branches: one release branch per version.** Work for a release goes
into `release/X.Y.Z` (e.g. `release/2.7.0`), branched from `main` when
that version starts. That branch bumps `VERSION` to `X.Y.Z` and adds the
`## X.Y.Z - <date>` changelog section (first commit on it).
- Every change gets its own branch from the release branch:
`feature/<name>` (new behaviour) or `bugfix/<name>` (a fix), and a
pull request **into the release branch**, not into `main`. Its commits
add their changelog lines to that version's section.
- When the release is done and tested (the test plan in
steamify-cachyos-dev, `TESTPLAN.md`), the release branch gets a pull
request **into `main`**. Merging it publishes the release (below).
- A fix for a released version: a new `release/X.Y.Z+1` from `main`,
with the `bugfix/` branch into it; don't reopen an old release branch.
- Up to the release branch, the agent manages it: create the
`feature/`/`bugfix/` branches, commit, push, and merge them into the
release branch itself once tested (with `gh`: `gh pr create --base
release/X.Y.Z` + `gh pr merge --merge`; no review needed). Without
`gh` (`command -v gh`), ask the user to install it (Ubuntu:
`sudo apt install gh`, then `gh auth login`) instead of working around
it. Only the
release branch's pull request into `main` is the user's: never commit
or merge to `main`, the user merges that one. Never work directly on
`main` or a `release/*` branch either: edits, even uncommitted ones,
go on a `feature/`/`bugfix/` branch (create it before editing; the
release branch only gets the version-bump commit and merged PRs).
Don't push a new `release/*` branch before it's needed: a push can
start CI builds (a dev ISO).
A machine without a git identity: pass the repo's author per command
(`git -c user.name=... -c user.email=... commit`, taken from `git
log`) instead of changing the git config.
A `.no-release-yet` file at the root of a feature/bugfix branch means
it must not be merged into the release branch yet (work in progress,
untested on hardware). When the feature is complete, ask the user
whether it may be released; on a yes, delete the file, then open the
pull request and merge it.
Keep a feature/bugfix branch up to date by merging (or rebasing on)
its release branch, the release branch by merging `main` when that
moved.
- A push to `main` publishes release `v$VERSION` (tag + bundle + that
changelog section) and marks it latest. An existing version is never
overwritten: without a bump nothing is released, and pull requests show
a warning.
a warning when `VERSION` is a version that's already released (so a
release branch that forgot its bump shows it on every PR).
- A release that adds, removes or renames a menu row also retakes the
README screenshot (`assets/screenshot-menu.png`): the app from the
branch on a Steam Machine, every row visible (window 1280 wide, tall
enough), the header showing the new `VERSION`, scaled to 1600 px wide.
README screenshot (`assets/screenshot-menu-steam-machine.png`; NVIDIA PCs get the same menu, so there is no separate
one): the app from the branch on a Steam Machine, every row visible and the window only as tall as its options need, the header showing the new
`VERSION`, 1600 px wide. Size the window with a KWin script (match the `steamify-ui` class exactly: a title match also hits
Konsole and editor windows) and crop the full-screen capture to the client rectangle; mind the display scale (the Steam
Machine runs at 1.75).
- Users install through `releases/latest/download/steamify.sh`
(GitHub's newest release). The `latest` tag and release follow the newest
version tag too (moved, asset replaced, when a new version is released),
@@ -227,6 +285,11 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
kernel only pairs that with a named lookup ("Port C") when the CEC driver
registered first, which never happens since amdgpu loads from the
initramfs. Without it `/dev/cec0` exists but stays at `f.f.f.f`.
The source comes from one raw.githubusercontent.com download, which GitHub rate-limits (HTTP 429 after a
day of test installs, per address): it's kept in `/var/cache/steamify/cros-ec-cec-<sha256:12>.c` and reused
while its checksum matches. The name carries the checksum, so a new pin (commit + SHA-256 in `lib/cec.sh`)
downloads its own file once and removes the old one. The repo is an unofficial mirror of Valve's kernel
without releases: if it ever disappears, ship the file with Steamify. The test VMs share the host's cache.
- Power-off fix (`poweroff`, `lib/fremont-poweroff.sh`, a default sub-option
of `machine`): recent kernels (7.2 and the 6.x/7.0/7.1 updates with the
backport) keep the firmware's S4/S5 wake bit on GPIO
@@ -271,7 +334,7 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
(`state_get vram installed_pkgs`).
- Install-time mode (`steamify.sh --defaults`, 2.6.0): applies what the menu
would preselect, no menu or prompts (needs passwordless sudo). The Steam
Machine ISO ([steammachine-cachyos-live-iso](https://github.com/theupriser/steammachine-cachyos-live-iso))
Machine ISO ([steamify-cachyos-live-iso](https://github.com/theupriser/steamify-cachyos-live-iso))
runs it in the installer for a user who has never logged in: no session
bus, user systemd or plasmashell. So every `systemctl --user` goes through
`user_systemctl` (`lib/common.sh`; without a session only unit files change,
@@ -279,6 +342,28 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
removes it so Plasma builds Vapor's layout at the first login. What needs
that layout (single user's launcher) runs then, from a one-time autostart
(`lib/first-login.sh`, `--first-login`) that also opens the app.
`--options <id>,...` (exactly these on, the rest off; an item on brings its
parent) and `--boot gamescope|desktop` (desktop only with `gaming`) (2.7.0,
`defaults_options` in `lib/menu.sh`); the ISO's installer page passes them.
Items this PC can't use are left off with a warning, not an error.
`steamify.sh --boot gamescope|desktop` on its own (2.7.0, for scripts) only
switches `boot` on an installed conversion: `WANTED` is `CURRENT` plus
that, so no updates or removals a normal run would pick.
- What Steam's System settings show (SteamOS conversion, 2.9.0): the OS name
stays CachyOS's everywhere (legal clarity; Steam's OS Name is
`lsb_release -d`, not touched). `/etc/os-release` gets `VERSION_ID=steamos-X.Y`
(Steam's OS Version: Valve's newest jupiter repo; offline, the previous
value), `VERSION_CODENAME=steam-machine`, `VARIANT="Steamify <version>"` and
`VARIANT_ID=steamify-<version>` (Steam shows VARIANT_ID as OS Variant),
refreshed by `os_version_refresh` after every run that changes something; a
pacman hook (`zz-steamify-os-release`) runs after cachyos-hooks. KDE's About
this System would show VERSION_ID after the name: `kcm-about-distrorc`
`UseOSReleaseVersion=true` (kset) makes it show os-release's VERSION, which
CachyOS doesn't have. Never `NAME` or `PRETTY_NAME`: limine-snapper-sync
finds the boot entries by the OS name ("Target OS name ... not found in
/boot/limine.conf"); `/etc/default/limine` gets `TARGET_OS_NAME="CachyOS"`
when it has none, as a guard. The serial number (tmpfiles rule) is Steam
Machine support's, so Fremont only.
- `Relogin=true` means a gamescope that fails to start is relaunched in a
tight loop; keep that in mind when changing session handling.
+114 -1
View File
@@ -5,9 +5,122 @@ All notable changes, per version and per commit. Versions follow
`steamify.sh`. Versions before 0.7.0 were numbered afterwards,
one per merged pull request.
## 2.12.0 - 2026-10-13
- **feat: setting up the SteamOS conversion on NVIDIA first checks that nothing blocks it (a pacman lock, less than 1.5 GB free), and a failed setup is remembered: a desktop notification now and a reminder in the menu until a setup works**
- **docs: the README and TECHNICAL.md describe NVIDIA as it is now (the SteamOS conversion with the patched gamescope; "Gaming on NVIDIA" only as history); the NVIDIA menu screenshot is gone, NVIDIA PCs get the same menu**
- **feat: the patched gamescope comes in one build per CPU level, like CachyOS's repositories (generic, x86-64-v3, v4, znver4): Steamify picks the best one this CPU can run (`gsn_cpu_variant`), falls back to generic, and starts the installed gamescope once headless (`gsn_smoke_test`): a build that doesn't start is replaced by the generic one. CI compiles each with an explicit `-march` (a `-march=native` build died with SIGILL on a Ryzen) and checks the binary for instructions beyond its level.**
- **feat: on NVIDIA the gaming session runs on the display's own mode and Steam's interface at its size: a second gamescope patch (`gamescope-ui-native.patch`, release `3.16.31-3` of steamify-gamescope-nvidia, now the pin; built once per CPU level, see below) keeps Steam's UI X server at the output size when Steam (with `-steamos3`) asks for 1920x1080, and a wrapper (`gamescope-display-wrapper.sh`, set up by the conversion with a drop-in of `gamescope-session.service`) starts gamescope on the mode KDE has saved for the connected display, else the biggest mode at the highest refresh rate: before, gaming mode ran at the display's preferred mode (60 Hz on many monitors) with a 1080p Steam UI. Tested on an RTX 5080, 3440x1440@175: widescreen, sharp, a Proton game starts**
- **fix: on an NVIDIA PC that had single user mode on before the SteamOS conversion, "Return to Gaming Mode" and the boot choice work again: single user mode's desktop autologin (`zzz-steamify-autologin.conf`, read last) pinned the Plasma session; the conversion removes it, and turning the conversion off sets it up again**
- **fix: when an option fails, its sub-options are not applied any more (a failed conversion used to leave its "Boot into the desktop", silent start and icons on without it)**
- **fix: a PC that keeps `python-hidapi` (liquidctl needs it) can use the SteamOS conversion: jupiter-hw-support, which gamescope-session-cachyos depends on, wants `python-hid`, which conflicts with it and stopped the package install. Steamify builds and installs an empty placeholder package that provides `python-hid` first (`patches/python-hid-shim.PKGBUILD`); only the Steam Deck controller firmware updater uses it**
- **feat: the patched gamescope is downloaded as the newest release of steamify-gamescope-nvidia (checked against that release's `.sha256`), a newer one than the installed package shows the SteamOS conversion as an update, and what this Steamify was tested with is the fallback: the pinned package (sha256 in Steamify), the copy the ISO leaves, a build here; the repository's workflow builds each release in CachyOS's container**
- `124b424`, `f234c72` **fix: HDMI-CEC on the Steam Machine no longer needs GitHub: when downloading Valve's CEC driver fails, Steamify uses the copy it carries (`patches/cros-ec-cec.c`), checked against the same pinned sha256**
- `127b8cf`, `2811ecc`, `f3cd053`, `4325d80`, `03f9e87`, `5b5181f`, `06ffc8b`, `4d60dd2`, `0ba94b2` **feat: the SteamOS conversion is offered on NVIDIA PCs again (GTX 16 / RTX 20 series or newer: Turing and later, the cards NVIDIA's open kernel driver supports): it builds and installs a patched gamescope from the recipe and patch Steamify carries (`patches/`, prepared in [steamify-gamescope-nvidia](https://github.com/theupriser/steamify-gamescope-nvidia); gamescope 3.16.31, so a Steamify release builds the version it was tested with) (GBM-allocated scan-out buffers, NVIDIA's corrupted gaming-mode picture, gamescope#2309; a few minutes to build) and sets `gamescope_drm_gbm_scanout=1` for the session (`lib/gamescope-nvidia.sh`). It replaces "Gaming on NVIDIA", which is not offered any more (the kernel parameters, early modules, suspend memory and the 32-bit libraries come with the conversion on a supported card; a PC that had "Gaming on NVIDIA" on has it turned off when the conversion is turned on). Tested on an RTX 5080 (driver 615.78.08): Steam's gaming UI is clean with it and shows artifacts without. The package is pinned to one gamescope release: updating it is not automatic yet**
- `ce4bc67` **feat: "Gaming on NVIDIA" also keeps video memory over suspend (`NVreg_PreserveVideoMemoryAllocations=1` in `/etc/modprobe.d/90-steamify-nvidia.conf`, the initramfs is rebuilt), enables `nvidia-suspend`/`nvidia-resume`/`nvidia-hibernate`, and installs `lib32-nvidia-utils` and `lib32-libxkbcommon` (32-bit games, the 32-bit MangoHud preload) on a supported card; off removes only what Steamify added. Untested on hardware yet**
## 2.11.1 - 2026-10-02
- `d70aeb8` **docs: new README screenshots of the app (2.11 menu) on a Steam Machine and on an NVIDIA PC, and the README names the opt-in "Start Steam silently in desktop mode" and the icons as an option of the conversion**
## 2.11.0 - 2026-10-01
- `2e51d1d`, `3494924`, `fbdad5a`, `079e725`, `188799e` **feat: "Start Steam silently" sub-option: under the SteamOS conversion (`silent`, opt-in now: it was always on before; an existing one is moved from its systemd unit to the autostart entry by the "update") and under Gaming on NVIDIA (`nvsilent`, opt-in, not together with Big Picture); the SteamOS one is called "Start Steam silently in desktop mode" and is always shown, also on a Steam Machine; it is Steam's own autostart entry (`~/.config/autostart/steam.desktop`), so Steam's setting "Run Steam when my computer starts" follows it**
- `2f7b53f` **fix: the Steam Machine's serial number step no longer fails inside the ISO's installer (`/sys` is read-only in its chroot; the tmpfiles.d file applies at the next boot anyway), which made `machine` report an error and the install log end with `exit: 1`**
- `45a23af`, `bd8f387` **feat: `STEAMIFY_NO_DOWNLOAD` (the ISO's `steamify.nodownload` kernel parameter): the installed system's first login runs the Steamify bundle of the ISO instead of downloading the newest release (to test an unreleased Steamify)**
- `f3810a9` **fix: the SteamOS theme no longer leaves a blank Discover pin in the taskbar (Plasma's default taskbar pins Discover, which CachyOS doesn't install); the taskbar gets CachyOS's own pins (System Settings, files, browser), also on the ISO's first login**
- `7869b47` **feat: "Steam Deck/Machine icons" (`glyphs`) is a sub-option of the SteamOS conversion, like "Start Steam silently in desktop mode": it only changes gaming mode**
- `4d387b2` **chore: the Steamify unit tests (`controllers-test.sh`, `nvidia-test.sh`) and the NVIDIA hardware test live in steamify-cachyos-dev (`tests/`) now; `tests/` is removed here**
## 2.10.0 - 2026-10-01
- `d6db4f6` **feat: "Gaming on NVIDIA" (nvidia) replaces the SteamOS conversion on PCs with an NVIDIA GPU, where gamescope's gaming mode shows a corrupted picture (NVIDIA bug 5240452): Steam is installed when missing and starts at login on the Plasma desktop; the sub-option "Steam starts in Big Picture" (bigpicture) starts it in Big Picture, unticked in its normal window, on an empty Plasma session so windows of the last session don't cover it. On a supported card (RTX 20 series or newer) it also sets the NVIDIA kernel parameters and early modules. The conversion and its options are hidden there unless already on**
- `d1373e2` **feat: single user mode also works on NVIDIA PCs, without the SteamOS conversion**
- `5811f5c` **fix: "Add as non-Steam game" isn't offered on NVIDIA PCs (shown when already on, so it can be turned off)**
- `83248a8` **feat: Extended controller support (extended_controller_support, off by default): the Xbox wireless dongle (xone) and Xbox controllers over Bluetooth with rumble and battery level (xpadneo), built for every installed kernel; off removes only what Steamify installed**
- `01a77b7` **feat: a new opt-in option (unticked by default) shows the "new" badge in the menu and the app**
- `f1983a9` **docs: README screenshots of the 2.10.0 app, side by side: on a Steam Machine and on a PC with an NVIDIA card**
## 2.9.6 - 2026-09-30
- **fix(ci): a dev ISO from a release branch is built with that branch's own Steamify (input steamify_ref of the ISO workflow), not the newest published release: release/2.9.6 kept producing 2.9.5 ISOs**
- **docs: changelog hashes for 2.9.6**
- `9a2965d` **fix(ci): the ISO workflow always runs from master; main asks for a release ISO, a release branch for a dev ISO (input `kind` of iso-1-github-tag.yml; feat/steamify is no longer needed)**
- `c42d545` **fix(ci): a version published from main starts a real ISO release, a release branch a dev ISO (it always started the dev one)**
- `b3bed43` **chore: Version 2.9.6**
## 2.9.5 - 2026-09-30
- `70ecb56` **ci: trigger a mirror sync**
- `a7276cf` **ci: sync the git.upriser.nl mirror on every branch and tag**
- `b3549d4` **ci: sync the mirror on release branch pushes too**
- `a232a55` **feat(ci): sync the git.upriser.nl mirror on every push to main and every tag (git-upriser-sync.yml calls the mirror-sync API: the Gitea mirror builds at once instead of at its interval; secrets GIT_UPRISER_URL and GIT_UPRISER_TOKEN, retried, skipped on Gitea itself)**
- `3676c64` **docs: link the renamed steamify-cachyos-live-iso repo (the release dispatches the ISO workflow iso-1-github-tag.yml there)**
- `971e792` **chore: Version 2.9.5**
## 2.9.4 - 2026-09-29
- `619ef1f` **docs: AGENTS.md and TECHNICAL.md: the CEC driver cache, the one workflow for GitHub and a Gitea mirror, and that a new release starts the Steamify ISO's release**
- `b624053` **chore: Version 2.9.4**
## 2.9.3 - 2026-09-29
- `7f5d29c` **feat(ci): a new Steamify release starts the Steamify ISO's release (a test build from the ISO repo's feat/steamify; secret ISO_DISPATCH_TOKEN)**
- `cfe3ce7` **chore: Version 2.9.3**
## 2.9.2 - 2026-09-29
- **feat(ci): a Gitea mirror publishes its own release per version (akkuman/gitea-release-action; never overwrites one, notes from the changelog, links to the mirror)**
- **fix(ci): the build also runs on a Gitea mirror (act_runner): shellcheck through ludeeus/action-shellcheck, which brings its own binary; the artifact goes up there with christopherhx/gitea-upload-artifact (GitHub refuses a workflow that mentions the deprecated upload-artifact@v3); releases are only published on GitHub**
- `fe4f3f4` **chore: Version 2.9.2**
## 2.9.1 - 2026-09-29
- `c87e6c7` **docs: changelog for the CEC driver cache**
- `fc05725` **fix: HDMI-CEC: an older cached CEC driver is removed when a new pin is downloaded**
- `90bc5e4` **fix: HDMI-CEC: Valve's CEC driver is downloaded once and kept in /var/cache/steamify (GitHub rate-limits repeated downloads with HTTP 429, which left CEC off); turning CEC on again needs no network**
- `d99ff65` **chore: Version 2.9.1**
## 2.9.0 - 2026-09-28
- `d30d505` **chore: Version 2.9.0**
- `66ea63c` **docs: KDE Plasma only: other desktops and display managers "are not supported" (README, was "don't work yet")**
- `9b86cd1` **docs: KDE Plasma only: other desktops don't work yet (README)**
- `d73e71d` **docs: README, TECHNICAL and TROUBLESHOOTING for 2.9.0: Steam's System settings, serial number and Wi-Fi backend, --defaults --list, the Steamify ISO**
- `a72d848` **feat: Steam's OS Version is the SteamOS release Steamify follows (steamos-3.9), its OS Codename steam-machine; About this System keeps "CachyOS Linux" (UseOSReleaseVersion)**
- `27a4070` **fix: Steam's OS Variant shows Steamify's version (VARIANT_ID=steamify-2.9.0; Steam shows VARIANT_ID, not VARIANT)**
- `44bfe07` **fix: No VERSION_ID in os-release: About this System said "CachyOS Linux 2.9.0"; the version is in VARIANT ("Steamify 2.9.0")**
- `435f69c` **feat: Steam's System settings: the OS name stays CachyOS (no renaming, also not in About this System), Steamify <version> as the variant, the SteamOS release it follows as the codename (steamos-3.9)**
- `ce71b34` **feat: Steam's System settings and About this System like on SteamOS: OS name "CachyOS with Steamify" (lsb-release, kept through CachyOS updates by a pacman hook), Steamify as OS variant and version (os-release VARIANT/VERSION_ID; NAME/PRETTY_NAME stay CachyOS's for limine, TARGET_OS_NAME pinned), "CachyOS Linux with Steamify" in About this System; on a Steam Machine the serial number and Steam's Wi-Fi backend setting (existing installs get it as an update)**
- `07a94cd` **fix(ui): The progress screen's ✓ and ! stand upright: only a dot on the circle spins while a step runs (they kept the spinner's last angle)**
## 2.8.0 - 2026-09-28
- **chore: Version 2.8.0**
- **feat: steamify.sh --defaults --list: what --defaults can set up here, as JSON, for an installer page**
## 2.7.0 - 2026-09-28
- `4f6e6fd` **feat: --defaults takes --skip <ids> and --boot gamescope|desktop, for the Steam Machine ISO's installer pages**
- `5bf9da4` **fix: HDMI-CEC enables cec-audio-control.socket (the TV remote's volume keys); existing installs get it as an update**
- `14aa7c0` **fix: the app's progress screen says "Boot into desktop", not "Boot into Boot into"**
- `c4ec755` **feat: --defaults --options <ids> (exactly these on) replaces --skip; steamify.sh --boot gamescope|desktop on its own switches where an installed conversion starts**
- `eadbdda` **docs: release branches (release/X.Y.Z, feature/ and bugfix/ branches into it, the release into main); CI builds release/** pushes**
- `bee18b7` **docs: the agent merges feature/bugfix branches into the release branch; the release into main stays the user's**
- `501c0ba` **docs: gh or git for merging into the release branch**
- `b1adacb` **docs: gh is required for the release-branch pull requests; ask the user to install it**
- `ced4a45` **chore: the v2 app preview is gone (it is the released app): no preview pre-release step in CI, no mention in steamify-app.sh**
- **fix: turning the theme off no longer brings back single user's launcher restriction when single user was turned off in between**
## 2.6.0 - 2026-09-27
- **feat: --defaults applies the default setup without the menu, also from an installer (no session): the base for a Steam Machine ISO**
- `b948c20` **feat: --defaults applies the default setup without the menu, also from an installer (no session): the base for a Steam Machine ISO**
## 2.5.2 - 2026-09-27
+61 -4
View File
@@ -1,6 +1,9 @@
# Steamify CachyOS
![The Steamify app's menu on a Steam Machine](assets/screenshot-menu.png)
The Steamify app's menu, on a Steam Machine. On a PC with an NVIDIA graphics card (GTX 16 / RTX 20 series or
newer) the same SteamOS conversion is offered; Steamify installs a patched gamescope for it.
![The Steamify app's menu on a Steam Machine](assets/screenshot-menu-steam-machine.png)
Turn a CachyOS desktop PC into a SteamOS-style console: it boots straight
into Steam's Big Picture (gamescope), and you can switch to the KDE Plasma
@@ -19,10 +22,28 @@ Steam Machine.
on the desktop too.
- **Boot into: gamescope / desktop** - where the PC starts. Gaming mode is
the default.
- **Start Steam silently in desktop mode** (off by default) - Steam starts
at login on the desktop, in the tray without a window. It is Steam's own
autostart entry, so Steam's setting "Run Steam when my computer starts"
follows it (the Linux Steam client has no silent option of its own).
- Steam's **System** settings show the SteamOS release Steamify follows
as OS Version (e.g. `steamos-3.9`), `steam-machine` as OS Codename and
Steamify's version as OS Variant. The OS name stays CachyOS
([details](TECHNICAL.md#what-steams-system-settings-show)).
- **NVIDIA graphics cards** (GTX 16 / RTX 20 series or newer): stock gamescope shows a corrupted
picture on them (an open NVIDIA driver bug), so on an NVIDIA PC Steamify installs a **patched gamescope**
(GBM scan-out, from [steamify-gamescope-nvidia](https://github.com/theupriser/steamify-gamescope-nvidia))
and sets up the rest: the gaming session starts on the monitor's own resolution and refresh rate, with
Steam's interface at that size. It is the same conversion as everywhere else, with one extra step.
The newest patched build for your CPU is downloaded from GitHub (checked, with a built-in fallback if GitHub
can't be reached), and a newer one shows up as an **Update** in the menu. Tested on an RTX 5080 only
([details](TECHNICAL.md#nvidia-graphics-cards)).
The old **Gaming on NVIDIA** option (Steam on the desktop) is gone: a PC that had it on is moved to the
conversion, and keeps starting Steam in Big Picture if that was on.
2. **SteamOS theme** - the Vapor look of SteamOS for the desktop, with
**Add to Steam** in right-click menus
([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 (an option of the SteamOS conversion).
4. **Single user mode** - like SteamOS: never a login or lock screen, and no
KDE wallet password prompts (e.g. from Brave). Typing a password with a
controller is no fun
@@ -33,7 +54,7 @@ Steam Machine.
With **Add as non-Steam game** (on by default) it's in your Steam library
too: started from Steam it gets your controller (a Steam Controller only
talks to Steam), in gaming mode as well. Steam closes for a moment while
it's added, so that's done from the desktop.
it's added, so that's done from the desktop. Not offered on PCs with an NVIDIA graphics card yet (not tested there).
6. **Update notifications** - on by default: a notification and a tray icon
when there's a new Steamify, with Open Steamify and Skip this version. It
never updates anything by itself
@@ -52,6 +73,11 @@ Steam Machine.
the Steam Machine) or a USB CEC adapter; most graphics cards, NVIDIA
included, don't have it. See [CEC.md](CEC.md) for which PCs do, and how to
add it to your own build ([details](TECHNICAL.md#hdmi-cec)).
9. **Extended controller support** (off by default) - the Xbox wireless
dongle (xone) and Xbox controllers over Bluetooth with rumble, the right
button mapping and the battery level (xpadneo). Both are installed from the
CachyOS repo and built for every installed kernel
([details](TECHNICAL.md#extended-controller-support)).
## Steam Machine
@@ -62,7 +88,8 @@ What Steamify can do on a Valve Steam Machine:
([details](TECHNICAL.md#hdmi-cec)).
- **Steam Machine support** - the front **LED bar** works, Steam's
**hardware settings** (fan, performance) work, and the power button puts
it to sleep like a console.
it to sleep like a console. Steam's System settings show the serial
number, and Steam's **Wi-Fi backend** setting works.
- **Power-off fix** - on by default: shutting down really turns the Steam
Machine off, also with the newest CachyOS kernel
([details](TECHNICAL.md#power-off-fix-steam-machine)).
@@ -73,6 +100,11 @@ What Steamify can do on a Valve Steam Machine:
## Requirements
- CachyOS with the KDE Plasma desktop (the default CachyOS Desktop edition)
> **KDE Plasma only.** Steamify works with KDE Plasma and its login managers
> (plasma-login-manager or SDDM). Other desktops (GNOME, Hyprland, Cosmic,
> ...) and other display managers are not supported: the switching between gaming mode
> and the desktop, the theme and the shortcuts are all built for Plasma.
- Your normal user account, with permission to use `sudo`
## Installation
@@ -83,6 +115,11 @@ Open **Konsole** on your Plasma desktop and run:
curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify.sh | bash
```
Installing CachyOS on a Steam Machine? The
[Steamify CachyOS ISO](https://github.com/theupriser/steamify-cachyos-live-iso)
has Steamify built into the installer: pick the options there, and the
first boot is already set up.
Prefer to look at the script first? Clone the repository and run
[`./steamify.sh`](steamify.sh) (keep the whole folder: it needs
[`lib/`](lib/)).
@@ -125,6 +162,14 @@ steamos-session-select persistent # start where you left off last time
steamos-session-select oneshot # always start in gaming mode (default)
```
Where the PC starts after a restart can also be set from a script, once the
SteamOS conversion is on (only that changes; `sudo` may ask for your password):
```bash
steamify.sh --boot desktop # start in the desktop from the next boot on
steamify.sh --boot gamescope # start in gaming mode again (default)
```
## Something wrong?
Stuck on a black screen in gaming mode: press **Ctrl+Alt+F3**, log in, and run
@@ -141,6 +186,18 @@ Contributing (or an AI agent)? Read [AGENTS.md](AGENTS.md).
MIT - do whatever you want with it. See [LICENSE.md](LICENSE.md).
### Code from other projects
Some files in `patches/` are not MIT. They keep their own license, and each file says where it comes from:
| File | What | License and source |
|---|---|---|
| `steamify-fremont-poweroff.c` | Kernel module for the Steam Machine's power-off fix. It does what Valve's Steam Machine kernel does for this (clears the S4 wake bit of GPIO pin 18 at probe), but at power-off, for kernels without Valve's change. Valve's kernel: [evlaV/linux-integration](https://github.com/evlaV/linux-integration). | GPL-2.0, like the Linux kernel it is built into |
| `cros-ec-cec.c` | Valve's Steam Machine HDMI-CEC driver (ChromeOS EC CEC, Copyright BayLibre; Valve's version of it), at a pinned commit of [evlaV/linux-integration](https://github.com/evlaV/linux-integration). Used when it can't be downloaded. | GPL-2.0+, unmodified (Steamify's `cros-ec-cec-single-port.patch` is applied when it is built) |
| `gamescope-gbm-scanout.patch` | GBM scan-out for gamescope on NVIDIA: nine commits of [farnoy/gamescope](https://github.com/farnoy/gamescope) on [ValveSoftware/gamescope](https://github.com/ValveSoftware/gamescope). | gamescope's license (BSD-2-Clause) |
| `gamescope-ui-native.patch` | Keeps Steam's interface at the display's size in gamescope on NVIDIA (a small patch after the one above). | gamescope's license (BSD-2-Clause) |
| `gamescope-nvidia.PKGBUILD` | The recipe for the patched gamescope package, used only when it can't be downloaded. | MIT, like Steamify |
## Disclaimer
Steamify CachyOS is an unofficial community project. It is not affiliated
+196 -10
View File
@@ -43,6 +43,14 @@ 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.
**Without the conversion** (any PC with it off; on NVIDIA that is before 2.12.0 or after turning it off) nothing else logs in by itself, so single user mode
does that too: `single_login_enable` (`lib/single-user.sh`) switches to SDDM and writes
`/etc/sddm.conf.d/zzz-steamify-autologin.conf` (`User=`, `Session=plasma.desktop`, `Relogin=true`; named to sort after
`steam-set-session`'s `zz-steamos-autologin.conf`, which may still say gamescope from an earlier conversion), and drops any
`[Autologin]` from `/etc/sddm.conf`. Turning it off removes the file and goes back to plasma-login-manager. With the
conversion on, the conversion does the login as before. Ticking single user mode only ticks the conversion where the
conversion is offered.
**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),
@@ -100,9 +108,11 @@ tools write to the conf.d fragment into the base config.
(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;
- `KWIN_IM_SHOW_ALWAYS=1` for the virtual keyboard; the sub-option `silent`
("Start Steam silently in desktop mode", opt-in, always shown) writes
`~/.config/autostart/steam.desktop` (the system steam.desktop with `-silent` in its Exec line). That is the
file Steam's own setting "Run Steam when my computer starts" creates and removes, so the setting shows it. An older
`steam-desktop-autostart` user unit is removed;
- 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.
@@ -111,9 +121,13 @@ 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.
The conversion also sets what Steam's System settings show, see
[below](#what-steams-system-settings-show).
**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.
file), which makes Steam show Steam Deck button glyphs in gaming mode. It is a sub-option of the SteamOS conversion (`gaming`):
it does nothing without gaming mode, ticking it ticks the conversion and turning the conversion off turns it off.
## SteamOS desktop look
@@ -139,6 +153,33 @@ taken from the newest `steamdeck-kde-presets` on Valve's SteamOS mirror
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).
## What Steam's System settings show
With the SteamOS conversion on, `/etc/os-release` gets:
| Key | Value | Shown in Steam as |
|---|---|---|
| `VERSION_ID` | `steamos-3.9`: the newest SteamOS release in Valve's repo (offline: the previous value) | OS Version |
| `VERSION_CODENAME` | `steam-machine` | OS Codename |
| `VARIANT` / `VARIANT_ID` | `Steamify 2.9.0` / `steamify-2.9.0` | OS Variant (`VARIANT_ID`) |
The OS name stays CachyOS everywhere: Steam's OS Name comes from
`lsb_release -d`, which isn't touched, and `NAME`/`PRETTY_NAME` stay
CachyOS's. limine-snapper-sync finds the boot entries by that name, so
changing it would break snapshot boot entries; `/etc/default/limine` gets
`TARGET_OS_NAME="CachyOS"` as a guard when it has none.
KDE's About this System would show `VERSION_ID` after the name
("CachyOS Linux steamos-3.9"); `UseOSReleaseVersion=true` in
`~/.config/kcm-about-distrorc` makes it show `VERSION`, which CachyOS doesn't
have, so it keeps saying "CachyOS Linux".
CachyOS's own hooks (`cachyos-hooks`) reset os-release on updates of
`filesystem`; the pacman hook `zz-steamify-os-release` runs after them and
sets the keys again (`/usr/local/libexec/steamify-os-release`). The values are
refreshed after every Steamify run that changes something. Turning the
conversion off removes the hook and runs CachyOS's `cachyos-branding` again.
## Front LED bar (Steam Machine)
The **Steam Machine support** component, only shown on Fremont hardware
@@ -156,7 +197,14 @@ The **Steam Machine support** component, only shown on Fremont hardware
- installs and enables `steamos-manager`, the service Steam in gaming mode
uses for hardware settings (fan, performance, and the HDMI-CEC settings
when [HDMI-CEC](#hdmi-cec) is on); it recognises the Steam Machine from its
DMI data.
DMI data;
- makes the serial number readable for Steam, which shows it in its System
settings (`/etc/tmpfiles.d/steamify-serial.conf`: `product_serial` is
root-only by default);
- makes Steam's Wi-Fi backend setting work: steamos-manager switches it in
`/etc/NetworkManager/conf.d/99-valve-wifi-backend.conf`, which is created
with `wifi.backend=wpa_supplicant` (NetworkManager's default) when no
backend is set anywhere yet.
Turning it off removes all of that again (the AUR helper is kept).
@@ -169,6 +217,112 @@ 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.
## Extended controller support
`extended_controller_support` (`lib/controllers.sh`, top-level, off by default, 2.10.0) installs two kernel drivers from CachyOS's own
repo (no AUR): `xone-dkms` with `xone-dongle-firmware` for the Xbox wireless dongle (the kernel has no driver for it; wired
Xbox controllers go through the same driver), and `xpadneo-dkms` for Xbox One/Series controllers over Bluetooth (without it
they work as plain gamepads but miss rumble including the triggers, the right button mapping and the battery level). Both are
DKMS modules, built for every installed kernel, so `install_kernel_headers` (`lib/steam-machine.sh`) makes sure each kernel has its
headers first. A PC that already has the AUR's `xone-dkms-git` keeps it: it provides the same driver and conflicts with the repo's
`xone-dkms`. Status is the three packages being installed. Off removes only what Steamify installed (state
`extended_controller_support`, `installed_pkgs`); the headers stay. The drivers load when the device shows up: the dongle is
plugged in again, or the controller reconnected. Its test is in steamify-cachyos-dev (`tests/controllers-test.sh`).
## NVIDIA graphics cards
**The problem.** gamescope's gaming mode shows a corrupted picture on NVIDIA graphics cards: NVIDIA's open bug 5240452 (flicker
and artifacts in the DRM session at modes above 2560x1440@120, seen up to driver 595 and on RTX 3070 Ti, 4090, 5090 and 5080; also
[gamescope#2309](https://github.com/ValveSoftware/gamescope/issues/2309)). The kernel parameters `nvidia-drm.modeset=1`/`fbdev=1`
and loading the NVIDIA modules early don't change it, and gamescope nested inside KDE artifacts too.
**The fix.** gamescope with GBM-allocated scan-out buffers, opt-in through the environment variable
`gamescope_drm_gbm_scanout=1`. The patch is nine commits of [farnoy/gamescope](https://github.com/farnoy/gamescope)
(`nvidia-gbm-wayland`). A second small patch keeps Steam's UI server (Steam with `-steamos3` asks gamescope for 1920x1080) at the
display's size. Both are built into one package, `steamify-gamescope-nvidia` (it provides and conflicts with `gamescope`), in
[theupriser/steamify-gamescope-nvidia](https://github.com/theupriser/steamify-gamescope-nvidia): the patches, the PKGBUILD, and CI.
Tested on an RTX 5080 (driver 615.78.08, 3440x1440@175): clean picture, widescreen and sharp Steam UI, Proton games start.
Everything below is `lib/gamescope-nvidia.sh` unless named otherwise.
**What the SteamOS conversion does on an NVIDIA PC** (`gaming_enable` calls `gsn_enable`; `nvidia_present`: PCI vendor 0x10de,
display class, its driver providing `nvidia_drm`; `nvidia_supported`: GTX 16 / RTX 20 series or newer, Turing and later):
1. `gsn_preflight`: no pacman lock, at least 1.5 GB free on `/`. A failure stops before anything changes.
2. `nvidia_swap_out`: turns "Gaming on NVIDIA" off if it was on (see below).
3. On a supported card the kernel parameters, early modules, suspend memory and 32-bit libraries (`nvidia_kernel_enable`,
`nvidia_extras_enable`, `lib/nvidia.sh`, described below).
4. `gsn_install_package`: the patched gamescope (next paragraph).
5. `gsn_env_enable`: `gamescope_drm_gbm_scanout=1` in `~/.config/gamescope-session/environment` and
`~/.config/environment.d/99-steamify-gamescope-nvidia.conf`.
6. `gsn_display_enable`: the display wrapper. `patches/gamescope-display-wrapper.sh` is installed as `gamescope` in Steamify's bin
folder and put first in the gaming session's PATH with a drop-in of `gamescope-session.service` (`steamify-display.conf`). It
starts gamescope on the mode KDE has saved for the connected display (`~/.config/kwinoutputconfig.json`, modes from
`modetest`), else the biggest mode at the highest refresh rate; with several displays connected it passes nothing and gamescope
chooses. `--print-args` shows what it would use.
7. `single_login_release` (`lib/single-user.sh`): single user mode's `zzz-steamify-autologin.conf` is read last and would pin the
Plasma session, so it goes (turning the conversion off sets it up again). `python_hid_shim_ensure` (`lib/packages.sh`): the
conversion's dependency jupiter-hw-support wants `python-hid`, which conflicts with `python-hidapi` (liquidctl needs it): an
empty package providing `python-hid` (`patches/python-hid-shim.PKGBUILD`) makes both work.
Turning the conversion off removes the setting, the wrapper and the kernel settings, and keeps the packages. When the setup
fails, `gsn_fail_mark` records it (state `gamescope_nvidia`, key `failed`) and sends a desktop notification; the menu reminds
(`gsn_fail_note`) until a setup works. A failed option's sub-options aren't applied.
**Getting the package, and keeping it current.** gamescope changes every few weeks, so the package is downloaded, not carried.
CI builds it in CachyOS's container, once per CPU level like CachyOS's repositories: generic, x86-64-v3, v4 and znver4, each with an
explicit `-march` (a `-march=native` build died with SIGILL on a Ryzen) and checked for instructions beyond its level and for both
patches. A release is `<gamescope version>-<package release>` (for example `3.16.31-3`) with `SHA256SUMS`. `gsn_cpu_variant`
picks the build for this CPU (the dynamic loader's x86-64 level; AMD with `avx512_bf16` is znver4) and generic is the fallback.
`gsn_fetch_package` tries, for the CPU's variant and then generic: the newest release on GitHub (if newer than the pin), the release
Steamify was tested with (`GSN_VERSION`, hashes in `GSN_SHA256`: from GitHub, the cache, or the copy the ISO leaves in
`/usr/local/share/steamify/gamescope`); only files with the expected sha256 count (newer releases: their `.sha256`). If nothing is
reachable, `gsn_build_package` builds from `patches/gamescope-nvidia.PKGBUILD` and the two patches (`makepkg` as the user in
`~/.cache/steamify`). `gsn_install_package` installs with `pacman -U --ask 4`; `gsn_smoke_test` then starts gamescope headless
and installs the generic build instead if it doesn't start. Nothing else changes before a package is installed. A newer release than
the installed package shows the conversion as an update (`gsn_update_pending`, looked up at most every six hours, quiet offline).
A new pin = the release's tag and each variant's sha256 in `GSN_VERSION` and `GSN_SHA256` plus the PKGBUILD's pkgver/pkgrel; the patches in `patches/` must
stay identical to that repository's. New gamescope versions: the repository's daily check bumps the version when the patch still
applies, or opens an issue when it needs a rebase; a person merges and tags the release.
**What the kernel side does** (`lib/nvidia.sh`; a supported card only): `nvidia-drm.modeset=1 nvidia-drm.fbdev=1` on the kernel command
line (Limine, systemd-boot or GRUB; the file is backed up first; on Limine into the existing `KERNEL_CMDLINE[default]="..."` or
`+="..."` line, never an extra appended line, which ends up as text), and `/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf`
(`MODULES+=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)`), then the initramfs and boot entries are rebuilt and checked. The
early-load drop-in is only there while every installed kernel has the NVIDIA modules (mkinitcpio fails on a missing module): a
pacman hook (`/etc/pacman.d/hooks/85-steamify-nvidia-initramfs.hook`) runs `/usr/local/libexec/steamify-nvidia-initramfs` at every
kernel or driver change and decides again. `nvidia_extras_enable` adds `NVreg_PreserveVideoMemoryAllocations=1`
(`/etc/modprobe.d/90-steamify-nvidia.conf`), enables `nvidia-suspend`/`resume`/`hibernate` and installs `lib32-nvidia-utils` and
`lib32-libxkbcommon`. Takes effect at the next boot. The tests are in steamify-cachyos-dev: `tests/nvidia-test.sh` (a fake GPU, a
stub `pacman` and `systemctl`, a temp home) and `tests/nvidia-hardware-test.sh` (a real NVIDIA PC).
**Limits.** Tested on one PC (RTX 5080, Ryzen). The "Add as non-Steam game" option isn't offered on NVIDIA PCs yet. Steam must keep
running with `-steamos3` (the Display settings' "Maximum Game Resolution" exists only with it, and dropping it or setting
`XWAYLAND_COUNT=1` stopped games from launching). If upstream fixes the bug, the patched package and this section go, and Steamify
goes back to the stock gamescope.
**"Gaming on NVIDIA" (before 2.12.0).** With an NVIDIA GPU the SteamOS conversion (`gaming`, and its options `boot`, `single`, `glyphs`) wasn't offered, unless
it was already on. `nvidia` ("Gaming on NVIDIA", `lib/nvidia.sh`) took its place (both would start Steam at login): it installs `steam` when missing (recorded in state `nvidia`, removed again only then) and enables the user
unit `steamify-steam-autostart.service` (`services/`, `ExecStart=/usr/bin/steam @ARGS@`, Plasma only). Its sub-option
`bigpicture` ("Steam starts in Big Picture", ticked along with it) rewrites the unit with `-gamepadui` and sets
`ksmserverrc [General] loginMode=emptySession` (`kset`, undone with `krevert bigpicture`), so windows of the last session
(Discord, a browser) aren't restored on top of Big Picture; unticked, Steam starts in its normal window. The opt-in sub-option `nvsilent` ("Start Steam silently") rewrites the unit with `-silent`
(tray only); it and `bigpicture` exclude each other. The PC always boots into the desktop: there is no gamescope session. Takes effect at the next
login.
On a supported card (RTX 20 series or newer: not on chwd's legacy lists `/var/lib/chwd/ids/nvidia-*.ids`, the VRAM booster's
check, `vram_nvidia_legacy_id`; `nvidia_supported`) enabling it also does what NVIDIA's DRM stack wants, though it didn't cure
gamescope: `nvidia-drm.modeset=1 nvidia-drm.fbdev=1` on the kernel command line (Limine, systemd-boot or GRUB; the file is
backed up first; on Limine into the existing `KERNEL_CMDLINE[default]="..."` or `+="..."` line, never an extra appended line,
which ends up as text; the kernel treats `nvidia_drm` and `nvidia-drm` alike) and
`/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf` (`MODULES+=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)`), then the initramfs
and boot entries are rebuilt and checked for the parameters. The early-load drop-in is only there while every installed kernel
has the NVIDIA modules (mkinitcpio fails on a missing module, and `limine-mkinitcpio` then skips that kernel's boot entry):
a pacman hook (`/etc/pacman.d/hooks/85-steamify-nvidia-initramfs.hook`, after DKMS, before the initramfs is built) runs
`/usr/local/libexec/steamify-nvidia-initramfs` at every kernel or driver change and decides again. Turning it off removes
all of it. The tests are in steamify-cachyos-dev: `tests/nvidia-test.sh` (a fake GPU, a stub `pacman` and `systemctl`, a temp home) and `tests/nvidia-hardware-test.sh` (a real NVIDIA PC).
Tried and dropped (notes in steamify-cachyos-dev, `nvidia/HARDWARE-RESULTS.md`): a gamescope session that opens Big Picture in Plasma (it needed the `start-gamescope-session`
command shadowed in `/usr/local/bin`, and showed the desktop for a few seconds, then a Steam window with a black border);
a KWin session without Plasma (the mouse stuttered every second or two).
## HDMI-CEC
The **HDMI-CEC** item (on every PC; ticked by default only on a Steam
@@ -180,7 +334,9 @@ SHA-256 in Valve's package index:
- `cecd`, Valve's CEC daemon (a user service started with the desktop and
gaming mode): TV remote keys become normal key presses (arrows, Enter,
Back), and it turns the TV on and off with the PC;
- `cec-audio-control`: volume of the TV or receiver;
- `cec-audio-control`: volume of the TV or receiver (the TV remote's volume
keys); its user socket is enabled by Steamify, since SteamOS enables it
through a preset that pacman doesn't apply;
- `inputattach-cec-units` (with `linuxconsole` from CachyOS for
`inputattach`): attaches USB CEC adapters (Pulse-Eight, RainShadow).
@@ -249,8 +405,7 @@ desktop through its desktop layout (A = Enter, B = Esc, Y = Space, X =
Steam's keyboard), in gaming mode as a gamepad.
Steam reads the list only at startup and writes it back on exit, so Steam is
closed (`steam -shutdown`) for the edit and started again (through
`steam-desktop-autostart.service` when it's there). That's refused in gaming
closed (`steam -shutdown`) for the edit and started again (`steam -silent`). That's refused in gaming
mode and when Steamify itself was started from Steam (`SteamGameId`), since
closing Steam would end it. An entry for the start script that's already
there (added by hand, or with the path from before 2.5.0) is updated, not
@@ -395,7 +550,8 @@ In the home folder, everything is under `steamify`:
| `~/.local/share/steamify/bin/` | The shortcut's start scripts (`run-app`, `run-wizard`) and `steamify-notifier` |
| `~/.local/share/icons/hicolor/scalable/apps/steamify.svg` | The shortcut's icon |
| `~/.local/share/applications/steamify-*.desktop` | The launcher entries |
| `~/.config/systemd/user/` | `steamify-update-check.*`, `steam-desktop-autostart.service` |
| `~/.config/autostart/steam.desktop` | the `silent` option (Steam's "Run Steam when my computer starts") |
| `~/.config/systemd/user/` | `steamify-update-check.*` |
System-wide: `/usr/local/lib/steamify/` (kernel headers script),
`/usr/src/steamify-*` (DKMS modules), `/var/cache/steamify/kernel` (the old
@@ -418,6 +574,33 @@ steamos-session-select persistent # remember the last-used session across reboo
steamos-session-select oneshot # always start in gamescope (default, like a Deck)
```
`steamify.sh --boot gamescope|desktop` sets where the PC starts after a
restart (the menu's Boot into row) from a script: it needs the SteamOS
conversion and changes nothing else, not even the updates a normal run would
apply. Exit code 1 without the conversion or with another value.
## Install-time mode (`--defaults`)
For an installer (the Steam Machine ISO), without a session or prompts
(passwordless sudo required):
```bash
steamify.sh --defaults # what the menu would preselect
steamify.sh --defaults --options gaming,theme,cec,machine --boot desktop
steamify.sh --defaults --list # what --defaults can set up here, as JSON
```
`--options` turns on exactly the listed items (the menu's ids: `gaming`,
`theme`, `glyphs`, `single`, `launcher`, `notify`, `vram`, `cec`, `machine`,
`poweroff`), everything else off; an item that's on brings its parent
(single user the conversion, the power-off fix Steam Machine support). An
item this PC can't use (e.g. `vram` without a VRAM region) is left off with
a warning. `--boot desktop` needs `gaming`. Unknown options, ids, `boot` or
`bios` in the list: exit code 1 before anything changes. What needs a
Plasma session waits for the first login (`lib/first-login.sh`). The same step (and the online theme path) replaces Plasma's default taskbar pins, which include Discover
(not installed on CachyOS: a blank icon), by CachyOS's own (`taskbar_drop_discover` in `lib/vapor-theme.sh`; skipped when Discover is
installed or the taskbar already has its own `launchers`).
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.
@@ -446,11 +629,14 @@ immediately, which can turn into a loop - see
| `lib/steam-game.sh` | Add as non-Steam game: Steamify in the Steam library (`patches/steam-shortcuts.py`) |
| `lib/update-notifier.sh` | Update notifications: the notifier from `patches/` and its user timer |
| `lib/first-login.sh` | `--defaults` without a session (the Steam Machine ISO's installer): the one-time first-login step that sets up single user's launcher on Plasma's new layout and opens the app |
| `lib/controllers.sh` | Extended controller support: Xbox wireless dongle (xone) and Xbox Bluetooth (xpadneo) from the CachyOS repo |
| `lib/gamescope-nvidia.sh` | gamescope on NVIDIA: builds and installs the patched gamescope (GBM scan-out) from steamify-gamescope-nvidia and sets `gamescope_drm_gbm_scanout=1` for the SteamOS conversion |
| `lib/nvidia.sh` | Gaming on NVIDIA: Steam on the desktop, started at login, optionally in Big Picture (replaces the SteamOS conversion there) |
| `lib/vram-booster.sh` | VRAM booster (`dmemcg-booster`, `plasma-foreground-booster`) |
| `services/` | The systemd units the scripts install (`service_file`, `@KEY@` placeholders); see its README |
| `patches/` | Module sources and patches the scripts build or apply (`patch_file`); see its README |
| `.github/tools/bundle.sh` | Builds the single-file version (`dist/steamify.sh`), with `patches/` and `services/` embedded |
| `.github/workflows/bundle.yml` | Builds and checks it on every push; publishes it on `main` |
| `.github/workflows/bundle.yml` | Builds and checks it on every push (GitHub and a Gitea mirror); publishes it on `main`, and starts the Steamify ISO's release |
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
+8
View File
@@ -18,6 +18,14 @@ restarted right away (`Relogin=true`), which is why it can loop.
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.
## "Target OS name ... not found in /boot/limine.conf"
limine-snapper-sync finds the boot entries by the OS name. Steamify never
changes that name (it only adds version keys to `/etc/os-release`), and pins
`TARGET_OS_NAME="CachyOS"` in `/etc/default/limine`. If you see this message,
check that `PRETTY_NAME` in `/etc/os-release` still says `CachyOS`, or set
`TARGET_OS_NAME` in `/etc/default/limine` to the name your boot entries use.
## The LED bar on the Steam Machine stays dark
Restart once more; a freshly installed driver often needs a reboot. The menu
Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 KiB

+21 -3
View File
@@ -50,10 +50,11 @@ backend_status() {
items+="${items:+,}{\"id\":$(json_str "$c"),\"label\":$(json_str "${LABEL[$c]%%:*}")"
items+=",\"hint\":$(json_str "$( [[ "${LABEL[$c]}" == *:* ]] && echo "${LABEL[$c]#*: }")")"
items+=",\"kind\":\"$kind\",\"parent\":$(json_str "$parent")"
items+=",\"needs\":\"\""
items+=",\"on\":$( [[ "$now" == 1 ]] && echo true || echo false)"
items+=",\"wanted\":$( [[ "${WANTED[$c]:-0}" == 1 ]] && echo true || echo false)"
items+=",\"update\":$(feature_outdated "$c" && echo true || echo false)"
items+=",\"new\":$(feature_new "$c" && echo true || echo false)"
items+=",\"new\":$({ feature_new "$c" || feature_new_optin "$c"; } && echo true || echo false)"
items+=",\"selectable\":$(component_selectable "$c" && echo true || echo false)"
# Why it can't be turned on, for the app's explanation.
[[ "$c" == vram && -n "${VRAM_NVIDIA_CASE:-}" ]] &&
@@ -146,8 +147,18 @@ backend_apply() {
for id in "${want[@]}"; do [[ "$id" == "$c" ]] && WANTED[$c]=1; done
done
# The same rules as the menu's toggles.
[[ "${WANTED[single]:-0}" == 1 ]] && WANTED[gaming]=1
[[ "${WANTED[gaming]:-0}" == 0 ]] && { WANTED[single]=0; WANTED[boot]=0; }
[[ "${WANTED[single]:-0}" == 1 ]] && component_available gaming && WANTED[gaming]=1
if component_available gaming; then
[[ "${WANTED[gaming]:-0}" == 0 ]] && { WANTED[single]=0; WANTED[boot]=0; }
else
WANTED[boot]=0
fi
[[ "${WANTED[bigpicture]:-0}" == 1 ]] && WANTED[nvidia]=1
[[ "${WANTED[nvsilent]:-0}" == 1 ]] && WANTED[nvidia]=1
[[ "${WANTED[bigpicture]:-0}" == 1 ]] && WANTED[nvsilent]=0
[[ "${WANTED[nvidia]:-0}" == 0 ]] && { WANTED[bigpicture]=0; WANTED[nvsilent]=0; }
[[ "${WANTED[silent]:-0}" == 1 || "${WANTED[glyphs]:-0}" == 1 ]] && component_available gaming && WANTED[gaming]=1
[[ "${WANTED[gaming]:-0}" == 0 ]] && { WANTED[silent]=0; WANTED[glyphs]=0; }
[[ "${WANTED[poweroff]:-0}" == 1 ]] && WANTED[machine]=1
[[ "${WANTED[machine]:-0}" == 0 ]] && WANTED[poweroff]=0
[[ "${WANTED[kpin]:-0}" == 1 ]] && WANTED[machine]=1
@@ -174,9 +185,16 @@ backend_apply() {
if backend_run_component "$c" disable; then feature_record "$c" disable; else failed+=("$c"); fi
done
for c in "${TO_ENABLE[@]}"; do
# A sub-option of something that just failed isn't applied: it would be left on without its parent.
if [[ -n "${PARENT[$c]:-}" && " ${failed[*]:-} " == *" ${PARENT[$c]} "* ]]; then
backend_event log "\"id\":$(json_str "$c"),\"line\":$(json_str "Skipped: ${LABEL[${PARENT[$c]}]%%:*} didn't work.")"
failed+=("$c")
continue
fi
if backend_run_component "$c" enable; then is_action "$c" || feature_record "$c" enable; else failed+=("$c"); fi
done
feature_record_unticked
os_version_refresh
[[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " =~ \ (gaming|single|boot|kpin)\ ]] && RESTART_FOR_LOGIN=true
backend_event finished "\"failed\":$(json_list "${failed[@]}"),\"restart\":$(restart_needed && echo true || echo false)"
}
+25 -6
View File
@@ -26,6 +26,10 @@ CEC_DKMS_VER=1
CEC_DKMS_SRC="/usr/src/$CEC_DKMS_NAME-$CEC_DKMS_VER"
CEC_DRIVER_URL="https://raw.githubusercontent.com/evlaV/linux-integration/10c8c8800ccd3ae359203b4eefb6479f613b3b8e/drivers/media/cec/platform/cros-ec/cros-ec-cec.c"
CEC_DRIVER_SHA256=e89fd4e87fceb32d713ffc59b64794779b3c2b3c012b0e2f2e9f0c40b42f4373
# Downloaded once, then kept: raw.githubusercontent.com rate-limits (HTTP 429) repeated downloads
# from one address, and turning CEC off and on again needs no network then. Named after its
# checksum, so a new pin (a new commit and checksum above) downloads its file once by itself.
CEC_DRIVER_CACHE="/var/cache/steamify/cros-ec-cec-${CEC_DRIVER_SHA256:0:12}.c"
cec_link_steamos_manager() {
# On a Steam Machine: steamos-manager writes cecd's config from Steam's
@@ -65,12 +69,24 @@ cec_driver_enable() {
fi
install_kernel_headers || return 1
tmp="$(mktemp -d)"
info "Downloading Valve's Steam Machine CEC driver..."
if ! curl -fsL "$CEC_DRIVER_URL" -o "$tmp/cros-ec-cec.c" ||
! echo "$CEC_DRIVER_SHA256 $tmp/cros-ec-cec.c" | sha256sum -c --quiet -; then
rm -rf "$tmp"
err "Downloading Valve's CEC driver failed (or its checksum didn't match)."
return 1
if [[ -f "$CEC_DRIVER_CACHE" ]] && echo "$CEC_DRIVER_SHA256 $CEC_DRIVER_CACHE" | sha256sum -c --quiet - 2>/dev/null; then
cp "$CEC_DRIVER_CACHE" "$tmp/cros-ec-cec.c"
else
info "Downloading Valve's Steam Machine CEC driver..."
if ! curl -fsL --retry 2 --max-time 60 "$CEC_DRIVER_URL" -o "$tmp/cros-ec-cec.c" ||
! echo "$CEC_DRIVER_SHA256 $tmp/cros-ec-cec.c" | sha256sum -c --quiet -; then
# GitHub is down, rate-limits us, or sent something else: Steamify carries the same file (checked the same way).
warn "Downloading Valve's CEC driver failed (or its checksum didn't match): using the copy Steamify carries."
patch_file cros-ec-cec.c > "$tmp/cros-ec-cec.c"
if ! echo "$CEC_DRIVER_SHA256 $tmp/cros-ec-cec.c" | sha256sum -c --quiet -; then
rm -rf "$tmp"
err "Steamify's own copy of Valve's CEC driver doesn't match the pinned checksum (a new pin needs a new copy in patches/)."
return 1
fi
fi
# The name carries the checksum: a newer pin gets its own file, the old one goes.
sudo find "${CEC_DRIVER_CACHE%/*}" -maxdepth 1 -name 'cros-ec-cec-*.c' ! -name "${CEC_DRIVER_CACHE##*/}" -delete 2>/dev/null
sudo install -Dm644 "$tmp/cros-ec-cec.c" "$CEC_DRIVER_CACHE"
fi
# So it finds amdgpu's HDMI port (see the patch for why).
patch_file cros-ec-cec-single-port.patch | patch -s -d "$tmp" -p1 ||
@@ -155,6 +171,9 @@ cec_enable() {
printf '%s\n' "# Written by Steamify: overrides STEAM_ENABLE_CEC=0 from the gaming mode script." \
'[Service]' "EnvironmentFile=-$CEC_STEAM_ENV" | sudo tee "$CEC_STEAM_DROPIN" >/dev/null
user_systemctl daemon-reload
# The TV remote's volume keys: SteamOS enables this socket through its
# preset, which pacman doesn't apply, so nothing else does on CachyOS.
user_systemctl enable --now cec-audio-control.socket 2>/dev/null
cec_link_steamos_manager
if compgen -G "/dev/cec*" >/dev/null; then
user_systemctl restart cecd.service 2>/dev/null
+43
View File
@@ -0,0 +1,43 @@
#!/bin/bash
# Extended controller support: the Xbox wireless dongle (xone, with its firmware) and Xbox controllers over Bluetooth (xpadneo).
# The kernel has no driver for the dongle, and without xpadneo a Bluetooth Xbox controller works as a plain gamepad (buttons and
# sticks) but misses rumble (trigger motors too), proper button mapping and the battery level. Both are DKMS modules from CachyOS's
# own repo, built for every installed kernel (so every kernel needs its headers). Sourced by steamify.sh; not meant to be run on
# its own.
controller_xone_pkg() {
# A PC with the AUR's xone-dkms-git already works: the repo's xone-dkms would conflict with it.
pacman -Q xone-dkms-git >/dev/null 2>&1 && echo xone-dkms-git || echo xone-dkms
}
controller_pkgs() { echo "xpadneo-dkms $(controller_xone_pkg) xone-dongle-firmware"; }
extended_controller_support_status() {
# shellcheck disable=SC2046 # a list of package names
pacman -Q $(controller_pkgs) >/dev/null 2>&1
}
extended_controller_support_enable() {
local p prev missing=()
info "Installing extended controller support (Xbox wireless dongle, Xbox Bluetooth)..."
install_kernel_headers || return 1
for p in $(controller_pkgs); do pacman -Q "$p" >/dev/null 2>&1 || missing+=("$p"); done
if ((${#missing[@]})); then
sudo pacman -S --needed --noconfirm "${missing[@]}" ||
{ err "Installing ${missing[*]} failed."; return 1; }
# What a first, half-finished run installed stays on the list.
prev="$(state_get extended_controller_support installed_pkgs)"
state_set extended_controller_support installed_pkgs "$(echo "$prev ${missing[*]}" | xargs)"
fi
ok "Extended controller support on: plug the dongle in again (or reconnect the controller) to use it."
}
extended_controller_support_disable() {
local pkgs
pkgs="$(state_get extended_controller_support installed_pkgs)"
# Only what Steamify installed; kernel headers stay.
# shellcheck disable=SC2086 # a list of package names
[[ -n "$pkgs" ]] && sudo pacman -Rns --noconfirm $pkgs >/dev/null 2>&1
state_clear extended_controller_support
ok "Extended controller support off."
}
+17 -6
View File
@@ -7,13 +7,25 @@
FIRST_LOGIN_ENTRY="${XDG_CONFIG_HOME:-$HOME/.config}/autostart/steamify-first-login.desktop"
FIRST_LOGIN_SCRIPT="$STEAMIFY_BIN/first-login"
FIRST_LOGIN_BUNDLE="$STEAMIFY_BIN/first-login-steamify.sh"
first_login_schedule() {
# The newest release's Steamify runs at the first login. STEAMIFY_NO_DOWNLOAD (the ISO was started with
# steamify.nodownload, to test an unreleased Steamify): this very bundle runs instead.
# (Only a single-file bundle can be copied; SCRIPT_DIR doesn't exist in one.)
local run self="${BASH_SOURCE[0]:-}"
if [[ -n "${STEAMIFY_NO_DOWNLOAD:-}" && -f "$self" ]] && grep -q '^# Single-file build' "$self"; then
install_executable "$FIRST_LOGIN_BUNDLE" 755 < "$self"
run="bash \"$FIRST_LOGIN_BUNDLE\" --first-login; rm -f \"$FIRST_LOGIN_BUNDLE\""
else
run="curl -fsSL --max-time 30 \"$WIZARD_URL\" | bash -s -- --first-login"
fi
install_executable "$FIRST_LOGIN_SCRIPT" 755 << EOF2
#!/bin/bash
# Installed by Steamify CachyOS: runs once, at the first desktop login.
set -o pipefail
curl -fsSL --max-time 30 "$WIZARD_URL" | bash -s -- --first-login
mkdir -p ~/.cache
{ $run; } > ~/.cache/steamify-first-login.log 2>&1
rm -f "$FIRST_LOGIN_ENTRY" "\$0"
[[ -x "$WIZARD_APP_LAUNCHER" ]] && exec "$WIZARD_APP_LAUNCHER"
EOF2
@@ -31,10 +43,9 @@ first_login_run() {
[[ -n "$(plasma_applets org.kde.plasma.kickoff)" ]] && break
sleep 2
done
if single_status; then
stop_plasmashell_for_edit
single_launcher
restart_plasmashell_if_stopped
fi
stop_plasmashell_for_edit
taskbar_drop_discover
single_status && single_launcher
restart_plasmashell_if_stopped
return 0
}
+308
View File
@@ -0,0 +1,308 @@
#!/bin/bash
# gamescope's own gaming mode on NVIDIA: a patched gamescope that allocates its scan-out buffers with GBM, which cures
# the corrupted picture (https://github.com/ValveSoftware/gamescope/issues/2309). The patch is opt-in: gamescope reads
# `gamescope_drm_gbm_scanout=1` from its environment, which is set for the gaming session (gamescope-session's
# environment file, and environment.d).
#
# gamescope changes every few weeks, so the package is not carried but downloaded: the newest release of
# https://github.com/theupriser/steamify-gamescope-nvidia (CI builds it in CachyOS's container, a person merges the
# version bump), and a newer one than the installed package shows as an update of the SteamOS conversion
# (`gsn_update_pending`). What this Steamify release was tested with is the fallback: the pinned package (sha256 below),
# a copy that came with the ISO, and last a build here from the recipe and patch in patches/. Simple files that don't change
# stay in patches/ and are used as they are.
#
# Used by the SteamOS conversion (`gaming_enable`/`gaming_disable`) on every NVIDIA PC (`nvidia_present`); the kernel
# parameters and the rest of lib/nvidia.sh's setup only on a supported card (`nvidia_supported`): GTX 16 / RTX 20 series or
# newer (Turing and later, what NVIDIA's open kernel driver supports). Tested on an RTX 5080 only.
# Sourced by steamify.sh; not meant to be run on its own.
GSN_PKG=steamify-gamescope-nvidia
GSN_RELEASES="${GSN_RELEASES:-https://github.com/theupriser/steamify-gamescope-nvidia/releases}"
# The package this Steamify release was tested with (patches/gamescope-nvidia.PKGBUILD names the same version): the
# fallback. A new pin: the release's tag, the package's sha256, and the PKGBUILD's pkgver/pkgrel.
GSN_VERSION=3.16.31-3
# One build per CPU level, like CachyOS's repositories: generic is the one every x86-64 PC can run.
declare -A GSN_SHA256=(
[generic]=4c7995694d2726c356785d9aec12a11c98419b4d2e139617f03bb46c758ced56
[v3]=43c6928ca9fa67c063c4e1b993b686145e94e68de1bd46645782d3186779fa2d
[v4]=b6d4621e4b98d9aa5c600fe9a5a1cae164fef6bbf93d259fb8a6af666b098f9f
[znver4]=60f45da74a0b96d776219f6e6aa22c5d23e48b08233b3fa12b6c4d7aa6812dd9
)
# Where the ISO leaves a copy of the pinned package, for installs without GitHub.
GSN_BACKUP_DIR="${GSN_BACKUP_DIR:-/usr/local/share/steamify/gamescope}"
# How long a look at GitHub for a newer release counts (seconds), so opening the menu doesn't ask every time.
GSN_CHECK_EVERY="${GSN_CHECK_EVERY:-21600}"
GSN_ENV_LINE='gamescope_drm_gbm_scanout=1'
GSN_ENV_D="${GSN_ENV_D:-$HOME/.config/environment.d/99-steamify-gamescope-nvidia.conf}"
GSN_SESSION_ENV="${GSN_SESSION_ENV:-$HOME/.config/gamescope-session/environment}"
gsn_package_installed() { pacman -Q "$GSN_PKG" >/dev/null 2>&1; }
gsn_installed_version() { pacman -Q "$GSN_PKG" 2>/dev/null | awk '{print $2}'; }
gsn_env_set() { [[ -f "$GSN_ENV_D" ]] && grep -qsx "$GSN_ENV_LINE" "$GSN_SESSION_ENV"; }
gsn_status() { gsn_package_installed && gsn_env_set; }
gsn_newer() {
# gsn_newer <a> <b>: version a is newer than b (pacman's own comparison).
[[ "$(vercmp "$1" "$2" 2>/dev/null)" == 1 ]]
}
gsn_latest_tag() {
# The newest release's tag (<gamescope version>-<package release>), from GitHub; nothing when it can't be reached or
# there is no release. Follows the redirect of /releases/latest: no API, no rate limit, no JSON. gsn_latest_tag [seconds]
local url
url="$(curl -fsL --connect-timeout 4 --max-time "${1:-10}" -o /dev/null -w '%{url_effective}' "$GSN_RELEASES/latest" 2>/dev/null)" || return 1
url="${url##*/}"
[[ "$url" =~ ^[0-9][0-9A-Za-z._]*-[0-9]+$ ]] || return 1
echo "$url"
}
gsn_update_pending() {
# The patched gamescope is installed and GitHub has a newer release: the menu shows the conversion as an update.
# Looked up at most every GSN_CHECK_EVERY seconds (the answer is kept in the state), quickly, and silently when
# offline: no network is never a reason to show or hide anything.
gsn_package_installed || return 1
local now checked latest
now="$(date +%s)"; checked="$(state_get gamescope_nvidia checked 0)"
if (( now - ${checked:-0} > GSN_CHECK_EVERY )); then
state_set gamescope_nvidia checked "$now"
latest="$(gsn_latest_tag 5)" && state_set gamescope_nvidia latest "$latest"
fi
latest="$(state_get gamescope_nvidia latest)"
[[ -n "$latest" ]] && gsn_newer "$latest" "$(gsn_installed_version)"
}
gsn_cpu_variant() {
# The best build this CPU can run: znver4 (Ryzen 7000/9000: AMD with AVX-512 BF16), v4, v3, else generic. The
# dynamic loader knows the CPU level best ("x86-64-v4 (supported, searched)").
local help; help="$(/lib64/ld-linux-x86-64.so.2 --help 2>/dev/null)"
if grep -q 'x86-64-v4 (supported' <<< "$help"; then
if grep -q AuthenticAMD /proc/cpuinfo && grep -qw avx512_bf16 /proc/cpuinfo; then echo znver4; else echo v4; fi
elif grep -q 'x86-64-v3 (supported' <<< "$help"; then echo v3
else echo generic; fi
}
gsn_sha_of() {
# The hash a release file is expected to have: the pinned one for the pinned version, else the .sha256 next to the
# package in the same release (it shows the download wasn't damaged, not who made it). gsn_sha_of <tag> <file> <variant>
local tag="$1" file="$2" variant="$3"
if [[ "$tag" == "$GSN_VERSION" ]]; then echo "${GSN_SHA256[$variant]}"; return 0; fi
curl -fsL --connect-timeout 10 --max-time 30 "$GSN_RELEASES/download/$tag/$file.sha256" 2>/dev/null | awk 'NR==1 {print $1}'
}
gsn_download() {
# gsn_download <tag> <variant>: the release's package, verified, in the cache; prints its path.
local tag="$1" variant="$2" cache="${XDG_CACHE_HOME:-$HOME/.cache}/steamify" file want got
file="$GSN_PKG-$tag-$variant.pkg.tar.zst"
mkdir -p "$cache" || return 1
want="$(gsn_sha_of "$tag" "$file" "$variant")"
[[ "$want" =~ ^[0-9a-f]{64}$ ]] || return 1
if [[ -f "$cache/$file" ]] && [[ "$(sha256sum "$cache/$file" | awk '{print $1}')" == "$want" ]]; then echo "$cache/$file"; return 0; fi
curl -fsL --retry 2 --connect-timeout 15 --max-time 300 -o "$cache/$file.part" "$GSN_RELEASES/download/$tag/$file" || { rm -f "$cache/$file.part"; return 1; }
got="$(sha256sum "$cache/$file.part" | awk '{print $1}')"
if [[ "$got" != "$want" ]]; then rm -f "$cache/$file.part"; return 1; fi
mv -f "$cache/$file.part" "$cache/$file"
echo "$cache/$file"
}
gsn_fetch_package() {
# The package to install, as a path: the build for this CPU, then the generic one, each from the newest release on
# GitHub (not older than the pinned one), else the pinned release (GitHub, the cache, or the copy that came with the
# ISO). Only files with the expected sha256 count. gsn_fetch_package [variant]
local tag pkg v cache="${XDG_CACHE_HOME:-$HOME/.cache}/steamify" file copy
local -a variants=("${1:-$(gsn_cpu_variant)}"); [[ "${variants[0]}" == generic ]] || variants+=(generic)
tag="$(gsn_latest_tag)" || tag=""
for v in "${variants[@]}"; do
if [[ -n "$tag" && "$tag" != "$GSN_VERSION" ]] && gsn_newer "$tag" "$GSN_VERSION"; then
info "Downloading the newest patched gamescope ($tag, $v)..." >&2
if pkg="$(gsn_download "$tag" "$v")"; then echo "$pkg"; return 0; fi
warn "Downloading the newest patched gamescope ($tag, $v) failed: trying the version this Steamify was tested with ($GSN_VERSION)." >&2
fi
info "Downloading the patched gamescope ($GSN_VERSION, $v)..." >&2
if pkg="$(gsn_download "$GSN_VERSION" "$v")"; then echo "$pkg"; return 0; fi
file="$GSN_PKG-$GSN_VERSION-$v.pkg.tar.zst"
for copy in "$GSN_BACKUP_DIR/$file" "$cache/$file"; do
if [[ -f "$copy" ]] && [[ "$(sha256sum "$copy" | awk '{print $1}')" == "${GSN_SHA256[$v]}" ]]; then
info "Using the copy that came with Steamify: $copy" >&2
echo "$copy"; return 0
fi
done
done
warn "Downloading the patched gamescope failed (or its checksum didn't match)." >&2
return 1
}
gsn_smoke_test() {
# The installed gamescope must start (headless, no screen needed): a build that crashes would leave gaming mode in a loop.
# It segfaults while shutting down headless (also unpatched builds), so what counts is that it ran the program.
timeout 25 gamescope --backend headless -W 640 -H 360 -r 60 -- true 2>&1 | grep -q 'Primary child shut down'
}
gsn_build_package() {
# Last resort: builds the pinned package from the recipe and patch Steamify carries (patches/), as the user. It
# downloads gamescope's sources (Valve's repository at the PKGBUILD's pkgver, and its submodules) and compiles them: a
# few minutes. Prints the package's path.
local dir cache="${XDG_CACHE_HOME:-$HOME/.cache}/steamify"
info "Building the patched gamescope here instead (a few minutes)..." >&2
sudo pacman -S --needed --noconfirm base-devel git >&2 || { err "Installing the build tools failed." >&2; return 1; }
# On disk, not /tmp: the installer's chroot has a tmpfs there (RAM), and the build is 1-2 GB.
mkdir -p "$cache" && dir="$(mktemp -d -p "$cache" gamescope-build.XXXXXX)" || return 1
patch_file gamescope-nvidia.PKGBUILD > "$dir/PKGBUILD" &&
patch_file gamescope-gbm-scanout.patch > "$dir/gamescope-gbm-scanout.patch" &&
patch_file gamescope-ui-native.patch > "$dir/gamescope-ui-native.patch" || {
err "Couldn't write the build files." >&2
rm -rf "$dir"; return 1
}
if ! (cd "$dir" && makepkg -sf --noconfirm --nocheck >&2); then
err "Building the patched gamescope failed (see above)." >&2
rm -rf "$dir"; return 1
fi
# Moved out, so the build folder can go.
local f built=""
for f in "$dir"/"$GSN_PKG"-*.pkg.tar.*; do
[[ -e "$f" && "$f" != *-debug-* ]] && { built="$f"; break; }
done
[[ -n "$built" ]] && mv "$built" "$cache/gamescope-local-build.pkg.tar.zst" || { rm -rf "$dir"; return 1; }
rm -rf "$dir"
echo "$cache/gamescope-local-build.pkg.tar.zst"
}
gsn_install_package() {
# The patched gamescope, newest first, each step only when the one before failed: the newest release from GitHub, the
# pinned package (GitHub, cache or the copy that came with the ISO), a build here. A package that downloaded fine but
# doesn't install (its libraries differ from this PC's) goes on to the next step too. Nothing else changes before one of
# them is installed, so a failure leaves the PC as it was, with what it had. --ask 4 lets pacman replace the stock
# gamescope it conflicts with, and an older patched one.
local pkg latest have
have="$(gsn_installed_version)"
latest="$(gsn_latest_tag)" || latest=""
# Already the newest (or, offline, the pinned version or newer): nothing to do.
if [[ -n "$have" ]]; then
if [[ -n "$latest" ]] && ! gsn_newer "$latest" "$have"; then return 0; fi
if [[ -z "$latest" ]] && ! gsn_newer "$GSN_VERSION" "$have"; then return 0; fi
fi
if pkg="$(gsn_fetch_package)" && sudo pacman -U --noconfirm --ask 4 "$pkg"; then
if ! gsn_smoke_test; then
warn "The patched gamescope for this CPU doesn't start: installing the generic build instead."
pkg="$(gsn_fetch_package generic)" && sudo pacman -U --noconfirm --ask 4 "$pkg" && gsn_smoke_test ||
{ err "The patched gamescope doesn't start on this PC."; return 1; }
fi
ok "Patched gamescope installed."
return 0
fi
warn "The ready-made patched gamescope couldn't be installed."
[[ -n "$have" ]] && { warn "The installed one ($have) stays."; return 0; }
if pkg="$(gsn_build_package)" && sudo pacman -U --noconfirm --ask 4 "$pkg"; then
ok "Patched gamescope built and installed."
return 0
fi
err "The patched gamescope couldn't be installed. The stock gamescope stays."
return 1
}
gsn_env_enable() {
mkdir -p "$(dirname "$GSN_ENV_D")" "$(dirname "$GSN_SESSION_ENV")"
echo "$GSN_ENV_LINE" > "$GSN_ENV_D"
touch "$GSN_SESSION_ENV"
grep -qsx "$GSN_ENV_LINE" "$GSN_SESSION_ENV" || echo "$GSN_ENV_LINE" >> "$GSN_SESSION_ENV"
}
gsn_env_disable() {
rm -f "$GSN_ENV_D"
[[ -f "$GSN_SESSION_ENV" ]] && sed -i "/^${GSN_ENV_LINE}\$/d" "$GSN_SESSION_ENV"
return 0
}
# --- The session starts gamescope on the display's own mode
#
# gamescope-session starts gamescope without a size or a refresh rate, so it takes the display's *preferred* mode, which
# many monitors declare as 60 Hz while the desktop runs them at their highest. patches/gamescope-display-wrapper.sh (put
# first in the gaming session's PATH by a drop-in of gamescope-session.service) starts it on the mode KDE has saved for
# the connected display, else the biggest mode at the highest refresh rate, read at every session start.
GSN_DISPLAY_WRAPPER="${GSN_DISPLAY_WRAPPER:-$STEAMIFY_BIN/gamescope}"
GSN_DISPLAY_DROPIN="${GSN_DISPLAY_DROPIN:-$HOME/.config/systemd/user/gamescope-session.service.d/steamify-display.conf}"
gsn_display_status() { [[ -x "$GSN_DISPLAY_WRAPPER" && -f "$GSN_DISPLAY_DROPIN" ]]; }
gsn_display_pending() {
# The patched gamescope is in use but the session still starts it on the display's preferred mode: a normal run sets it up.
gsn_package_installed && gsn_env_set && ! gsn_display_status
}
gsn_display_enable() {
patch_file gamescope-display-wrapper.sh | install_executable "$GSN_DISPLAY_WRAPPER" 755 || return 1
mkdir -p "$(dirname "$GSN_DISPLAY_DROPIN")"
cat > "$GSN_DISPLAY_DROPIN" << EOF
# Steamify: the gaming session starts gamescope on the display's own mode (see lib/gamescope-nvidia.sh). Turning the
# SteamOS conversion off removes this file.
[Service]
ExecStart=
ExecStart=/usr/bin/bash -c 'PATH="$(dirname "$GSN_DISPLAY_WRAPPER"):\$PATH" exec /usr/lib/steamos/gamescope-session'
EOF
user_systemctl daemon-reload
}
gsn_display_disable() {
# Only our own files: the wrapper is recognised by its first comment line.
grep -qs "^# Steamify: gamescope-session starts gamescope" "$GSN_DISPLAY_WRAPPER" && rm -f "$GSN_DISPLAY_WRAPPER"
[[ -f "$GSN_DISPLAY_DROPIN" ]] && rm -f "$GSN_DISPLAY_DROPIN" && rmdir "$(dirname "$GSN_DISPLAY_DROPIN")" 2>/dev/null
user_systemctl daemon-reload
return 0
}
gsn_preflight() {
# Before anything changes: what would make the install fail half way. Prints the reason and fails, or passes.
if [[ -e /var/lib/pacman/db.lck ]]; then
echo "another package manager is running (/var/lib/pacman/db.lck): finish or close it and try again"; return 1
fi
local free; free="$(df --output=avail -BM / 2>/dev/null | tail -1 | tr -dc 0-9)"
if [[ -n "$free" ]] && (( free < 1500 )); then
echo "less than 1.5 GB free on /: free some space and try again"; return 1
fi
return 0
}
gsn_fail_mark() {
# Remember a failed setup so the menu says so on the next start, and tell the user now (desktop notification).
state_set gamescope_nvidia failed "$(date '+%Y-%m-%d %H:%M'): $1"
command -v notify-send >/dev/null 2>&1 &&
notify-send -a Steamify -i dialog-warning "Steamify: gaming mode setup failed" "$1. Open Steamify and apply again." 2>/dev/null || true
}
gsn_fail_note() {
# The menu's reminder of a failed setup (nothing when the last one worked).
local f; f="$(state_get gamescope_nvidia failed)"
[[ -n "$f" ]] && warn "The last gaming mode setup for NVIDIA failed ($f). Apply the conversion again to retry."
return 0
}
gsn_enable() {
# Only on a supported NVIDIA card; elsewhere the stock gamescope is right.
nvidia_present || return 0
local why
if ! why="$(gsn_preflight)"; then err "Gaming mode for NVIDIA can't be set up: $why."; gsn_fail_mark "$why"; return 1; fi
# A PC that had "Gaming on NVIDIA" on: Steam must not also start on the desktop.
nvidia_swap_out
# What NVIDIA's DRM stack wants (lib/nvidia.sh; a supported card only, the functions check): modeset/fbdev on the
# kernel command line, the modules in the initramfs, video memory kept over suspend, the 32-bit libraries. Takes
# effect at the next boot, like the session.
nvidia_kernel_enable || return 1
nvidia_extras_enable || return 1
gsn_install_package || { gsn_fail_mark "the patched gamescope couldn't be installed"; return 1; }
state_set gamescope_nvidia failed ""
gsn_env_enable
gsn_display_enable || warn "Couldn't set up the display mode for gaming mode: gamescope takes the display's preferred mode."
ok "gamescope draws NVIDIA's picture through GBM (gamescope_drm_gbm_scanout=1) from the next gaming mode start."
}
gsn_disable() {
# The setting goes, and what nvidia_*_enable added; the packages stay (patched gamescope, 32-bit libraries), like
# the other packages of the conversion (steam, gamescope-session-cachyos).
gsn_env_disable
gsn_display_disable
nvidia_present || return 0
nvidia_extras_disable
nvidia_kernel_disable
}
+97 -10
View File
@@ -18,10 +18,84 @@ gaming_status() {
esac
}
# What Steam shows in its System settings besides the OS name (which stays
# CachyOS's, for legal clarity and because limine-snapper-sync finds the boot
# entries by it): the SteamOS release as the OS version, steam-machine as the
# codename, Steamify with its version as the variant (os-release VERSION_ID,
# VERSION_CODENAME, VARIANT_ID; see patches/steamify-os-release.sh).
OS_NAME_SCRIPT=/usr/local/libexec/steamify-os-release
OS_NAME_HOOK=/etc/pacman.d/hooks/zz-steamify-os-release.hook
# limine's tools take the OS name from TARGET_OS_NAME, else os-release's
# PRETTY_NAME: pinned to CachyOS, the boot entries never depend on os-release.
LIMINE_DEFAULTS=/etc/default/limine
steamos_release() {
# The SteamOS release Steamify follows: Valve's newest jupiter-X.Y repo
# (where the SteamOS extras come from), as an os-release ID (no spaces or
# capitals: steamos-3.9). Offline (e.g. in the installer): the one set
# before, if any.
local v
v="$(curl -fsL --max-time 15 "$VALVE_MIRROR/" 2>/dev/null | grep -oE 'jupiter-[0-9]+\.[0-9]+/' | tr -d / | sort -V | tail -n 1)"
if [[ -n "$v" ]]; then echo "steamos-${v#jupiter-}"
else sed -n 's/^VERSION_ID=\(steamos-.*\)$/\1/p' /etc/os-release 2>/dev/null; fi
}
os_name_enable() {
# 2.9.0 pre-releases renamed the OS itself; the name stays CachyOS's.
if grep -q '^DISTRIB_DESCRIPTION=.*with Steamify' /etc/lsb-release 2>/dev/null &&
[[ -x /usr/share/libalpm/scripts/cachyos-branding ]]; then
sudo /usr/share/libalpm/scripts/cachyos-branding lsb-release
fi
[[ -n "$(kreadconfig6 --file kcm-about-distrorc --group General --key Name 2>/dev/null)" ]] &&
kset gaming kcm-about-distrorc General Name --delete
# About this System: os-release's VERSION (CachyOS has none) instead of
# VERSION_ID, which is the SteamOS release now.
kset gaming kcm-about-distrorc General UseOSReleaseVersion true
patch_file steamify-os-release.sh | fill VERSION="$VERSION" STEAMOS="$(steamos_release)" |
sudo install -Dm755 /dev/stdin "$OS_NAME_SCRIPT" &&
patch_file steamify-os-release.hook | fill SCRIPT="$OS_NAME_SCRIPT" |
sudo install -Dm644 /dev/stdin "$OS_NAME_HOOK" &&
sudo "$OS_NAME_SCRIPT" || { err "Setting Steamify's version for Steam failed."; return 1; }
if [[ -f "$LIMINE_DEFAULTS" ]] && ! grep -q '^TARGET_OS_NAME=' "$LIMINE_DEFAULTS"; then
backup_file "$LIMINE_DEFAULTS"
printf '%s\n' 'TARGET_OS_NAME="CachyOS"' | sudo tee -a "$LIMINE_DEFAULTS" > /dev/null
state_set gaming limine_target 1
fi
return 0
}
os_name_disable() {
sudo rm -f "$OS_NAME_HOOK" "$OS_NAME_SCRIPT"
[[ -f /etc/os-release ]] && sudo sed -i -e '/^VARIANT=/d' -e '/^VARIANT_ID=/d' -e '/^VERSION_ID=/d' -e '/^VERSION_CODENAME=/d' /etc/os-release
# CachyOS's own values back, the way its hooks write them.
if [[ -x /usr/share/libalpm/scripts/cachyos-branding ]]; then
sudo /usr/share/libalpm/scripts/cachyos-branding os-release
# Undoes 2.9.0 pre-releases, which changed lsb-release's name too.
sudo /usr/share/libalpm/scripts/cachyos-branding lsb-release
fi
if [[ -n "$(state_get gaming limine_target)" ]]; then
sudo sed -i '/^TARGET_OS_NAME="CachyOS"$/d' "$LIMINE_DEFAULTS"
state_set gaming limine_target ""
fi
return 0
}
os_version_refresh() {
# After every run that changed something (sudo is at hand then): Steam's
# OS version follows Steamify's, not just the one that set it up. Silent:
# the app reads the backend's stdout as JSON.
gaming_status 2>/dev/null || return 0
grep -qx "VARIANT_ID=steamify-$VERSION" /etc/os-release 2>/dev/null && return 0
os_name_enable > /dev/null 2>&1
return 0
}
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
# NVIDIA: gamescope's picture is corrupted unless it is the patched one (no-op elsewhere).
gsn_enable || return 1
case "$LOGIN_MANAGER" in
sddm)
switch_to_sddm || return 1
@@ -35,8 +109,11 @@ gaming_enable() {
install_session_sync
;;
esac
# (NVIDIA with single user mode from before the conversion: its desktop autologin would pin the Plasma session.)
single_login_release
create_desktop_shortcut
steam_enable
os_name_enable || return 1
}
gaming_disable() {
@@ -49,6 +126,11 @@ gaming_disable() {
switch_to_plasmalogin
remove_desktop_shortcut
steam_disable
gsn_disable
krevert gaming
os_name_disable
# NVIDIA with single user mode still on: nothing else logs in by itself any more.
nvidia_present && single_status && single_login_enable
ok "The PC boots to the normal login screen again (from the next boot)."
}
@@ -92,6 +174,20 @@ switch_to_sddm() {
ok "SDDM is the login manager from the next boot on."
}
remove_sddm_base_autologin() {
# /etc/sddm.conf is read last: an [Autologin] there would override our fragments.
if [[ -f /etc/sddm.conf ]] && grep -q '^\[Autologin\]' /etc/sddm.conf; then
backup_file /etc/sddm.conf
sudo awk '
/^\[Autologin\]/ { skip=1; next }
/^\[/ { skip=0 }
!skip { print }
' /etc/sddm.conf | sudo tee /etc/sddm.conf.tmp > /dev/null &&
sudo mv /etc/sddm.conf.tmp /etc/sddm.conf
info "Removed [Autologin] from /etc/sddm.conf (backup kept)."
fi
}
configure_sddm_autologin() {
# User= and Relogin= live in our own fragment; steam-set-session keeps
# Session= up to date in zz-steamos-autologin.conf, which sorts later
@@ -107,16 +203,7 @@ Session=gamescope-session.desktop
Relogin=true
EOF
if [[ -f /etc/sddm.conf ]] && grep -q '^\[Autologin\]' /etc/sddm.conf; then
backup_file /etc/sddm.conf
sudo awk '
/^\[Autologin\]/ { skip=1; next }
/^\[/ { skip=0 }
!skip { print }
' /etc/sddm.conf | sudo tee /etc/sddm.conf.tmp > /dev/null &&
sudo mv /etc/sddm.conf.tmp /etc/sddm.conf
info "Removed [Autologin] from /etc/sddm.conf (backup kept)."
fi
remove_sddm_base_autologin
# Start in gamescope, through CachyOS's own tool so its file is current.
sudo /usr/lib/steamos/steam-set-session gamescope-session.desktop
+157 -16
View File
@@ -5,18 +5,19 @@
# Menu order. Components are turned on in this order and off in reverse;
# gaming must come first (single user builds on it).
COMPONENTS=(gaming boot theme glyphs single launcher steamgame notify vram cec machine poweroff kpin hdmi bios)
COMPONENTS=(gaming boot silent glyphs nvidia bigpicture nvsilent theme single launcher steamgame notify extended_controller_support vram cec machine poweroff kpin hdmi bios)
# One-off actions rather than on/off components: never preselected, never
# re-applied, not listed as on or off.
ACTIONS=(bios)
# Sub-options, shown indented under their parent and only while it's ticked.
declare -A PARENT=([boot]=gaming [steamgame]=launcher [poweroff]=machine [kpin]=machine [hdmi]=machine [bios]=machine)
declare -A PARENT=([boot]=gaming [silent]=gaming [glyphs]=gaming [bigpicture]=nvidia [nvsilent]=nvidia [steamgame]=launcher [poweroff]=machine [kpin]=machine [hdmi]=machine [bios]=machine)
# Never preselected on a first run: booting into the desktop is a choice,
# gamescope is the default; HDMI-CEC is opt-in (it can wake the machine or
# upset other devices on the TV, even on SteamOS), except on a Steam Machine,
# which has CEC like on SteamOS. HDMI refresh boost needs someone at the
# screen to confirm each step.
NO_PRESELECT=(boot cec kpin hdmi)
# screen to confirm each step. Extended controller support builds kernel modules for
# every installed kernel: opt-in.
NO_PRESELECT=(boot silent nvsilent cec kpin hdmi extended_controller_support)
# Feature versions: the Steamify version in which what a component's enable
# sets up last changed; set it to the new VERSION whenever you change one.
# Each successful run records it (state "features"); a component that's on
@@ -25,20 +26,25 @@ NO_PRESELECT=(boot cec kpin hdmi)
# FEATURE_BASELINE.
FEATURE_BASELINE=2.1.0
declare -A FEATURE_VERSION=(
[gaming]=2.1.0 [boot]=2.1.0 [theme]=2.1.0 [glyphs]=2.1.0 [single]=2.1.0
[launcher]=2.1.0 [cec]=2.1.0 [machine]=2.2.0 [poweroff]=2.2.0 [vram]=2.3.0 [notify]=2.5.0 [steamgame]=2.5.1
[gaming]=2.9.0 [boot]=2.1.0 [silent]=2.11.0 [nvidia]=2.12.0 [bigpicture]=2.10.0 [nvsilent]=2.11.0 [theme]=2.1.0 [glyphs]=2.1.0 [single]=2.1.0
[launcher]=2.1.0 [cec]=2.7.0 [machine]=2.9.0 [poweroff]=2.2.0 [vram]=2.3.0 [notify]=2.5.0 [steamgame]=2.5.1 [extended_controller_support]=2.10.0
[kpin]=2.1.0 [hdmi]=2.1.0
)
declare -A LABEL=(
[gaming]="SteamOS conversion: boot into gaming mode, Steam on the desktop"
[boot]="Boot into the desktop instead of gaming mode"
[nvidia]="Gaming on NVIDIA: Steam on the desktop, started at login"
[silent]="Start Steam silently in desktop mode: in the tray at login on the desktop, no window"
[nvsilent]="Start Steam silently: in the tray at login, no window (untick: Steam's normal window)"
[bigpicture]="Steam starts in Big Picture: the controller-friendly Steam (untick: Steam's normal window)"
[theme]="Install SteamOS theme: Vapor look (cachyos-vapor)"
[glyphs]="Install Steam Deck/Machine icons: Deck button icons in gaming mode"
[single]="Single user mode: no password, lock screen or log out (SDDM)"
[launcher]="Steamify shortcut: the app on the desktop, Steamify Terminal in the launcher"
[steamgame]="Add as non-Steam game: Steamify in your Steam library, for the controller and gaming mode"
[notify]="Update notifications: a notification when there's a new Steamify, never updates by itself"
[extended_controller_support]="Extended controller support: Xbox wireless dongle (xone), Xbox controllers over Bluetooth (xpadneo)"
[vram]="VRAM booster: the game in front keeps its VRAM, background apps make room"
[cec]="HDMI-CEC: use Steam with the TV remote, TV on/off with the PC (experimental)"
[machine]="Steam Machine support: LED bar driver, hardware settings in Steam"
@@ -51,9 +57,14 @@ declare -A CURRENT WANTED
component_available() {
case "$1" in
# The SteamOS conversion is offered on NVIDIA PCs too: it installs the patched gamescope there (lib/gamescope-nvidia.sh)
# and turns off "Gaming on NVIDIA" (lib/nvidia.sh), which is not offered any more (nvidia_available).
nvidia|bigpicture|nvsilent) nvidia_available ;;
machine|poweroff) machine_available ;;
vram) vram_available ;;
steamgame) steamgame_available ;;
# Steamify in the Steam library is for gaming mode's controller: not needed for Big Picture on the desktop (NVIDIA PCs;
# shown anyway when it's already on, so it can be turned off).
steamgame) steamgame_available && { ! nvidia_present || steamgame_status; } ;;
kpin) kpin_available ;;
hdmi) hdmi_available ;;
bios) bios_available ;;
@@ -72,6 +83,8 @@ 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
# Silent start is hidden while Steam starts in Big Picture.
[[ "$1" == nvsilent && "${WANTED[bigpicture]:-0}" == 1 ]] && return 1
[[ -z "${PARENT[$1]:-}" || "${WANTED[${PARENT[$1]}]:-0}" == 1 ]]
}
@@ -100,7 +113,11 @@ feature_outdated() {
# On, but set up by an older version of that feature (compared as
# versions: 2.10.0 is newer than 2.9.0).
local have want
# "Gaming on NVIDIA" is on: the SteamOS conversion replaces it, as an update of what the PC has (lib/nvidia.sh).
[[ "$1" == gaming ]] && nvidia_migration_pending && return 0
[[ "${CURRENT[$1]:-0}" == 1 ]] || return 1
# NVIDIA: a newer patched gamescope is out (lib/gamescope-nvidia.sh): a normal run installs it.
[[ "$1" == gaming ]] && nvidia_present && { gsn_update_pending || gsn_display_pending; } && return 0
have="$(state_get features "$1" "$FEATURE_BASELINE")"
[[ "$have" == off ]] && have="$FEATURE_BASELINE"
want="${FEATURE_VERSION[$1]:-$FEATURE_BASELINE}"
@@ -113,7 +130,7 @@ menu_label() {
# last run.
local l="${LABEL[$1]}" b=""
if feature_outdated "$1"; then b="${c_yellow}(update)${c_reset}"
elif feature_new "$1"; then b="${c_green}(new)${c_reset}"; fi
elif feature_new "$1" || feature_new_optin "$1"; then b="${c_green}(new)${c_reset}"; fi
if [[ -n "$b" ]]; then
if [[ "$l" == *:* ]]; then l="${l%%:*} $b:${l#*:}"
else l+=" $b"; fi
@@ -121,16 +138,30 @@ menu_label() {
printf '%s' "$l"
}
feature_new() {
# A default option added after its parent was set up (e.g. the power-off
# fix under Steam Machine support; a top-level one counts the SteamOS
# conversion as its parent): never turned on or off.
local p="${PARENT[$1]:-gaming}"
[[ "$1" != gaming && "${CURRENT[$p]:-0}" == 1 && "${CURRENT[$1]:-0}" == 0 ]] &&
! is_action "$1" && component_selectable "$1" && [[ " ${NO_PRESELECT[*]} " != *" $1 "* ]] &&
feature_added() {
# An option added after its parent was set up (e.g. the power-off fix under
# Steam Machine support; a top-level one counts the SteamOS conversion, or
# "Gaming on NVIDIA" where that replaces it, as its parent), not on yet,
# with no record: shown as "new".
local p="${PARENT[$1]:-}" parent_on=0
if [[ -n "$p" ]]; then parent_on="${CURRENT[$p]:-0}"
elif [[ "${CURRENT[gaming]:-0}" == 1 || "${CURRENT[nvidia]:-0}" == 1 ]]; then parent_on=1; fi
[[ "$1" != gaming && "$parent_on" == 1 && "${CURRENT[$1]:-0}" == 0 ]] &&
! is_action "$1" && component_selectable "$1" &&
[[ -z "$(state_get features "$1")" ]]
}
feature_new() {
# A new default option: ticked for you (never turned on or off by itself).
feature_added "$1" && [[ " ${NO_PRESELECT[*]} " != *" $1 "* ]]
}
feature_new_optin() {
# A new opt-in option (unticked by default): the "new" badge only, it stays
# unticked. Boot into is a choice row of the conversion, not an item.
feature_added "$1" && [[ "$1" != boot && " ${NO_PRESELECT[*]} " == *" $1 "* ]]
}
component_selectable() {
# Greyed out and not tickable when it has nothing to do.
case "$1" in
@@ -141,6 +172,7 @@ component_selectable() {
detect_components() {
local c any=false
for c in "${COMPONENTS[@]}"; do CURRENT[$c]=0; WANTED[$c]=0; done
for c in "${COMPONENTS[@]}"; do
component_available "$c" || continue
if "${c}_status"; then CURRENT[$c]=1; any=true; else CURRENT[$c]=0; fi
@@ -154,6 +186,17 @@ detect_components() {
component_available "$c" || continue
{ feature_outdated "$c" || feature_new "$c"; } && WANTED[$c]=1
done
# "Gaming on NVIDIA" was on: its replacement, the SteamOS conversion, comes with the defaults of a first run, and the
# PC starts the way it did: Steam in Big Picture at login becomes booting into gaming mode, any other Steam start
# (a normal window, or silent in the tray) becomes booting into the desktop, with the silent start kept.
if nvidia_migration_pending; then
WANTED[glyphs]=1
if bigpicture_status; then WANTED[boot]=0
else
WANTED[boot]=1
nvsilent_status && WANTED[silent]=1
fi
fi
# The kernel pin is no longer needed (the power-off fix), and HDMI
# refresh boost needed the pin: whatever is left of either is unticked,
# so a normal run removes it.
@@ -179,15 +222,106 @@ detect_components() {
fi
}
defaults_options() {
# defaults_options [--options <id>[,<id>...]] [--boot gamescope|desktop]:
# what --defaults sets up, from the Steam Machine ISO's installer page.
# --options: exactly these items on, every other off (without it, the
# menu's preselection). An item that's on brings its parent (single user
# the conversion, like ticking it in the menu); one this PC can't use is
# left off with a warning. --boot desktop needs the conversion. Returns 1
# on an unknown option, item or value.
local c ids list=false boot=""
while [[ $# -gt 0 ]]; do
# Both take a value: without one, shift 2 fails and never shifts, so
# the loop would spin forever.
[[ "$1" == --options || "$1" == --boot ]] && [[ $# -lt 2 ]] && { err "$1 needs a value"; return 1; }
case "$1" in
--options)
list=true
IFS=, read -ra ids <<< "$2"
shift 2 ;;
--boot)
case "$2" in
gamescope|desktop) boot="$2" ;;
*) err "--boot takes gamescope or desktop"; return 1 ;;
esac
shift 2 ;;
*) err "Unknown option: $1"; return 1 ;;
esac
done
if [[ "$list" == true ]]; then
for c in "${ids[@]}"; do
[[ -n "$c" ]] || continue
[[ -n "${LABEL[$c]:-}" ]] || { err "Unknown item: $c"; return 1; }
[[ "$c" == boot ]] && { err "Where to start is --boot, not an item."; return 1; }
is_action "$c" && { err "$c can't be part of the install."; return 1; }
done
for c in "${COMPONENTS[@]}"; do WANTED[$c]=0; done
for c in "${ids[@]}"; do
[[ -n "$c" ]] || continue
if component_available "$c" && component_selectable "$c"; then
WANTED[$c]=1
else
warn "Leaving out ${LABEL[$c]%%:*}: not available on this PC."
fi
done
[[ "${WANTED[single]}" == 1 ]] && component_available gaming && WANTED[gaming]=1
for c in "${COMPONENTS[@]}"; do
[[ "${WANTED[$c]}" == 1 && -n "${PARENT[$c]:-}" ]] && WANTED[${PARENT[$c]}]=1
done
fi
case "$boot" in
desktop)
[[ "${WANTED[gaming]}" == 1 ]] || { err "--boot desktop needs the SteamOS conversion (gaming)."; return 1; }
WANTED[boot]=1 ;;
gamescope) WANTED[boot]=0 ;;
esac
return 0
}
defaults_list() {
# --defaults --list: what --defaults can set up on this PC, as one JSON
# array, for an installer page (the Steam Machine ISO builds its Steamify
# page from it, so new items show up without a new ISO). Per item: id,
# label, hint, kind (toggle, or choice for boot: on = desktop), parent,
# on (preselected like a first run) and selectable. Actions, retired
# items and items this PC can't use (e.g. no Steam account yet) are
# left out. Read-only: no sudo, no state.
local c kind on sel items=""
for c in "${COMPONENTS[@]}"; do
component_available "$c" || continue
is_action "$c" && continue
[[ "$c" == kpin || "$c" == hdmi ]] && continue
kind=toggle; [[ "$c" == boot ]] && kind=choice
sel=false; component_selectable "$c" && sel=true
on=false
[[ "$sel" == true && " ${NO_PRESELECT[*]} " != *" $c "* ]] && on=true
[[ "$c" == cec ]] && machine_available && on=true
items+="${items:+,}{\"id\":$(json_str "$c"),\"label\":$(json_str "${LABEL[$c]%%:*}")"
items+=",\"hint\":$(json_str "$( [[ "${LABEL[$c]}" == *:* ]] && echo "${LABEL[$c]#*: }")")"
items+=",\"kind\":\"$kind\",\"parent\":$(json_str "${PARENT[$c]:-}"),\"needs\":\"\",\"hideWhen\":$(json_str "$([[ "$c" == nvsilent ]] && echo bigpicture)"),\"on\":$on,\"selectable\":$sel}"
done
printf '[%s]\n' "$items"
}
toggle_component() {
local c="$1"
component_selectable "$c" || return 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" == single && "${WANTED[single]}" == 1 ]] && component_available gaming; then WANTED[gaming]=1; 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
# Big Picture is opt-out: ticked along with "Gaming on NVIDIA".
if [[ "$c" == nvidia ]]; then WANTED[bigpicture]=${WANTED[nvidia]}; fi
if [[ "$c" == bigpicture && "${WANTED[bigpicture]}" == 1 ]]; then WANTED[nvidia]=1; WANTED[nvsilent]=0; fi
# Silent and Big Picture exclude each other.
if [[ "$c" == nvsilent && "${WANTED[nvsilent]}" == 1 ]]; then WANTED[nvidia]=1; WANTED[bigpicture]=0; fi
if [[ "$c" == nvidia && "${WANTED[nvidia]}" == 0 ]]; then WANTED[nvsilent]=0; fi
if [[ "$c" == gaming && "${WANTED[gaming]}" == 0 ]]; then WANTED[silent]=0; WANTED[glyphs]=0; fi
if [[ "$c" == silent && "${WANTED[silent]}" == 1 ]]; then WANTED[gaming]=1; fi
if [[ "$c" == glyphs && "${WANTED[glyphs]}" == 1 ]]; then WANTED[gaming]=1; fi
# The power-off fix is opt-out: ticked along with Steam Machine support.
if [[ "$c" == machine ]]; then WANTED[poweroff]=${WANTED[machine]}; fi
if [[ "$c" == poweroff && "${WANTED[poweroff]}" == 1 ]]; then WANTED[machine]=1; fi
@@ -203,6 +337,7 @@ toggle_component() {
show_menu() {
local i=0 c now want
echo
gsn_fail_note
printf " ${c_bold}%-3s %-6s %-6s %s${c_reset}\n" "#" "Now" "Want" "Component"
MENU_ITEMS=()
for c in "${COMPONENTS[@]}"; do
@@ -377,11 +512,17 @@ apply_changes() {
if "${c}_disable"; then feature_record "$c" disable; else failed+=("$c"); fi
done
for c in "${TO_ENABLE[@]}"; do
# A sub-option of something that just failed isn't applied: it would be left on without its parent.
if [[ -n "${PARENT[$c]:-}" && " ${failed[*]:-} " == *" ${PARENT[$c]} "* ]]; then
warn "Skipping ${LABEL[$c]%%:*}: ${LABEL[${PARENT[$c]}]%%:*} didn't work."
continue
fi
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
if "${c}_enable"; then is_action "$c" || feature_record "$c" enable; else failed+=("$c"); fi
done
feature_record_unticked
os_version_refresh
FAILED=("${failed[@]}")
}
+339
View File
@@ -0,0 +1,339 @@
#!/bin/bash
# Gaming on NVIDIA PCs: the SteamOS conversion (gamescope session) isn't offered there, because gamescope's DRM session shows a
# corrupted picture on NVIDIA (NVIDIA's open bug 5240452: modes above 2560x1440@120). `nvidia` is the replacement: Steam on the
# Plasma desktop, installed when missing and started at login; `bigpicture` (a sub-option) makes it start in Big Picture.
# On a supported card (RTX 20 series or newer, `nvidia_supported`) it also sets up what NVIDIA's Wayland/DRM stack wants:
# nvidia-drm.modeset=1 and fbdev=1 on the kernel command line and the NVIDIA modules in the initramfs. That didn't cure
# gamescope's picture (the driver already reported both `Y`), but it is the standard setup and may be needed.
# Sourced by steamify.sh; not meant to be run on its own.
NVIDIA_UNIT=steamify-steam-autostart.service
NVIDIA_PARAMS="nvidia-drm.modeset=1 nvidia-drm.fbdev=1"
NVIDIA_INITRAMFS_CONF=${NVIDIA_INITRAMFS_CONF:-/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf}
# The script and pacman hook that keep the initramfs file right at every kernel change.
NVIDIA_SCRIPT=${NVIDIA_SCRIPT:-/usr/local/libexec/steamify-nvidia-initramfs}
NVIDIA_HOOK=${NVIDIA_HOOK:-/etc/pacman.d/hooks/85-steamify-nvidia-initramfs.hook}
# Keeps video memory over suspend (with the nvidia-suspend/resume services).
NVIDIA_MODPROBE=${NVIDIA_MODPROBE:-/etc/modprobe.d/90-steamify-nvidia.conf}
NVIDIA_SLEEP_UNITS="nvidia-suspend.service nvidia-resume.service nvidia-hibernate.service"
# 32-bit parts Steam games and the 32-bit MangoHud preload need.
NVIDIA_LIB32_PKGS="lib32-nvidia-utils lib32-libxkbcommon"
# The running kernel's command line; the tests point this at a file.
NVIDIA_CMDLINE=${NVIDIA_CMDLINE:-/proc/cmdline}
# The DRM devices in sysfs; the tests point this at a fake tree.
NVIDIA_DRM_DIR=${NVIDIA_DRM_DIR:-/sys/class/drm}
nvidia_unit_file() { echo "$HOME/.config/systemd/user/$NVIDIA_UNIT"; }
nvidia_present() {
# An NVIDIA GPU (PCI vendor 0x10de, display class) whose driver provides
# nvidia_drm. nouveau has none, and gamescope is not a problem there.
local d
for d in "$NVIDIA_DRM_DIR"/card[0-9]*/device; do
[[ "$(cat "$d/vendor" 2>/dev/null)" == 0x10de ]] || continue
[[ "$(cat "$d/class" 2>/dev/null)" == 0x03* ]] || continue
modinfo nvidia_drm >/dev/null 2>&1 && return 0
done
return 1
}
# Not offered any more (2.12.0): the SteamOS conversion with the patched gamescope (lib/gamescope-nvidia.sh) covers NVIDIA PCs.
# A PC that has this on keeps it until the conversion is turned on, which calls nvidia_swap_out.
nvidia_available() { return 1; }
nvidia_migration_pending() {
# "Gaming on NVIDIA" is on (set up before 2.12.0) and the SteamOS conversion that replaces it isn't: the menu ticks the
# conversion and shows it as an update, so a normal run moves the PC over (nvidia_swap_out in gsn_enable).
nvidia_present && nvidia_status && ! gaming_status 2>/dev/null
}
nvidia_swap_out() {
# The conversion takes over: Steam no longer starts by itself on the desktop at login (both would start it). Steam, the
# packages and the NVIDIA settings stay: the conversion needs them too. The state stays for the conversion's own disable.
nvidia_status || return 0
info "Gaming on NVIDIA is replaced by the SteamOS conversion: Steam no longer starts on the desktop at login."
user_systemctl disable --now "$NVIDIA_UNIT" >/dev/null 2>&1
rm -f "$(nvidia_unit_file)"
user_systemctl daemon-reload
krevert bigpicture
return 0
}
bigpicture_available() { nvidia_available; }
nvidia_status() {
pacman -Q steam >/dev/null 2>&1 && [[ -f "$(nvidia_unit_file)" ]] &&
user_systemctl is-enabled -q "$NVIDIA_UNIT" 2>/dev/null
}
nvidia_write_unit() {
# nvidia_write_unit [args]: the autostart unit, Steam started with those arguments.
local unit; unit="$(nvidia_unit_file)"
mkdir -p "$(dirname "$unit")"
service_file steamify-steam-autostart.service ARGS="$1" > "$unit" || { err "Writing $unit failed."; return 1; }
user_systemctl daemon-reload
}
nvidia_enable() {
info "Gaming on NVIDIA: Steam on the desktop, started at login..."
if ! pacman -Q steam >/dev/null 2>&1; then
sudo pacman -S --needed --noconfirm steam || { err "Installing Steam failed."; return 1; }
state_set nvidia installed_pkgs steam
fi
# Keeps what the Big Picture option set up; a first run starts Steam normally.
local unit_args=""
if bigpicture_status; then unit_args=-gamepadui; elif nvsilent_status; then unit_args=-silent; fi
nvidia_write_unit "$unit_args" || return 1
user_systemctl enable "$NVIDIA_UNIT" >/dev/null 2>&1 || { err "Enabling $NVIDIA_UNIT failed."; return 1; }
nvidia_kernel_enable || return 1
nvidia_extras_enable || return 1
ok "Steam starts at your next login."
}
nvidia_disable() {
local pkgs unit; unit="$(nvidia_unit_file)"
user_systemctl disable --now "$NVIDIA_UNIT" >/dev/null 2>&1
rm -f "$unit"
user_systemctl daemon-reload
# Swapped for the SteamOS conversion (turned on first, in the same run): it needs the same settings, they stay.
gaming_status 2>/dev/null || nvidia_extras_disable
pkgs="$(state_get nvidia installed_pkgs)"
if gaming_status 2>/dev/null; then
local p; pkgs=" $pkgs "
for p in $NVIDIA_LIB32_PKGS; do pkgs="${pkgs// $p / }"; done
fi
# Only what Steamify installed; games and the Steam folder stay.
# shellcheck disable=SC2086 # a list of package names
[[ -n "$pkgs" ]] && sudo pacman -Rns --noconfirm $pkgs >/dev/null 2>&1
state_clear nvidia
krevert bigpicture
gaming_status 2>/dev/null || nvidia_kernel_disable
ok "Steam no longer starts at login."
}
bigpicture_status() { grep -qs -- '-gamepadui' "$(nvidia_unit_file)"; }
bigpicture_enable() {
# The unit comes from nvidia_enable (it runs first); without it there is nothing to change.
[[ -f "$(nvidia_unit_file)" ]] || return 0
nvidia_write_unit -gamepadui || return 1
# Windows of the last session (Discord, a browser) would be restored on top of Big Picture.
kset bigpicture ksmserverrc General loginMode emptySession
ok "Steam starts in Big Picture at your next login, on an empty desktop."
}
bigpicture_disable() {
krevert bigpicture
[[ -f "$(nvidia_unit_file)" ]] || return 0
nvidia_write_unit "" && ok "Steam starts in its normal window at your next login."
}
# Sub-option: Steam starts silently (tray only, no window). Big Picture and
# a silent start exclude each other (the menus untick one when the other is
# ticked); a first run with both gets Big Picture.
nvsilent_status() { grep -qs -- '-silent' "$(nvidia_unit_file)"; }
nvsilent_enable() {
[[ -f "$(nvidia_unit_file)" ]] || return 0
bigpicture_status && return 0
nvidia_write_unit -silent || return 1
ok "Steam starts silently at your next login."
}
nvsilent_disable() {
nvsilent_status || return 0
nvidia_write_unit "" && ok "Steam starts in its normal window at your next login."
}
# --- Kernel parameters and early modules (a supported card only)
nvidia_supported() {
# A supported NVIDIA GPU: PCI vendor 0x10de, display class, its kernel
# driver installed, and not older than Turing (GTX 16 / RTX 20 series: the cards NVIDIA's open kernel driver supports), decided like the
# VRAM booster does (vram_nvidia_legacy_id: chwd's legacy card lists, every
# other card runs NVIDIA's open modules). Older cards are left alone.
local d
for d in "$NVIDIA_DRM_DIR"/card[0-9]*/device; do
[[ "$(cat "$d/vendor" 2>/dev/null)" == 0x10de ]] || continue
[[ "$(cat "$d/class" 2>/dev/null)" == 0x03* ]] || continue
vram_nvidia_legacy_id "$(cat "$d/device" 2>/dev/null)" && continue
modinfo nvidia_drm >/dev/null 2>&1 && return 0
done
return 1
}
nvidia_legacy_gpu() {
# 0 when the only NVIDIA GPU(s) are older than RTX 20 (reported, not fixed).
local d found=0
for d in "$NVIDIA_DRM_DIR"/card[0-9]*/device; do
[[ "$(cat "$d/vendor" 2>/dev/null)" == 0x10de && "$(cat "$d/class" 2>/dev/null)" == 0x03* ]] || continue
vram_nvidia_legacy_id "$(cat "$d/device" 2>/dev/null)" || return 1
found=1
done
[[ $found == 1 ]]
}
nvidia_boot_file() {
# Same boot loaders as the kernel command line elsewhere (lib/hdmi-refresh.sh).
hdmi_boot_file
}
nvidia_param_set() {
# 0 when $1 (e.g. nvidia-drm.modeset=1) is on the kernel command line or
# already in the boot loader's file (not booted yet).
# The kernel treats "nvidia_drm" and "nvidia-drm" alike: CachyOS' own
# setup may already have written the underscore spelling.
local f re; f="$(nvidia_boot_file)"; re="${1//nvidia-drm/nvidia[-_]drm}"
grep -qwE -- "$re" "$NVIDIA_CMDLINE" 2>/dev/null ||
{ [[ -n "$f" ]] && grep -qwE -- "$re" "$f" 2>/dev/null; }
}
nvidia_params_missing() {
local p
for p in $NVIDIA_PARAMS; do nvidia_param_set "$p" || return 0; done
return 1
}
nvidia_initramfs_ok() {
[[ -f "$NVIDIA_INITRAMFS_CONF" ]] || grep -qE '^MODULES=.*nvidia_drm' /etc/mkinitcpio.conf 2>/dev/null
}
nvidia_modules_everywhere() {
# 0 when every installed kernel has the nvidia_drm module (the script's own check).
patch_file steamify-nvidia-initramfs.sh | NVIDIA_MODULES_DIR="${NVIDIA_MODULES_DIR:-/usr/lib/modules}" bash -s check
}
nvidia_hook_install() {
# Never installs an empty script or hook (a piped patch_file that failed
# still lets install succeed with no input).
local script hook
script="$(patch_file steamify-nvidia-initramfs.sh)" && [[ -n "$script" ]] &&
hook="$(patch_file steamify-nvidia-initramfs.hook | fill SCRIPT="$NVIDIA_SCRIPT")" && [[ -n "$hook" ]] || return 1
printf '%s\n' "$script" | sudo install -Dm755 /dev/stdin "$NVIDIA_SCRIPT" &&
printf '%s\n' "$hook" | sudo install -Dm644 /dev/stdin "$NVIDIA_HOOK"
}
nvidia_rebuild_boot() {
local f; f="$(nvidia_boot_file)"
info "Rebuilding the initramfs and boot entries..."
case "$f" in
/etc/default/limine) sudo limine-mkinitcpio ;;
/etc/sdboot-manage.conf) sudo /usr/bin/mkinitcpio -P && sudo sdboot-manage gen ;;
/etc/default/grub) sudo /usr/bin/mkinitcpio -P && sudo grub-mkconfig -o /boot/grub/grub.cfg ;;
*) sudo /usr/bin/mkinitcpio -P ;;
esac
}
nvidia_boot_has_params() {
# 0 when the generated boot entries carry the parameters (or can't be
# checked): limine.conf, systemd-boot's entries, grub.cfg.
local target
case "$1" in
*/limine) target="${NVIDIA_LIMINE_CONF:-/boot/limine.conf}" ;;
*/sdboot-manage.conf) target="${NVIDIA_SDBOOT_DIR:-/boot/loader/entries}" ;;
*/grub) target="${NVIDIA_GRUB_CFG:-/boot/grub/grub.cfg}" ;;
*) return 0 ;;
esac
sudo test -e "$target" || return 0
sudo grep -rqs 'nvidia-drm.modeset=1' "$target"
}
nvidia_kernel_enable() {
nvidia_supported || return 0
local f changed=0 p; f="$(nvidia_boot_file)"
if nvidia_params_missing; then
if [[ -z "$f" ]]; then
warn "NVIDIA GPU found, but no supported boot loader: add '$NVIDIA_PARAMS' to the kernel command line yourself."
else
info "NVIDIA GPU found: adding $NVIDIA_PARAMS to the kernel command line (fixes a corrupted screen in gaming mode)..."
backup_file "$f"
case "$f" in
*/limine)
sudo sed -i -E "s/^(KERNEL_CMDLINE\[default\]\+?=\"[^\"]*)\"/\1 $NVIDIA_PARAMS\"/" "$f" ;;
*/sdboot-manage.conf)
sudo sed -i -E "s/^(LINUX_OPTIONS=\"[^\"]*)\"/\1 $NVIDIA_PARAMS\"/" "$f" ;;
*/grub)
sudo sed -i -E "s/^(GRUB_CMDLINE_LINUX_DEFAULT=\"[^\"]*)\"/\1 $NVIDIA_PARAMS\"/" "$f" ;;
esac
if ! grep -q "nvidia-drm.fbdev=1" "$f"; then
err "Couldn't add the NVIDIA parameters to $f (no kernel command line setting in it): add '$NVIDIA_PARAMS' yourself."
return 1
fi
changed=1
fi
fi
nvidia_hook_install || { err "Installing the NVIDIA initramfs hook failed."; return 1; }
local had=0; [[ -f "$NVIDIA_INITRAMFS_CONF" ]] && had=1
sudo env NVIDIA_INITRAMFS_CONF="$NVIDIA_INITRAMFS_CONF" NVIDIA_MODULES_DIR="${NVIDIA_MODULES_DIR:-/usr/lib/modules}" "$NVIDIA_SCRIPT" sync
if [[ -f "$NVIDIA_INITRAMFS_CONF" && $had == 0 ]]; then
info "Loading the NVIDIA modules early (initramfs)..."
changed=1
elif [[ ! -f "$NVIDIA_INITRAMFS_CONF" && $had == 1 ]]; then
warn "A kernel without the NVIDIA modules is installed: not loading them early any more (rebuilding the initramfs without them)."
changed=1
elif [[ ! -f "$NVIDIA_INITRAMFS_CONF" ]] && ! nvidia_initramfs_ok; then
warn "Not every installed kernel has the NVIDIA modules: not loading them early for now (the kernel parameters still apply; this is checked again at every kernel update)."
fi
if [[ $changed == 1 ]]; then
nvidia_rebuild_boot || { err "Rebuilding the boot entries failed."; return 1; }
if ! nvidia_boot_has_params "$f"; then
err "The boot entries don't have the NVIDIA parameters: the initramfs or boot entry build failed (see above)."
return 1
fi
ok "NVIDIA gaming mode fix applied; it takes effect after a reboot."
fi
return 0
}
nvidia_extras_enable() {
# Video memory kept over sleep, the matching services and the 32-bit libraries (a supported card only).
nvidia_supported || return 0
local pkg u units="" prev missing=()
if [[ ! -f "$NVIDIA_MODPROBE" ]]; then
info "Keeping video memory over suspend (NVreg_PreserveVideoMemoryAllocations=1)..."
printf '%s\n' '# Added by Steamify (Gaming on NVIDIA)' 'options nvidia NVreg_PreserveVideoMemoryAllocations=1' |
sudo tee "$NVIDIA_MODPROBE" >/dev/null || { err "Writing $NVIDIA_MODPROBE failed."; return 1; }
# The NVIDIA modules load from the initramfs, which carries modprobe.d.
nvidia_rebuild_boot || warn "Rebuilding the initramfs failed: the suspend setting applies once it is rebuilt."
fi
for u in $NVIDIA_SLEEP_UNITS; do
systemctl is-enabled -q "$u" 2>/dev/null && continue
sudo systemctl enable "$u" >/dev/null 2>&1 && units+="${units:+ }$u"
done
if [[ -n "$units" ]]; then
prev="$(state_get nvidia enabled_units)"
state_set nvidia enabled_units "${prev:+$prev }$units"
fi
for pkg in $NVIDIA_LIB32_PKGS; do pacman -Q "$pkg" >/dev/null 2>&1 || missing+=("$pkg"); done
if [[ ${#missing[@]} -gt 0 ]]; then
info "Installing the 32-bit libraries games need: ${missing[*]}"
if sudo pacman -S --needed --noconfirm "${missing[@]}"; then
prev="$(state_get nvidia installed_pkgs)"
state_set nvidia installed_pkgs "${prev:+$prev }${missing[*]}"
else
warn "Installing ${missing[*]} failed (is [multilib] enabled in /etc/pacman.conf?): 32-bit games may not start."
fi
fi
return 0
}
nvidia_extras_disable() {
# Only what Steamify enabled; the 32-bit packages go with installed_pkgs.
local u
for u in $(state_get nvidia enabled_units); do sudo systemctl disable "$u" >/dev/null 2>&1; done
if [[ -f "$NVIDIA_MODPROBE" ]]; then
sudo rm -f "$NVIDIA_MODPROBE"
nvidia_rebuild_boot || true
fi
}
nvidia_kernel_disable() {
local f changed=0; f="$(nvidia_boot_file)"
if [[ -n "$f" ]] && grep -q "$NVIDIA_PARAMS" "$f" 2>/dev/null; then
sudo sed -i "s/ \?$NVIDIA_PARAMS//" "$f"
changed=1
fi
[[ -f "$NVIDIA_INITRAMFS_CONF" ]] && { sudo rm -f "$NVIDIA_INITRAMFS_CONF"; changed=1; }
sudo rm -f "$NVIDIA_HOOK" "$NVIDIA_SCRIPT"
[[ $changed == 1 ]] && nvidia_rebuild_boot
return 0
}
+25
View File
@@ -58,6 +58,30 @@ bootstrap_yay() {
fi
}
python_hid_shim_ensure() {
# jupiter-hw-support (a dependency of gamescope-session-cachyos) wants python-hid, which conflicts with python-hidapi, and
# a PC can need python-hidapi (liquidctl does). Only Valve's Steam Deck controller firmware updater uses python-hid, which
# a PC doesn't run: so a placeholder package that provides python-hid keeps python-hidapi and satisfies the dependency
# (also at later updates, which a one-time --assume-installed wouldn't). It stays when the conversion goes off:
# jupiter-hw-support, which stays, depends on it.
pacman -Q python-hidapi >/dev/null 2>&1 || return 0
pacman -Q python-hid steamify-python-hid-shim >/dev/null 2>&1 && return 0
local dir pkg cache="${XDG_CACHE_HOME:-$HOME/.cache}/steamify"
warn "python-hidapi is installed (liquidctl and others need it) and conflicts with python-hid, which Steam Deck's tools ask for: keeping python-hidapi, with a small placeholder package for python-hid."
sudo pacman -S --needed --noconfirm base-devel >/dev/null || { err "Installing the build tools failed."; return 1; }
mkdir -p "$cache" && dir="$(mktemp -d -p "$cache" hid-shim.XXXXXX)" || return 1
patch_file python-hid-shim.PKGBUILD > "$dir/PKGBUILD" || { rm -rf "$dir"; return 1; }
if ! (cd "$dir" && makepkg -f --noconfirm >/dev/null) ||
! pkg="$(ls "$dir"/steamify-python-hid-shim-*.pkg.tar.* 2>/dev/null | head -1)" ||
! sudo pacman -U --noconfirm "$pkg"; then
rm -rf "$dir"
err "Couldn't set up the python-hid placeholder: python-hidapi and python-hid conflict, so the packages can't be installed."
return 1
fi
rm -rf "$dir"
ok "python-hidapi kept; python-hid is covered by a placeholder."
}
install_required_packages() {
# Step 2: gamescope/Steam and the desktop helpers the rest of the setup uses.
local -a MISSING
@@ -65,6 +89,7 @@ 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
python_hid_shim_ensure || return 1
info "Installing missing packages: ${MISSING[*]}"
sudo pacman -S --needed --noconfirm "${MISSING[@]}" || { err "Package installation failed."; return 1; }
else
+57
View File
@@ -101,7 +101,63 @@ single_launcher() {
done
}
single_launcher_drop() {
# The theme's layout backup is taken when it's turned on: with single
# user on then, it holds single's launcher keys, and restoring it after
# single was turned off brings them back. Drop exactly single's values
# (a launcher set up differently by the user is left alone).
local applet
local -a kick_args
for applet in $(plasma_applets org.kde.plasma.kickoff); do
kick_args=(--file plasma-org.kde.plasma.desktop-appletsrc --group Containments --group "${applet%%:*}"
--group Applets --group "${applet#*:}" --group Configuration --group General)
[[ "$(kreadconfig6 "${kick_args[@]}" --key primaryActions)" == 3 ]] &&
kwriteconfig6 "${kick_args[@]}" --key primaryActions --delete
[[ "$(kreadconfig6 "${kick_args[@]}" --key systemFavorites)" == 'suspend,reboot,shutdown' ]] &&
kwriteconfig6 "${kick_args[@]}" --key systemFavorites --delete
done
return 0
}
# Without the SteamOS conversion (NVIDIA PCs: gamescope's session is broken there) nothing logs in by itself, so single user
# mode does it: SDDM, autologin into the Plasma session. Named to sort after steam-set-session's zz-steamos-autologin.conf,
# which may still say gamescope from an earlier conversion.
SINGLE_AUTOLOGIN=${SINGLE_AUTOLOGIN:-/etc/sddm.conf.d/zzz-steamify-autologin.conf}
single_login_needed() { nvidia_present && ! gaming_status; }
single_login_enable() {
single_login_needed || return 0
switch_to_sddm || return 1
info "Logging in to the desktop by itself (SDDM autologin for $TARGET_USER)..."
sudo mkdir -p "$(dirname "$SINGLE_AUTOLOGIN")"
sudo tee "$SINGLE_AUTOLOGIN" > /dev/null << EOF
[Autologin]
User=$TARGET_USER
Session=plasma.desktop
Relogin=true
EOF
remove_sddm_base_autologin
}
single_login_release() {
# The SteamOS conversion logs in by itself, and picks the session (gamescope, or the desktop with "Boot into"): the
# desktop autologin above, which sorts last, would pin the Plasma session and "Return to Gaming Mode" would open KDE
# again. Called when the conversion is turned on; turning it off sets it up again (gaming_disable).
[[ -f "$SINGLE_AUTOLOGIN" ]] || return 0
info "The SteamOS conversion logs in by itself: removing the desktop autologin of single user mode."
sudo rm -f "$SINGLE_AUTOLOGIN"
}
single_login_disable() {
[[ -f "$SINGLE_AUTOLOGIN" ]] || return 0
sudo rm -f "$SINGLE_AUTOLOGIN"
# The way back is CachyOS's own login manager, unless the conversion is on (it keeps SDDM).
gaming_status || switch_to_plasmalogin
}
single_enable() {
single_login_enable || return 1
info "Turning off the lock screen, user switching and logging out..."
stop_plasmashell_for_edit
@@ -127,6 +183,7 @@ single_enable() {
}
single_disable() {
single_login_disable
info "Restoring the lock screen, user switching and logging out..."
stop_plasmashell_for_edit
if has_journal single; then
+43 -15
View File
@@ -5,35 +5,63 @@
steam_enable() {
info "Applying non-optional SteamOS environment & keyboard fixes..."
local steam_env_dir systemd_user_dir
local steam_env_dir
steam_env_dir="$HOME/.config/environment.d"
mkdir -p "$steam_env_dir"
# 2. Prevent KDE Wayland from blocking the virtual keyboard overlay
echo "KWIN_IM_SHOW_ALWAYS=1" > "$steam_env_dir/99-kde-virtual-keyboard.conf"
# 3. Setup robust background systemd service for Steam autostart on Desktop Mode
systemd_user_dir="$HOME/.config/systemd/user"
mkdir -p "$systemd_user_dir"
ok "Steam UI parameters and keyboard fix deployed successfully."
}
service_file steam-desktop-autostart.service > "$systemd_user_dir/steam-desktop-autostart.service"
# Sub-option of the SteamOS conversion: Steam starts silently (in the tray,
# no window) on the Plasma desktop. It is Steam's own autostart entry, the file
# Steam's setting "Run Steam when my computer starts" creates and removes, so
# that setting shows what Steamify did (and the other way around). Steam's copy
# is the system steam.desktop; ours has -silent in its Exec line.
STEAM_AUTOSTART="${XDG_CONFIG_HOME:-$HOME/.config}/autostart/steam.desktop"
STEAM_SYSTEM_DESKTOP=/usr/share/applications/steam.desktop
# Clean out old .desktop shortcut so they don't fight
rm -f "$HOME/.config/autostart/steam.desktop"
silent_status() {
# The old systemd unit counts as on too (until it is migrated by an update of this option).
grep -qs '^Exec=.*-silent' "$STEAM_AUTOSTART" ||
[[ -f "$HOME/.config/systemd/user/steam-desktop-autostart.service" ]]
}
# Reload user systemd context and activate the background loop
silent_old_unit_remove() {
# Before 2.11.0 the option was a systemd user unit: it would start Steam a second time.
local unit="$HOME/.config/systemd/user/steam-desktop-autostart.service"
[[ -f "$unit" ]] || return 0
user_systemctl disable --now steam-desktop-autostart.service 2>/dev/null
rm -f "$unit"
user_systemctl daemon-reload
user_systemctl enable --now steam-desktop-autostart.service
}
ok "Steam UI parameters and keyboard autostart deployed successfully."
silent_enable() {
silent_old_unit_remove
mkdir -p "$(dirname "$STEAM_AUTOSTART")"
if [[ -f "$STEAM_SYSTEM_DESKTOP" ]]; then
sed 's|^Exec=/usr/bin/steam %U$|Exec=/usr/bin/steam -silent %U|' "$STEAM_SYSTEM_DESKTOP" > "$STEAM_AUTOSTART"
fi
silent_status || printf '%s\n' '[Desktop Entry]' 'Name=Steam' 'Exec=/usr/bin/steam -silent %U' \
'Icon=steam' 'Terminal=false' 'Type=Application' 'Categories=Network;FileTransfer;Game;' > "$STEAM_AUTOSTART"
ok "Steam starts silently on the desktop from the next login."
}
silent_disable() {
silent_old_unit_remove
silent_status && rm -f "$STEAM_AUTOSTART"
ok "Steam no longer starts by itself on the desktop."
}
steam_disable() {
info "Removing Steam desktop autostart..."
user_systemctl 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"
user_systemctl daemon-reload
info "Removing Steam desktop settings..."
# The autostart entry is the `silent` option's: removed with it, and here
# too for when the conversion goes off by itself.
silent_status && silent_disable
silent_old_unit_remove
rm -f "$HOME/.config/environment.d/99-kde-virtual-keyboard.conf"
ok "Steam desktop settings removed (takes full effect at next login)."
}
+2 -6
View File
@@ -56,7 +56,7 @@ steamgame_steam_busy() {
steamgame_edit() {
# steamgame_edit <function>: runs it with Steam closed, then starts Steam
# again if it was running (through our autostart unit when that's there).
# again if it was running (silently, in the tray).
local was_running=false i
if pgrep -x steam >/dev/null; then
steamgame_steam_busy && return 1
@@ -72,11 +72,7 @@ steamgame_edit() {
fi
"$1"; local rc=$?
if [[ "$was_running" == true ]]; then
if user_systemctl is-enabled -q steam-desktop-autostart.service 2>/dev/null; then
user_systemctl start steam-desktop-autostart.service
else
systemd-run --user --collect -q steam -silent >/dev/null 2>&1
fi
systemd-run --user --collect -q steam -silent >/dev/null 2>&1
info "Steam is starting again."
fi
return "$rc"
+47
View File
@@ -378,6 +378,49 @@ machine_status() {
pacman -Qi leds-valve-dkms-git >/dev/null 2>&1 && pacman -Qi steamos-manager >/dev/null 2>&1
}
# Steam (steamwebhelper) reads the serial number for its System settings
# straight from DMI, which the kernel makes root-only; SteamOS makes it
# readable. A tmpfiles rule does that at every boot.
SERIAL_TMPFILES=/etc/tmpfiles.d/steamify-serial.conf
# Steam's Wi-Fi backend switch goes through steamos-manager, which reads and
# writes the backend in this file (SteamOS ships it); without it, it fails
# with "Wi-Fi backend not found in config". wpa_supplicant is what
# NetworkManager uses on CachyOS anyway. Not written when someone set a
# backend themselves.
WIFI_BACKEND_CONF=/etc/NetworkManager/conf.d/99-valve-wifi-backend.conf
wifi_backend_enable() {
[[ -f "$WIFI_BACKEND_CONF" ]] && return 0
grep -rqs '^wifi.backend=' /etc/NetworkManager/NetworkManager.conf /etc/NetworkManager/conf.d && return 0
printf '%s\n' "# Written by Steamify: Steam's Wi-Fi backend setting (steamos-manager)." \
'[device]' 'wifi.backend=wpa_supplicant' | sudo install -Dm644 /dev/stdin "$WIFI_BACKEND_CONF" ||
{ err "Writing the Wi-Fi backend setting failed."; return 1; }
state_set machine wifi_backend 1
}
wifi_backend_disable() {
[[ -n "$(state_get machine wifi_backend)" ]] || return 0
sudo rm -f "$WIFI_BACKEND_CONF"
state_clear machine
}
serial_enable() {
printf '%s\n' "# Written by Steamify: Steam shows the serial number, like on SteamOS." \
'z /sys/class/dmi/id/product_serial 0444 - - -' | sudo tee "$SERIAL_TMPFILES" > /dev/null ||
{ err "Making the serial number readable for Steam failed."; return 1; }
# The file applies at every boot; now too, unless /sys is read-only (the ISO's installer runs in a chroot).
sudo systemd-tmpfiles --create "$SERIAL_TMPFILES" 2>/dev/null ||
info "The serial number becomes readable for Steam at the next boot."
return 0
}
serial_disable() {
sudo rm -f "$SERIAL_TMPFILES"
sudo chmod 0400 /sys/class/dmi/id/product_serial 2>/dev/null
return 0
}
machine_enable() {
# The pinned kernel first when it's wanted too, so DKMS builds the LED
# driver for it once, instead of for the current kernel and then again.
@@ -403,6 +446,8 @@ EOF
# for hardware and controller settings; it knows the Steam Machine from its DMI data.
info "Installing hardware manager packages (steamos-manager & inputplumber)..."
sudo pacman -S --needed --noconfirm steamos-manager inputplumber || { err "Installing hardware manager packages failed."; return 1; }
serial_enable || return 1
wifi_backend_enable || return 1
sudo systemctl enable --now inputplumber.service
sudo systemctl enable --now steamos-manager.service
user_systemctl enable steamos-manager.service 2>/dev/null
@@ -426,6 +471,8 @@ machine_disable() {
sudo systemctl disable --now steamos-manager.service 2>/dev/null
sudo systemctl disable --now inputplumber.service 2>/dev/null
sudo pacman -Rns --noconfirm steamos-manager inputplumber 2>/dev/null
serial_disable
wifi_backend_disable
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
+27 -8
View File
@@ -106,17 +106,32 @@ theme_enable() {
# SteamOS panel, launcher icon and wallpaper. It needs plasmashell up.
lookandfeeltool -a "$VAPOR_LOOKANDFEEL" --resetLayout >/dev/null 2>&1 ||
{ err "lookandfeeltool failed."; return 1; }
# The new layout has a new launcher, without single user's settings.
if single_status; then
sleep 3
stop_plasmashell_for_edit
single_launcher
restart_plasmashell_if_stopped
fi
# The new layout has a new launcher, without single user's settings, and the default taskbar pins.
sleep 3
stop_plasmashell_for_edit
taskbar_drop_discover
single_status && single_launcher
restart_plasmashell_if_stopped
extras_enable || warn "SteamOS desktop extras not installed (see above); the theme itself is on."
ok "Vapor theme active."
}
taskbar_drop_discover() {
# Plasma's default taskbar pins Discover, which CachyOS doesn't install: the pin shows as a
# blank icon. Pin what CachyOS's own layout pins (/etc/skel) instead. Not when Discover is
# installed, and only on a taskbar that still has the default pins (no launchers key).
# Edit only while plasmashell is stopped.
[[ -e /usr/share/applications/org.kde.discover.desktop ]] && return 0
local applet grp
for applet in $(plasma_applets org.kde.plasma.icontasks); do
grp="Containments|${applet%%:*}|Applets|${applet#*:}|Configuration|General"
[[ -n "$(kreadconfig6 --file plasma-org.kde.plasma.desktop-appletsrc --group Containments --group "${applet%%:*}" \
--group Applets --group "${applet#*:}" --group Configuration --group General --key launchers)" ]] && continue
kset theme plasma-org.kde.plasma.desktop-appletsrc "$grp" launchers \
'applications:systemsettings.desktop,preferred://filemanager,preferred://browser'
done
}
theme_disable() {
extras_disable
info "Restoring the previous desktop look and layout..."
@@ -134,7 +149,11 @@ theme_disable() {
fi
# After the tools above, so keys that weren't set before are removed again.
krevert theme
single_status && single_launcher
if single_status; then
single_launcher
else
single_launcher_drop
fi
restart_plasmashell_if_stopped
qdbus6 org.kde.KWin /KWin org.kde.KWin.reconfigure >/dev/null 2>&1 || true
+8 -2
View File
@@ -44,6 +44,13 @@ vram_nvidia() {
# 470xx, 390xx); every other NVIDIA card runs the open kernel modules.
VRAM_CHWD_IDS=/var/lib/chwd/ids
vram_nvidia_legacy_id() {
# vram_nvidia_legacy_id <pci device id, with or without 0x>: 0 when chwd
# lists the card for a closed legacy branch, i.e. it is older than the
# RTX 20 series. Also what lib/nvidia.sh uses to decide if a card is supported.
grep -qwi "${1#0x}" "$VRAM_CHWD_IDS"/nvidia-*.ids 2>/dev/null
}
vram_nvidia_case() {
# Why an NVIDIA card's VRAM isn't registered, so the menu can say what to
# do: "update" (open modules older than 615), "switch <chwd profile>"
@@ -62,8 +69,7 @@ vram_nvidia_case() {
esac
for d in /sys/bus/pci/devices/*; do
[[ "$(cat "$d/vendor" 2>/dev/null)" == 0x10de && "$(cat "$d/class" 2>/dev/null)" == 0x03* ]] || continue
id="$(cat "$d/device")"; id="${id#0x}"
grep -qwi "$id" "$VRAM_CHWD_IDS"/nvidia-*.ids 2>/dev/null && { echo legacy; return; }
vram_nvidia_legacy_id "$(cat "$d/device")" && { echo legacy; return; }
done
for p in 580xx 470xx 390xx; do
pacman -Q "nvidia-$p-dkms" >/dev/null 2>&1 && { echo "switch nvidia-dkms-$p"; return; }
+8
View File
@@ -13,4 +13,12 @@ read and reviewed on their own. `lib/*.sh` reads them with `patch_file
| `steamify-fremont-poweroff.dkms.conf`, `steamify-cros-ec-cec.dkms.conf` | `lib/fremont-poweroff.sh`, `lib/cec.sh` | The DKMS modules' `dkms.conf` (`fill NAME=... VERSION=...`) |
| `steam-shortcuts.py` | `lib/steam-game.sh` (Add as non-Steam game) | Reads and writes Steam's binary `shortcuts.vdf` (find, add or update, remove) |
| `steamify-notifier.py` | `lib/update-notifier.sh` (Update notifications) | The daily check: notification and tray icon when there's a new release |
| `steamify-os-release.sh`, `steamify-os-release.hook` | `lib/login-manager.sh` (SteamOS conversion) | What Steam's System settings show besides the OS name (which stays CachyOS's): `VERSION_ID=steamos-X.Y`, `VERSION_CODENAME=steam-machine`, `VARIANT`/`VARIANT_ID` Steamify with its version, in `/etc/os-release`; the pacman hook runs it again after cachyos-hooks (`fill SCRIPT=... VERSION=... STEAMOS=...`) |
| `steamify-nvidia-initramfs.sh`, `steamify-nvidia-initramfs.hook` | `lib/nvidia.sh` (SteamOS conversion, NVIDIA fix) | The pacman hook (before the initramfs is built, after DKMS) and its script: the NVIDIA modules go into the initramfs only while every installed kernel has them, decided again at every kernel or driver change (`fill SCRIPT=...`) |
| `cros-ec-cec-single-port.patch` | `lib/cec.sh` (HDMI-CEC) | Makes Valve's Steam Machine CEC driver find amdgpu's HDMI port |
| `gamescope-gbm-scanout.patch` | `lib/gamescope-nvidia.sh` (gamescope on NVIDIA) | gamescope 3.16.31 patch: scan-out buffers allocated with GBM (opt-in, `gamescope_drm_gbm_scanout=1`), which cures the corrupted picture on NVIDIA; from farnoy/gamescope, see the header of the file |
| `gamescope-nvidia.PKGBUILD` | `lib/gamescope-nvidia.sh` (gamescope on NVIDIA) | The recipe for the patched gamescope package (CI builds it per CPU level; here it is only the local-build fallback) (`steamify-gamescope-nvidia`, provides and conflicts with `gamescope`): Valve's gamescope at `pkgver` plus `gamescope-gbm-scanout.patch`; written with the patch to a temporary folder and built with `makepkg` |
| `cros-ec-cec.c` | `lib/cec.sh` (HDMI-CEC) | Valve's Steam Machine CEC driver (GPL-2.0+, from evlaV/linux-integration at the pinned commit): the backup when downloading it from GitHub fails; used only if its sha256 equals `CEC_DRIVER_SHA256` (a new pin needs a new copy) |
| `python-hid-shim.PKGBUILD` | `lib/packages.sh` (required packages) | An empty package that provides `python-hid`, for PCs that keep `python-hidapi` (they conflict; jupiter-hw-support, a dependency of gamescope-session-cachyos, wants python-hid, only the Steam Deck controller updater uses it): built with `makepkg` and installed before the conversion's packages |
| `gamescope-ui-native.patch` | `lib/gamescope-nvidia.sh` (gamescope on NVIDIA) | Small patch after the GBM one: keeps Steam's UI X server at the display's size when Steam (with `-steamos3`) asks for 1920x1080; `gamescope_xwayland_ui_native=0` turns it off |
| `gamescope-display-wrapper.sh` | `lib/gamescope-nvidia.sh` (gamescope on NVIDIA) | Installed as `gamescope` in Steamify's bin folder and put first in the gaming session's PATH: starts gamescope on the mode KDE has saved for the connected display, else the biggest mode at the highest refresh rate (modetest + `kwinoutputconfig.json`) |
+621
View File
@@ -0,0 +1,621 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* CEC driver for ChromeOS Embedded Controller
*
* Copyright (c) 2018 BayLibre, SAS
* Author: Neil Armstrong <narmstrong@baylibre.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/dmi.h>
#include <linux/pci.h>
#include <linux/cec.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <media/cec.h>
#include <media/cec-notifier.h>
#define DRV_NAME "cros-ec-cec"
/**
* struct cros_ec_cec_port - Driver data for a single EC CEC port
*
* @port_num: port number
* @adap: CEC adapter
* @notify: CEC notifier pointer
* @rx_msg: storage for a received message
* @cros_ec_cec: pointer to the parent struct
*/
struct cros_ec_cec_port {
int port_num;
struct cec_adapter *adap;
struct cec_notifier *notify;
struct cec_msg rx_msg;
struct cros_ec_cec *cros_ec_cec;
};
/**
* struct cros_ec_cec - Driver data for EC CEC
*
* @cros_ec: Pointer to EC device
* @notifier: Notifier info for responding to EC events
* @write_cmd_version: Highest supported version of EC_CMD_CEC_WRITE_MSG.
* @num_ports: Number of CEC ports
* @ports: Array of ports
*/
struct cros_ec_cec {
struct cros_ec_device *cros_ec;
struct notifier_block notifier;
int write_cmd_version;
int num_ports;
struct cros_ec_cec_port *ports[EC_CEC_MAX_PORTS];
};
static void cros_ec_cec_received_message(struct cros_ec_cec_port *port,
uint8_t *msg, uint8_t len)
{
if (len > CEC_MAX_MSG_SIZE)
len = CEC_MAX_MSG_SIZE;
port->rx_msg.len = len;
memcpy(port->rx_msg.msg, msg, len);
cec_received_msg(port->adap, &port->rx_msg);
}
static void handle_cec_message(struct cros_ec_cec *cros_ec_cec)
{
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
uint8_t *cec_message = cros_ec->event_data.data.cec_message;
unsigned int len = cros_ec->event_size;
struct cros_ec_cec_port *port;
/*
* There are two ways of receiving CEC messages:
* 1. Old EC firmware which only supports one port sends the data in a
* cec_message MKBP event.
* 2. New EC firmware which supports multiple ports uses
* EC_MKBP_CEC_HAVE_DATA to notify that data is ready and
* EC_CMD_CEC_READ_MSG to read it.
* Check that the EC only has one CEC port, and then we can assume the
* message is from port 0.
*/
if (cros_ec_cec->num_ports != 1) {
dev_err(cros_ec->dev,
"received cec_message on device with %d ports\n",
cros_ec_cec->num_ports);
return;
}
port = cros_ec_cec->ports[0];
cros_ec_cec_received_message(port, cec_message, len);
}
static void cros_ec_cec_read_message(struct cros_ec_cec_port *port)
{
struct cros_ec_device *cros_ec = port->cros_ec_cec->cros_ec;
struct ec_params_cec_read params = {
.port = port->port_num,
};
struct ec_response_cec_read response;
int ret = 0;
int count = 0;
/* NOTE read as many messages as we can, return value from cros_ec_cmd
* here should either equal to the bytes transferred or a negative
* error code, so as long as we get positive integers we are
* reading messages!
*/
do {
ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_READ_MSG, &params,
sizeof(params), &response, sizeof(response));
if (ret > 0) {
++count;
cros_ec_cec_received_message(port, response.msg, response.msg_len);
}
}
while (ret > 0);
if (ret < 0 && count == 0) {
dev_err(cros_ec->dev,
"error reading CEC message on EC: %d\n", ret);
}
}
static void handle_cec_event(struct cros_ec_cec *cros_ec_cec)
{
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
uint32_t cec_events = cros_ec->event_data.data.cec_events;
uint32_t port_num = EC_MKBP_EVENT_CEC_GET_PORT(cec_events);
uint32_t events = EC_MKBP_EVENT_CEC_GET_EVENTS(cec_events);
struct cros_ec_cec_port *port;
if (port_num >= cros_ec_cec->num_ports) {
dev_err(cros_ec->dev,
"received CEC event for invalid port %d\n", port_num);
return;
}
port = cros_ec_cec->ports[port_num];
if (events & EC_MKBP_CEC_SEND_OK)
cec_transmit_attempt_done(port->adap, CEC_TX_STATUS_OK);
/* FW takes care of all retries, tell core to avoid more retries */
if (events & EC_MKBP_CEC_SEND_FAILED)
cec_transmit_attempt_done(port->adap,
CEC_TX_STATUS_MAX_RETRIES |
CEC_TX_STATUS_NACK);
if (events & EC_MKBP_CEC_HAVE_DATA)
cros_ec_cec_read_message(port);
}
static int cros_ec_cec_event(struct notifier_block *nb,
unsigned long queued_during_suspend,
void *_notify)
{
struct cros_ec_cec *cros_ec_cec;
struct cros_ec_device *cros_ec;
cros_ec_cec = container_of(nb, struct cros_ec_cec, notifier);
cros_ec = cros_ec_cec->cros_ec;
if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_EVENT) {
handle_cec_event(cros_ec_cec);
return NOTIFY_OK;
}
if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_MESSAGE) {
handle_cec_message(cros_ec_cec);
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
{
struct cros_ec_cec_port *port = adap->priv;
struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
struct ec_params_cec_set params = {
.cmd = CEC_CMD_LOGICAL_ADDRESS,
.port = port->port_num,
.val = logical_addr,
};
int ret;
ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_SET, &params, sizeof(params),
NULL, 0);
if (ret < 0) {
dev_err(cros_ec->dev,
"error setting CEC logical address on EC: %d\n", ret);
return ret;
}
return 0;
}
static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts,
u32 signal_free_time, struct cec_msg *cec_msg)
{
struct cros_ec_cec_port *port = adap->priv;
struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
struct ec_params_cec_write params;
struct ec_params_cec_write_v1 params_v1;
int ret;
if (cros_ec_cec->write_cmd_version == 0) {
memcpy(params.msg, cec_msg->msg, cec_msg->len);
ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_WRITE_MSG, &params,
cec_msg->len, NULL, 0);
} else {
params_v1.port = port->port_num;
params_v1.msg_len = cec_msg->len;
memcpy(params_v1.msg, cec_msg->msg, cec_msg->len);
ret = cros_ec_cmd(cros_ec, cros_ec_cec->write_cmd_version,
EC_CMD_CEC_WRITE_MSG, &params_v1,
sizeof(params_v1), NULL, 0);
}
if (ret < 0) {
dev_err(cros_ec->dev,
"error writing CEC msg on EC: %d\n", ret);
return ret;
}
return 0;
}
static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable)
{
struct cros_ec_cec_port *port = adap->priv;
struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
struct ec_params_cec_set params = {
.cmd = CEC_CMD_ENABLE,
.port = port->port_num,
.val = enable,
};
int ret;
ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_SET, &params, sizeof(params),
NULL, 0);
if (ret < 0) {
dev_err(cros_ec->dev,
"error %sabling CEC on EC: %d\n",
(enable ? "en" : "dis"), ret);
return ret;
}
return 0;
}
static const struct cec_adap_ops cros_ec_cec_ops = {
.adap_enable = cros_ec_cec_adap_enable,
.adap_log_addr = cros_ec_cec_set_log_addr,
.adap_transmit = cros_ec_cec_transmit,
};
#ifdef CONFIG_PM_SLEEP
static int cros_ec_cec_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
if (device_may_wakeup(dev))
enable_irq_wake(cros_ec_cec->cros_ec->irq);
return 0;
}
static int cros_ec_cec_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
if (device_may_wakeup(dev))
disable_irq_wake(cros_ec_cec->cros_ec->irq);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops,
cros_ec_cec_suspend, cros_ec_cec_resume);
#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI)
/*
* Specify the DRM device name handling the HDMI output and the HDMI connector
* corresponding to each CEC port. The order of connectors must match the order
* in the EC (first connector is EC port 0, ...), and the number of connectors
* must match the number of ports in the EC (which can be queried using the
* EC_CMD_CEC_PORT_COUNT host command).
*/
struct cec_dmi_match {
const char *sys_vendor;
const char *product_name;
const char *devname;
const char *const *conns;
};
static const char *const port_b_conns[] = { "Port B", NULL };
static const char *const port_c_conns[] = { "Port C", NULL };
static const char *const port_db_conns[] = { "Port D", "Port B", NULL };
static const char *const port_ba_conns[] = { "Port B", "Port A", NULL };
static const char *const port_ab_conns[] = { "Port A", "Port B", NULL };
static const char *const port_d_conns[] = { "Port D", NULL };
static const struct cec_dmi_match cec_dmi_match_table[] = {
/* AMD Lilac */
{ "AMD", "Lilac", "0000:03:00.0", port_c_conns },
/* Valve Fremont */
{ "OEM", "F7F", "0000:03:00.0", port_c_conns },
{ "Valve", "Fremont", "0000:03:00.0", port_c_conns },
/* Google Fizz */
{ "Google", "Fizz", "0000:00:02.0", port_b_conns },
/* Google Brask */
{ "Google", "Brask", "0000:00:02.0", port_b_conns },
/* Google Moli */
{ "Google", "Moli", "0000:00:02.0", port_b_conns },
/* Google Kinox */
{ "Google", "Kinox", "0000:00:02.0", port_b_conns },
/* Google Kuldax */
{ "Google", "Kuldax", "0000:00:02.0", port_b_conns },
/* Google Aurash */
{ "Google", "Aurash", "0000:00:02.0", port_b_conns },
/* Google Gladios */
{ "Google", "Gladios", "0000:00:02.0", port_b_conns },
/* Google Lisbon */
{ "Google", "Lisbon", "0000:00:02.0", port_b_conns },
/* Google Dibbi */
{ "Google", "Dibbi", "0000:00:02.0", port_db_conns },
/* Google Constitution */
{ "Google", "Constitution", "0000:00:02.0", port_ba_conns },
/* Google Boxy */
{ "Google", "Boxy", "0000:00:02.0", port_d_conns },
/* Google Taranza */
{ "Google", "Taranza", "0000:00:02.0", port_db_conns },
/* Google Dexi */
{ "Google", "Dexi", "0000:00:02.0", port_db_conns },
/* Google Dita */
{ "Google", "Dita", "0000:00:02.0", port_db_conns },
/* Google Dirks */
{ "Google", "Dirks", "0000:00:02.0", port_ab_conns },
/* Google Moxie */
{ "Google", "Moxie", "0000:00:02.0", port_b_conns },
/* AMD Lilac */
{ "AMD", "Lilac", "0000:06:00.0", port_c_conns },
};
static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
const char * const **conns)
{
int i;
for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) {
const struct cec_dmi_match *m = &cec_dmi_match_table[i];
if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
struct device *d;
/* Find the device, bail out if not yet registered */
d = bus_find_device_by_name(&pci_bus_type, NULL,
m->devname);
if (!d)
return ERR_PTR(-EPROBE_DEFER);
put_device(d);
*conns = m->conns;
return d;
}
}
/* Hardware support must be added in the cec_dmi_match_table */
dev_warn(dev, "CEC notifier not configured for this hardware\n");
return ERR_PTR(-ENODEV);
}
#else
static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
const char * const **conns)
{
return ERR_PTR(-ENODEV);
}
#endif
static int cros_ec_cec_get_num_ports(struct cros_ec_cec *cros_ec_cec)
{
struct ec_response_cec_port_count response;
int ret;
ret = cros_ec_cmd(cros_ec_cec->cros_ec, 0, EC_CMD_CEC_PORT_COUNT, NULL,
0, &response, sizeof(response));
if (ret < 0) {
/*
* Old EC firmware only supports one port and does not support
* the port count command, so fall back to assuming one port.
*/
cros_ec_cec->num_ports = 1;
return 0;
}
if (response.port_count == 0) {
dev_err(cros_ec_cec->cros_ec->dev,
"EC reports 0 CEC ports\n");
return -ENODEV;
}
if (response.port_count > EC_CEC_MAX_PORTS) {
dev_err(cros_ec_cec->cros_ec->dev,
"EC reports too many ports: %d\n", response.port_count);
return -EINVAL;
}
cros_ec_cec->num_ports = response.port_count;
return 0;
}
static int cros_ec_cec_get_write_cmd_version(struct cros_ec_cec *cros_ec_cec)
{
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
struct ec_params_get_cmd_versions_v1 params = {
.cmd = EC_CMD_CEC_WRITE_MSG,
};
struct ec_response_get_cmd_versions response;
int ret;
ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GET_CMD_VERSIONS, &params,
sizeof(params), &response, sizeof(response));
if (ret < 0) {
dev_err(cros_ec->dev,
"error getting CEC write command version: %d\n", ret);
return ret;
}
if (response.version_mask & EC_VER_MASK(1)) {
cros_ec_cec->write_cmd_version = 1;
} else {
if (cros_ec_cec->num_ports != 1) {
dev_err(cros_ec->dev,
"v0 write command only supports 1 port, %d reported\n",
cros_ec_cec->num_ports);
return -EINVAL;
}
cros_ec_cec->write_cmd_version = 0;
}
return 0;
}
static int cros_ec_cec_init_port(struct device *dev,
struct cros_ec_cec *cros_ec_cec,
int port_num, struct device *hdmi_dev,
const char * const *conns)
{
struct cros_ec_cec_port *port;
int ret;
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
port->cros_ec_cec = cros_ec_cec;
port->port_num = port_num;
port->adap = cec_allocate_adapter(&cros_ec_cec_ops, port, DRV_NAME,
CEC_CAP_DEFAULTS |
CEC_CAP_CONNECTOR_INFO, 1);
if (IS_ERR(port->adap))
return PTR_ERR(port->adap);
if (!conns[port_num]) {
dev_err(dev, "no conn for port %d\n", port_num);
ret = -ENODEV;
goto out_probe_adapter;
}
port->notify = cec_notifier_cec_adap_register(hdmi_dev, conns[port_num],
port->adap);
if (!port->notify) {
ret = -ENOMEM;
goto out_probe_adapter;
}
ret = cec_register_adapter(port->adap, dev);
if (ret < 0)
goto out_probe_notify;
cros_ec_cec->ports[port_num] = port;
return 0;
out_probe_notify:
cec_notifier_cec_adap_unregister(port->notify, port->adap);
out_probe_adapter:
cec_delete_adapter(port->adap);
return ret;
}
static int cros_ec_cec_probe(struct platform_device *pdev)
{
struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
struct cros_ec_device *cros_ec = ec_dev->ec_dev;
struct cros_ec_cec *cros_ec_cec;
struct cros_ec_cec_port *port;
struct device *hdmi_dev;
const char * const *conns = NULL;
int ret;
hdmi_dev = cros_ec_cec_find_hdmi_dev(&pdev->dev, &conns);
if (IS_ERR(hdmi_dev))
return PTR_ERR(hdmi_dev);
cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec),
GFP_KERNEL);
if (!cros_ec_cec)
return -ENOMEM;
platform_set_drvdata(pdev, cros_ec_cec);
cros_ec_cec->cros_ec = cros_ec;
device_init_wakeup(&pdev->dev, 1);
ret = cros_ec_cec_get_num_ports(cros_ec_cec);
if (ret)
return ret;
ret = cros_ec_cec_get_write_cmd_version(cros_ec_cec);
if (ret)
return ret;
for (int i = 0; i < cros_ec_cec->num_ports; i++) {
ret = cros_ec_cec_init_port(&pdev->dev, cros_ec_cec, i,
hdmi_dev, conns);
if (ret)
goto unregister_ports;
}
/* Get CEC events from the EC. */
cros_ec_cec->notifier.notifier_call = cros_ec_cec_event;
ret = blocking_notifier_chain_register(&cros_ec->event_notifier,
&cros_ec_cec->notifier);
if (ret) {
dev_err(&pdev->dev, "failed to register notifier\n");
goto unregister_ports;
}
return 0;
unregister_ports:
/*
* Unregister any adapters which have been registered. We don't add the
* port to the array until the adapter has been registered successfully,
* so any non-NULL ports must have been registered.
*/
for (int i = 0; i < cros_ec_cec->num_ports; i++) {
port = cros_ec_cec->ports[i];
if (!port)
break;
cec_notifier_cec_adap_unregister(port->notify, port->adap);
cec_unregister_adapter(port->adap);
}
return ret;
}
static void cros_ec_cec_remove(struct platform_device *pdev)
{
struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
struct cros_ec_cec_port *port;
int ret;
/*
* blocking_notifier_chain_unregister() only fails if the notifier isn't
* in the list. We know it was added to it by .probe(), so there should
* be no need for error checking. Be cautious and still check.
*/
ret = blocking_notifier_chain_unregister(
&cros_ec_cec->cros_ec->event_notifier,
&cros_ec_cec->notifier);
if (ret)
dev_err(dev, "failed to unregister notifier\n");
for (int i = 0; i < cros_ec_cec->num_ports; i++) {
port = cros_ec_cec->ports[i];
cec_notifier_cec_adap_unregister(port->notify, port->adap);
cec_unregister_adapter(port->adap);
}
}
static const struct platform_device_id cros_ec_cec_id[] = {
{ DRV_NAME, 0 },
{}
};
MODULE_DEVICE_TABLE(platform, cros_ec_cec_id);
static struct platform_driver cros_ec_cec_driver = {
.probe = cros_ec_cec_probe,
.remove = cros_ec_cec_remove,
.driver = {
.name = DRV_NAME,
.pm = &cros_ec_cec_pm_ops,
},
.id_table = cros_ec_cec_id,
};
module_platform_driver(cros_ec_cec_driver);
MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
MODULE_LICENSE("GPL");
+51
View File
@@ -0,0 +1,51 @@
#!/bin/bash
# Steamify: gamescope-session starts gamescope without a size or a refresh rate, so it takes the display's *preferred* mode,
# which many monitors declare as 60 Hz (the faster modes sit in an extension block of the EDID). This starts it the way the
# desktop runs that display: the mode KDE has saved for the connected display (System Settings > Display), when the display
# still has it, else its biggest mode at the highest refresh rate.
# Read at every session start (modetest, part of libdrm which gamescope needs, and KDE's ~/.config/kwinoutputconfig.json), so
# changing the resolution in KDE, another monitor or a TV just works. Anything unclear (no modetest output, no modes, more than
# one display connected: gamescope chooses which one it uses) leaves gamescope's own choice alone.
real=/usr/bin/gamescope
modes() {
# One line per mode of every connected connector: "<connector> <width> <height> <refresh>"
local m
for m in nvidia-drm amdgpu i915 xe nouveau radeon; do
timeout 10 modetest -M "$m" -c 2>/dev/null | awk '
/^[0-9]+[ \t]+[0-9]+[ \t]+(connected|disconnected|unknown)/ { conn = ($3 == "connected") ? $4 : ""; next }
conn != "" && /^[ \t]+#[0-9]+ [0-9]+x[0-9]+ [0-9.]+ / && !/interlace/ {
split($2, wh, "x"); printf "%s %d %d %.0f\n", conn, wh[1], wh[2], $3
}'
done
}
kde_mode() {
# kde_mode <connector>: "<width> <height> <refresh>" as KDE has it saved for that connector, or nothing.
python3 -I - "$1" "${XDG_CONFIG_HOME:-$HOME/.config}/kwinoutputconfig.json" 2>/dev/null << 'EOF'
import json, sys
try:
for block in json.load(open(sys.argv[2])):
if block.get("name") == "outputs":
for o in block.get("data", []):
m = o.get("mode")
if o.get("connectorName") == sys.argv[1] and m:
print(m["width"], m["height"], round(m["refreshRate"] / 1000))
except Exception:
pass
EOF
}
args=()
all="$(modes)"
conn="$(awk '{print $1}' <<< "$all" | sort -u)"
if [[ -n "$conn" && "$(wc -l <<< "$conn")" == 1 ]]; then
read -r w h r <<< "$(kde_mode "$conn")"
# KDE's mode only counts when the display really has it (a refresh rate within 1 Hz).
if [[ -z "${w:-}" ]] || ! awk -v w="$w" -v h="${h:-0}" -v r="${r:-0}" '$2 == w && $3 == h && ($4 - r < 2 && r - $4 < 2) {f = 1} END {exit !f}' <<< "$all"; then
read -r _ w h r < <(awk '{print $2 * $3, $2, $3, $4}' <<< "$all" | sort -k1,1nr -k4,4nr | head -1 | awk '{print "x", $2, $3, $4}')
fi
[[ "${w:-0}" -gt 0 && "${h:-0}" -gt 0 && "${r:-0}" -gt 0 ]] && args=(-W "$w" -H "$h" -r "$r")
fi
if [[ "${1:-}" == --print-args ]]; then echo "${args[*]}"; exit 0; fi
exec "$real" "${args[@]}" "$@"
File diff suppressed because it is too large. Load diff
+128
View File
@@ -0,0 +1,128 @@
# Maintainer: theupriser
# Based on Arch Linux's gamescope PKGBUILD (Maxime Gauduin, Giancarlo Razzolini, Sven-Hendrik Haase and contributors).
#
# gamescope with GBM-allocated scan-out buffers for NVIDIA (opt-in: gamescope_drm_gbm_scanout=1).
# The patches are gamescope-gbm-scanout.patch and gamescope-ui-native.patch (next to this file, applied in this order); see the
# README for where they come from.
# UPSTREAM_VERSION (next to this file) names the gamescope tag the patches are made for.
pkgname=steamify-gamescope-nvidia
_srcname=gamescope
pkgver=3.16.31
pkgrel=3
pkgdesc='SteamOS session compositing window manager, with GBM scan-out for NVIDIA (opt-in: gamescope_drm_gbm_scanout=1)'
arch=(x86_64)
url=https://github.com/theupriser/steamify-gamescope-nvidia
license=(
BSD-2-Clause
BSD-3-Clause
LicenseRef-Reshade
)
provides=("gamescope=$pkgver")
conflicts=(gamescope gamescope-nvidia-gbm)
replaces=(gamescope-nvidia-gbm)
depends=(
glibc
mesa
libstdc++
libgcc
lcms2
libavif
libcap.so
libdecor
libdrm
libei
libinput
libpipewire-0.3.so
libx11
libxcb
libxcomposite
libxcursor
libxdamage
libxext
libxfixes
libxi
libxkbcommon.so
libxmu
libxrender
libxres
libxtst
libxxf86vm
luajit
pixman
sdl2
seatd
systemd-libs
vulkan-icd-loader
wayland
xcb-util-errors
xcb-util-wm
xorg-server-xwayland
)
makedepends=(
benchmark
cmake
git
glslang
meson
ninja
vulkan-headers
wayland-protocols
catch2
)
source=(
gamescope::git+https://github.com/ValveSoftware/gamescope.git#commit=${pkgver}
gamescope-gbm-scanout.patch
gamescope-ui-native.patch
git+https://gitlab.freedesktop.org/emersion/libdisplay-info.git
git+https://gitlab.freedesktop.org/emersion/libliftoff.git
git+https://github.com/ValveSoftware/openvr.git
git+https://github.com/Joshua-Ashton/reshade.git
git+https://github.com/KhronosGroup/SPIRV-Headers.git
git+https://github.com/Joshua-Ashton/vkroots.git
git+https://gitlab.freedesktop.org/wlroots/wlroots.git
)
b2sums=('SKIP'
'SKIP'
'SKIP'
'SKIP'
'SKIP'
'SKIP'
'SKIP'
'SKIP'
'SKIP'
'SKIP')
prepare() {
cd gamescope
# GBM scan-out for NVIDIA. If it no longer applies to this gamescope version, stop here: the build must not
# silently produce an unpatched gamescope under this package's name.
git apply --check ../gamescope-gbm-scanout.patch || { echo "gbm patch does not apply to gamescope ${pkgver}" >&2; return 1; }
git apply ../gamescope-gbm-scanout.patch
git apply --check ../gamescope-ui-native.patch || { echo "ui-native patch does not apply to gamescope ${pkgver}" >&2; return 1; }
git apply ../gamescope-ui-native.patch
meson subprojects download
for submodule in src/reshade subprojects/{libdisplay-info,libliftoff,openvr,vkroots,wlroots} thirdparty/SPIRV-Headers; do
git submodule init ${submodule}
git config submodule.${submodule}.url ../${submodule##*/}
done
git -c protocol.file.allow=always submodule update
}
build() {
arch-meson gamescope build \
-Dforce_fallback_for=glm,stb,libdisplay-info,libliftoff,vkroots \
-Dpipewire=enabled
meson compile -C build
}
package() {
DESTDIR="${pkgdir}" meson install -C build \
--skip-subprojects
install -Dm 644 gamescope/LICENSE -t "${pkgdir}"/usr/share/licenses/${pkgname}/
}
# vim: ts=2 sw=2 et:
+39
View File
@@ -0,0 +1,39 @@
Keeps Steam's UI X server (server 0) at the output size when Steam asks for a smaller one.
With -steamos3 (what gamescope-session-cachyos passes) Steam sets its UI to 1920x1080 right after it starts
(GAMESCOPE_XWAYLAND_MODE_CONTROL), so on a bigger display, and on one that isn't 16:9, the whole interface is a scaled-up
1080p picture. Games already run at the display's size. With this the server stays at the output size and Steam draws its
window to match; Steam's SteamOS features (settings pages, game launching) keep working. Written for Steamify on an NVIDIA
PC (RTX 5080, 3440x1440@175, tested with Steam's UI and a Proton game); not part of the farnoy patch. On by default; the
setting gamescope_xwayland_ui_native=0 (in gamescope's environment) turns it off.
Applies with `git apply` after gamescope-gbm-scanout.patch (it ignores these lines).
diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
index 2cb5eb5..ce14c8d 100644
--- a/src/steamcompmgr.cpp
+++ b/src/steamcompmgr.cpp
@@ -129,6 +129,8 @@ LogScope g_WaitableLog("waitable");
gamescope::ConVar<bool> cv_overlay_unmultiplied_alpha{ "overlay_unmultiplied_alpha", false };
+gamescope::ConVar<bool> cv_xwayland_ui_native( "xwayland_ui_native", true, "Keep the Steam UI X server (server 0) at the output size when Steam asks for a smaller one." );
+
gamescope::ConVar<bool> cv_vr_show_forwarded_overlays{ "vr_show_forwarded_overlays", false };
std::string *g_pVROverlayKey = nullptr;
@@ -7240,6 +7242,15 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
height = std::min<int>(height, currentOutputHeight);
}
+ // Steam asks for a smaller size for its own UI (server 0, 1920x1080 with -steamos3), which makes the whole
+ // interface a scaled-up picture on a bigger display, and on one that isn't 16:9 as well. The UI server
+ // stays at the output size: Steam sizes its window to the server.
+ if ( server_idx == 0 && cv_xwayland_ui_native )
+ {
+ width = currentOutputWidth;
+ height = currentOutputHeight;
+ }
+
gamescope_xwayland_server_t *server = wlserver_get_xwayland_server( server_idx );
if ( server )
{
+17
View File
@@ -0,0 +1,17 @@
# Maintainer: theupriser
# An empty package that only tells pacman "python-hid is there". For a PC that keeps python-hidapi (liquidctl needs it):
# the real python-hid conflicts with it, and jupiter-hw-support (a dependency of gamescope-session-cachyos) wants python-hid.
# Only Valve's Steam Deck controller firmware updater in jupiter-hw-support uses it, which a PC doesn't run.
# Steamify (lib/packages.sh) builds it with makepkg and installs it before the conversion's packages.
pkgname=steamify-python-hid-shim
pkgver=1
pkgrel=1
pkgdesc='Placeholder: python-hid for PCs that keep python-hidapi (they conflict)'
arch=(any)
license=(MIT)
depends=(python-hidapi)
provides=('python-hid=99')
package() {
:
}
+5
View File
@@ -7,6 +7,11 @@
* leaves set on GPIO pin 18 (_SB.PCI0.GPP6) stays set, and the machine starts again right after powering off.
* Valve's kernel clears that bit at probe on Fremont (not for upstream);
* this does the same right before power-off, for kernels without it.
*
* What it does follows Valve's Steam Machine kernel
* (https://github.com/evlaV/linux-integration, GPL-2.0 like the rest of the
* Linux kernel); this module is Steamify's own implementation of it, also
* GPL-2.0. Not affiliated with or endorsed by Valve Corporation.
*/
#include <linux/acpi.h>
#include <linux/bits.h>
+17
View File
@@ -0,0 +1,17 @@
# Installed by Steamify CachyOS (SteamOS conversion, NVIDIA fix): decides
# again whether the NVIDIA modules go into the initramfs, after DKMS has built
# its modules (71-dkms-install) and before the initramfs is built
# (90-mkinitcpio-install); hooks run in file name order.
[Trigger]
Operation = Install
Operation = Upgrade
Operation = Remove
Type = Path
Target = usr/lib/modules/*/vmlinuz
Target = usr/lib/modules/*/extramodules/nvidia*
Target = usr/lib/modules/*/updates/*
[Action]
Description = Steamify: NVIDIA modules in the initramfs only if every kernel has them
When = PostTransaction
Exec = @SCRIPT@ sync
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# Installed by Steamify CachyOS (SteamOS conversion, NVIDIA fix), run by the
# pacman hook steamify-nvidia-initramfs.hook before the initramfs is built.
# The NVIDIA modules go into the initramfs (early KMS) only while EVERY
# installed kernel has them: mkinitcpio fails on a module a kernel lacks, and
# limine-mkinitcpio then skips that kernel's boot entry, kernel parameters
# included. Decided again at every kernel or driver change, so a new kernel
# without the modules (another kernel, a failed DKMS build, a downgrade) gets
# a working initramfs and the file comes back once it has them.
# steamify-nvidia-initramfs [sync|check]
# sync (default) write or remove the drop-in to match
# check exit status 0 when every installed kernel has nvidia_drm
conf="${NVIDIA_INITRAMFS_CONF:-/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf}"
mods="${NVIDIA_MODULES_DIR:-/usr/lib/modules}"
everywhere() {
local k found=0
for k in "$mods"/*/; do
[[ -f "$k/vmlinuz" || -f "$k/pkgbase" ]] || continue # an installed kernel, not a leftover folder
find "$k" -name 'nvidia-drm.ko*' -print -quit 2>/dev/null | grep -q . || return 1
found=1
done
[[ $found == 1 ]]
}
case "${1:-sync}" in
check) everywhere ;;
sync)
if everywhere; then
[[ -f "$conf" ]] || { mkdir -p "$(dirname "$conf")" &&
echo 'MODULES+=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)' > "$conf"; }
else
rm -f "$conf"
fi ;;
*) echo "usage: $0 [sync|check]" >&2; exit 2 ;;
esac
+14
View File
@@ -0,0 +1,14 @@
# Installed by Steamify CachyOS (SteamOS conversion): runs after
# cachyos-hooks' os-release/cachyos-branding hooks (hooks run in file name
# order).
[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = filesystem
Target = cachyos-hooks
[Action]
Description = Steamify: the OS variant and version Steam shows
When = PostTransaction
Exec = @SCRIPT@
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
# Installed by Steamify CachyOS (SteamOS conversion): what Steam shows in its
# System settings besides the OS name, which stays CachyOS's: the SteamOS
# release Steamify follows as the OS version (VERSION_ID, e.g. steamos-3.9),
# steam-machine as the codename, and Steamify with its version as the variant
# (VARIANT_ID, which Steam shows). KDE's About this System would show
# VERSION_ID after the name: the conversion sets its UseOSReleaseVersion, so it
# shows os-release's VERSION instead, which CachyOS doesn't have. NAME and
# PRETTY_NAME are never touched: limine-snapper-sync finds the boot entries by
# the OS name. The zz-steamify-os-release hook runs this after cachyos-hooks.
sed -i -e '/^VARIANT=/d' -e '/^VARIANT_ID=/d' -e '/^VERSION_ID=/d' -e '/^VERSION_CODENAME=/d' /etc/os-release
printf '%s\n' 'VARIANT="Steamify @VERSION@"' 'VARIANT_ID=steamify-@VERSION@' 'VERSION_CODENAME=steam-machine' >> /etc/os-release
[ -n "@STEAMOS@" ] && echo 'VERSION_ID=@STEAMOS@' >> /etc/os-release
exit 0
+1 -1
View File
@@ -11,6 +11,6 @@ in the shell code.
|---|---|---|
| `steamify-boot-desktop.service` | `lib/boot-session.sh` (Boot into: desktop) | Sets the Plasma session before the login manager starts |
| `sync-steamos-session.path`, `.service` | `lib/login-manager.sh` (SteamOS conversion, plasma-login-manager) | The sync bridge: copies Steam's session choice into `plasmalogin.conf` |
| `steam-desktop-autostart.service` | `lib/steam-desktop.sh` (SteamOS conversion) | Steam in the background on the Plasma desktop (user unit) |
| `steamify-steam-autostart.service` | `lib/nvidia.sh` (Gaming on NVIDIA) | Steam at login on the Plasma desktop, normal or in Big Picture (user unit) |
| `ensure-kernel-headers.service` | `lib/steam-machine.sh` (Steam Machine support) | Installs missing kernel headers at boot, so DKMS builds for every kernel |
| `steamify-update-check.service`, `.timer` | `lib/update-notifier.sh` (Update notifications) | The daily and login check for a new release (user units) |
@@ -1,13 +1,13 @@
[Unit]
Description=Steam Background Autostart for Virtual Keyboard
Description=Steam at login (Steamify, NVIDIA PCs)
After=graphical-session.target
PartOf=graphical-session.target
[Service]
Type=simple
# Only in the Plasma desktop session; gamescope-session starts its own Steam.
# Only in the Plasma desktop session.
ExecCondition=/bin/sh -c '[ "$XDG_CURRENT_DESKTOP" = KDE ]'
ExecStart=/usr/bin/steam -silent
ExecStart=/usr/bin/steam @ARGS@
Restart=on-failure
RestartSec=5
+43 -4
View File
@@ -15,11 +15,11 @@
set -uo pipefail
# Release version, see CHANGELOG.md.
VERSION=2.6.0
VERSION=2.12.0
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
for lib in common state packages login-manager single-user steam-desktop steam-machine fremont-poweroff vram-booster hdmi-refresh cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut steam-game update-notifier first-login menu backend; do
for lib in common state packages login-manager single-user steam-desktop steam-machine fremont-poweroff vram-booster hdmi-refresh nvidia gamescope-nvidia controllers cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut steam-game update-notifier first-login menu backend; do
# shellcheck source=/dev/null
source "$SCRIPT_DIR/lib/$lib.sh"
done
@@ -28,12 +28,21 @@ require_root_helper
BACKEND=false
[[ "${1:-}" == --backend ]] && BACKEND=true
# --defaults: apply what the menu would preselect, without the menu or any
# --defaults [--options <id>,...] [--boot gamescope|desktop]: apply what the
# menu would preselect (or exactly the listed items), without the menu or any
# prompt (the Steam Machine ISO's first login runs this; sudo must not ask).
DEFAULTS=false
[[ "${1:-}" == --defaults ]] && DEFAULTS=true
# --defaults --list: what --defaults can set up here, as JSON (an installer
# page is built from it); nothing else on stdout.
LIST=false
[[ "$DEFAULTS" == true && "${2:-}" == --list ]] && LIST=true
# --boot gamescope|desktop: only change where an installed conversion
# starts, everything else stays as it is (for scripts; no menu).
BOOT_ONLY=false
[[ "${1:-}" == --boot ]] && BOOT_ONLY=true
if [[ "$BACKEND" == false ]]; then
if [[ "$BACKEND" == false && "$LIST" == false ]]; then
echo -e "${c_bold}Steamify CachyOS${c_reset} v$VERSION"
echo "Turn the SteamOS-style parts on or off. The menu shows what is on now;"
echo "anything you turn off is put back the way it was."
@@ -123,11 +132,17 @@ if [[ "${1:-}" == --first-login ]]; then
exit 0
fi
if [[ "$LIST" == true ]]; then
defaults_list
exit 0
fi
if [[ "$DEFAULTS" == true ]]; then
RESTART_FOR_LOGIN=false
REAPPLY=false
sudo -n true 2>/dev/null || { err "--defaults needs sudo without a password."; exit 1; }
detect_components
defaults_options "${@:2}" || exit 1
plan_changes
apply_changes
# Run by the installer: the rest waits for the first desktop login.
@@ -137,6 +152,30 @@ if [[ "$DEFAULTS" == true ]]; then
exit 0
fi
if [[ "$BOOT_ONLY" == true ]]; then
RESTART_FOR_LOGIN=false
REAPPLY=false
case "${2:-}" in
gamescope|desktop) ;;
*) err "--boot takes gamescope or desktop"; exit 1 ;;
esac
detect_components
[[ "${CURRENT[gaming]}" == 1 ]] || { err "--boot needs the SteamOS conversion; turn it on first."; exit 1; }
# What is on stays on, and nothing else changes: not the updates or
# removals a normal run would pick.
for c in "${COMPONENTS[@]}"; do WANTED[$c]=${CURRENT[$c]:-0}; done
WANTED[boot]=0; [[ "$2" == desktop ]] && WANTED[boot]=1
plan_changes
if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then
ok "Already starting in $2."
exit 0
fi
apply_changes
[[ ${#FAILED[@]} -eq 0 ]] || { warn "These had problems (see above): ${FAILED[*]}"; exit 1; }
ok "Starts in $2 from the next boot on."
exit 0
fi
# Menu loop: after each run the menu comes back with the new state, until
# the user quits; the restart question comes then, once, for everything.
RESTART_FOR_LOGIN=false
+18 -6
View File
@@ -38,7 +38,9 @@ Item {
readonly property var status: backend.status || ({})
readonly property var items: status.items || []
readonly property var rows: items.filter(function (i) { return !i.parent || want[i.parent]; })
// Hidden: Big Picture excludes a silent start.
function hidden(i) { return i.id === "nvsilent" && want.bigpicture; }
readonly property var rows: items.filter(function (i) { return (!i.parent || want[i.parent]) && !hidden(i); })
readonly property var bios: status.bios || null
signal passwordRequested()
@@ -63,7 +65,8 @@ Item {
var w = {};
for (var i = 0; i < items.length; i++) w[items[i].id] = items[i].kind === "action" ? false : items[i].wanted;
want = w;
boot = nowOn("boot") ? "desktop" : "gamescope";
// (What the PC has, or what the backend ticked: "Gaming on NVIDIA" moving to the conversion keeps how Steam started.)
boot = want.boot ? "desktop" : "gamescope";
if (sel > rows.length) sel = 0;
}
Connections { target: backend; function onStatusChanged() { if (app.screen === "menu" || app.screen === "done") app.syncFromStatus(); } }
@@ -75,8 +78,15 @@ Item {
var w = Object.assign({}, want);
w[id] = !w[id];
// The same rules as the terminal menu (toggle_component).
if (id === "gaming" && !w.gaming) w.single = false;
if (id === "single" && w.single) w.gaming = true;
var hasGaming = items.some(function (i) { return i.id === "gaming"; });
if (id === "gaming" && !w.gaming) { w.single = false; w.silent = false; w.glyphs = false; }
if (id === "single" && w.single && hasGaming) w.gaming = true;
if (id === "nvidia") w.bigpicture = w.nvidia;
if (id === "bigpicture" && w.bigpicture) { w.nvidia = true; w.nvsilent = false; }
if (id === "nvsilent" && w.nvsilent) { w.nvidia = true; w.bigpicture = false; }
if (id === "nvidia" && !w.nvidia) w.nvsilent = false;
if (id === "silent" && w.silent) w.gaming = true;
if (id === "glyphs" && w.glyphs) w.gaming = true;
if (id === "machine") w.poweroff = w.machine;
if (id === "poweroff" && w.poweroff) w.machine = true;
if (id === "machine" && !w.machine) w.kpin = false;
@@ -90,8 +100,10 @@ Item {
for (var i = 0; i < items.length; i++) {
var it = items[i];
if (it.kind !== "toggle") continue;
if (hidden(it)) { if (it.on) p.push({ id: it.id, action: "off" }); continue; }
if (it.parent && !want[it.parent]) { if (it.on) p.push({ id: it.id, action: "off" }); continue; }
if (want[it.id] && !it.on) p.push({ id: it.id, action: "on", isNew: !!it["new"] });
// (An item that is off but flagged "update" replaces what the PC has: the conversion for "Gaming on NVIDIA".)
if (want[it.id] && !it.on) p.push({ id: it.id, action: it.update ? "update" : "on", isNew: !!it["new"] });
else if (!want[it.id] && it.on) p.push({ id: it.id, action: "off" });
else if (want[it.id] && (reapply || it.update)) p.push({ id: it.id, action: reapply ? "again" : "update" });
}
@@ -106,7 +118,7 @@ Item {
function wantedIds() {
var ids = [];
for (var i = 0; i < items.length; i++)
if (items[i].kind === "toggle" && want[items[i].id] && (!items[i].parent || want[items[i].parent])) ids.push(items[i].id);
if (items[i].kind === "toggle" && want[items[i].id] && !hidden(items[i]) && (!items[i].parent || want[items[i].parent])) ids.push(items[i].id);
return ids;
}
+8 -2
View File
@@ -35,9 +35,15 @@ Item {
border.width: step.st === "wait" || step.st === "run" ? 2 : 0; border.color: step.st === "run" ? Theme.accent : "#343f50"
Text { anchors.centerIn: parent; text: step.st === "ok" ? "✓" : (step.st === "fail" ? "!" : ""); color: step.st === "ok" ? Theme.good : Theme.bad
font.pixelSize: 13; font.weight: Font.Bold }
RotationAnimator on rotation { running: step.st === "run"; from: 0; to: 360; duration: 1000; loops: Animation.Infinite } }
// Only this dot turns while the step runs: ✓ and ! never rotate
// (an animator leaves its item at whatever angle it stopped).
Item { anchors.fill: parent; visible: step.st === "run"
Rectangle { width: 6; height: 6; radius: 3; color: Theme.accent; x: parent.width / 2 - 3; y: -3 }
RotationAnimator on rotation { running: step.st === "run"; from: 0; to: 360; duration: 1000; loops: Animation.Infinite } } }
Row { width: 380; anchors.verticalCenter: parent.verticalCenter; spacing: 10
Text { text: (Texts.actions[step.modelData.action] || [])[3] + Texts.label(step.modelData.id)
// Boot into's label is its prefix already: "Boot into desktop", not "Boot into Boot into".
Text { text: step.modelData.id === "boot" ? (Texts.actions[step.modelData.action] || [])[3] + Texts.actions[step.modelData.action][0].toLowerCase()
: (Texts.actions[step.modelData.action] || [])[3] + Texts.label(step.modelData.id)
color: step.st === "wait" ? Theme.faint : Theme.textHi; font.family: Theme.body; font.pixelSize: 16; font.weight: Font.DemiBold
anchors.verticalCenter: parent.verticalCenter; elide: Text.ElideRight
width: Math.min(implicitWidth, parent.width - (badge.visible ? badge.width + parent.spacing : 0)) }
+16 -1
View File
@@ -6,10 +6,22 @@ QtObject {
readonly property var items: ({
gaming: { label: "SteamOS conversion", hint: "Boot into gaming mode, Steam on the desktop",
body: "Boots straight into gaming mode. Switch to Desktop in Steam works, and Return to Gaming Mode on the desktop brings you back.",
changes: ["Autologin into gamescope", "Return to Gaming Mode shortcut", "Steam starts silently on the desktop"] },
changes: ["Autologin into gamescope", "Return to Gaming Mode shortcut"] },
boot: { label: "Boot into", hint: "Where the PC starts",
body: "Where the PC starts after a restart. Switching back and forth works the same either way.",
changes: ["Desktop: the Plasma session is set before login", "Gaming: the SteamOS default"] },
silent: { label: "Start Steam silently in desktop mode", hint: "In the tray at login on the desktop, no window",
body: "Steam starts by itself when you log in to the Plasma desktop, in the tray without a window. Gaming mode starts its own Steam.",
changes: ["Steam is started with -silent (a systemd user service, desktop only)", "Off: the service is removed again"] },
nvidia: { label: "Gaming on NVIDIA", hint: "Steam on the desktop, started at login",
body: "gamescope's own gaming mode shows a corrupted picture on NVIDIA graphics cards (an open NVIDIA driver bug), so it isn't offered here. This puts Steam on the Plasma desktop instead and starts it when you log in.",
changes: ["Steam is installed when it's missing", "Steam starts at login (a systemd user service)", "Off: the service is removed again; Steam is only removed if Steamify installed it"] },
bigpicture: { label: "Steam starts in Big Picture", hint: "The controller-friendly Steam, or the normal window",
body: "Steam opens in Big Picture at login, which works well with a controller on the TV. Untick it for Steam's normal window.",
changes: ["Steam is started with -gamepadui", "Plasma starts with an empty session, so old windows don't cover Big Picture", "Off: Steam starts in its normal window"] },
nvsilent: { label: "Start Steam silently", hint: "In the tray at login, no window",
body: "Steam starts in the tray at login without a window. Not together with Big Picture: ticking one unticks the other.",
changes: ["Steam is started with -silent", "Off: Steam starts in its normal window"] },
theme: { label: "SteamOS theme", hint: "Vapor look for the desktop, Add to Steam",
body: "The Vapor look of SteamOS for the desktop, with its panel, launcher icon and wallpaper.",
changes: ["cachyos-vapor global theme", "Add to Steam in right-click menus", "Nested Desktop, Steam keyboard window rule"] },
@@ -27,6 +39,9 @@ QtObject {
notify: { label: "Update notifications", hint: "A notification when there's a new Steamify",
body: "Checks once a day and when you log in to the desktop. When there's a new Steamify you get a notification and a tray icon: open the app to see what's new and choose what to update, or skip that version. It never updates anything by itself.",
changes: ["A daily check (systemd user timer), only on the desktop", "Tray icon only while an update is waiting", "Skip this version: quiet until the next one"] },
extended_controller_support: { label: "Extended controller support", hint: "Xbox wireless dongle, Xbox controllers over Bluetooth",
body: "Drivers for Xbox controllers that Linux doesn't handle well by itself. xone makes the Xbox wireless dongle work (the kernel has no driver for it), xpadneo gives an Xbox controller over Bluetooth rumble including the triggers, the right button mapping and its battery level. They are built for every installed kernel, which takes a minute or two each.",
changes: ["xone (the dongle) with its firmware, and xpadneo (Bluetooth), from the CachyOS repo", "Kernel headers for every installed kernel, so the drivers build for each", "Off: only what Steamify installed is removed again"] },
vram: { label: "VRAM booster", hint: "The game in front keeps its VRAM",
body: "Like SteamOS 3.9: the game you're playing keeps its video memory, and background apps are moved out first. Without it a game that needs most of the video memory can spill into system RAM and stutter. Shown for GPUs whose driver supports it (AMD, Intel, NVIDIA with its open kernel modules from driver 615) on kernel 7.2 or newer.",
changes: ["dmemcg-booster (system and user service)", "plasma-foreground-booster, which tells it which window is in front", "Off: removed again, unless you had installed them yourself"] },
+4 -4
View File
@@ -1,12 +1,12 @@
#!/bin/bash
# Steamify CachyOS - get and start the app (v2), for `curl | bash`:
# Steamify CachyOS - get and start the app, for `curl | bash`:
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify-app.sh | bash
# Downloads the newest release's app package (steamify-app.tar.gz: the app,
# steamify.sh, lib/, patches/ and services/) to ~/.local/share/steamify/app, installs PySide6 if
# it's missing, adds a launcher entry, and starts the app.
# STEAMIFY_RELEASE=<tag> takes that release instead of the newest (the
# v2-ui-preview build's copy of this script sets it). STEAMIFY_BRANCH=<branch>
# takes that branch's source instead (for testing before a build).
# STEAMIFY_RELEASE=<tag> takes that release instead of the newest.
# STEAMIFY_BRANCH=<branch> takes that branch's source instead (for testing
# before a build).
set -euo pipefail
REPO="theupriser/steamify-cachyos"