mirror of
https://github.com/theupriser/steamify-cachyos.git
synced 2026-10-03 17:41:58 +02:00
Compare commits
278
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9924617a2d | ||
|
|
5da1833a03 | ||
|
|
62cb75d161 | ||
|
|
2c004322af | ||
|
|
3915e1a96f | ||
|
|
098dc3d0d8 | ||
|
|
48352b5601 | ||
|
|
05b8f11c19 | ||
|
|
8b96e2d8a9 | ||
|
|
f1fb18ec81 | ||
|
|
1987fa903b | ||
|
|
b7dc931a4f | ||
|
|
3d20a7edb0 | ||
|
|
f1983a9caf | ||
|
|
01a77b7dc7 | ||
|
|
83248a8478 | ||
|
|
5811f5c8a4 | ||
|
|
71bb2e7aca | ||
|
|
d0e4d8cf79 | ||
|
|
d1373e28a6 | ||
|
|
d6db4f6f2d | ||
|
|
a5e9015d93 | ||
|
|
ae238ff383 | ||
|
|
c1217bcea3 | ||
|
|
f3d67fbe52 | ||
|
|
84cf0959e4 | ||
|
|
7caefb7da6 | ||
|
|
453656a0a2 | ||
|
|
6aa1377e16 | ||
|
|
6bcae8af18 | ||
|
|
3cbeb58211 | ||
|
|
83561bb214 | ||
|
|
ca027cb607 | ||
|
|
d93f9ea56c | ||
|
|
761f921e40 | ||
|
|
7a54a2fa59 | ||
|
|
9eab3978a9 | ||
|
|
9602d9d00b | ||
|
|
9a2965d339 | ||
|
|
c42d5454bf | ||
|
|
b3bed43854 | ||
|
|
3e79451244 | ||
|
|
971e792054 | ||
|
|
6557320c81 | ||
|
|
692e3fcbad | ||
|
|
70ecb563fd | ||
|
|
a7276cfd9a | ||
|
|
b3549d4eea | ||
|
|
a232a55bef | ||
|
|
397b3cb607 | ||
|
|
bda1d3ebbe | ||
|
|
3676c64c90 | ||
|
|
d44f29956a | ||
|
|
2858d13429 | ||
|
|
48cf3c7c7c | ||
|
|
619ef1f714 | ||
|
|
b6240539e6 | ||
|
|
7d6df21627 | ||
|
|
3cad70dcc4 | ||
|
|
b8742a5642 | ||
|
|
7f5d29c997 | ||
|
|
cfe3ce7bd0 | ||
|
|
3e4133c14a | ||
|
|
c4b8db85dc | ||
|
|
350011877b | ||
|
|
b1d79d48a6 | ||
|
|
5b42036786 | ||
|
|
4a64232fad | ||
|
|
4ec92f4d89 | ||
|
|
fe4f3f43be | ||
|
|
4bacc3b2e6 | ||
|
|
cc0ca1c35e | ||
|
|
580cc71b57 | ||
|
|
c87e6c763f | ||
|
|
fc05725a91 | ||
|
|
90bc5e4408 | ||
|
|
d99ff652dc | ||
|
|
2aa3068b60 | ||
|
|
1a6ddcfc0d | ||
|
|
11668db91c | ||
|
|
3316c1de28 | ||
|
|
d9365dac81 | ||
|
|
853a3926ee | ||
|
|
66ea63cf7e | ||
|
|
f560f332a9 | ||
|
|
9b86cd1a6c | ||
|
|
a72a4def71 | ||
|
|
ea051c77f6 | ||
|
|
4e829c6111 | ||
|
|
d73e71d4eb | ||
|
|
8f2d2be2aa | ||
|
|
53b72c8e33 | ||
|
|
3b4f3b2313 | ||
|
|
a72d848e78 | ||
|
|
3a58c72d16 | ||
|
|
27a40702ce | ||
|
|
8ab066f034 | ||
|
|
44bfe071d6 | ||
|
|
e281d5e879 | ||
|
|
435f69c151 | ||
|
|
9319cbca1e | ||
|
|
cf6bee1c86 | ||
|
|
16487ba00c | ||
|
|
07a94cd274 | ||
|
|
ce71b34b0d | ||
|
|
d30d505f7d | ||
|
|
9755cc3ac3 | ||
|
|
6f26e74229 | ||
|
|
2a01583b8f | ||
|
|
c40aa1f7d9 | ||
|
|
d204efb59d | ||
|
|
12f6cc0b3a | ||
|
|
b01e2c5221 | ||
|
|
affb5f79b0 | ||
|
|
58e43d4c4a | ||
|
|
23f265b02f | ||
|
|
ced4a45d2f | ||
|
|
6ad686a8b2 | ||
|
|
b1adacbcb3 | ||
|
|
8dd2db3a97 | ||
|
|
501c0ba425 | ||
|
|
bee18b76a8 | ||
|
|
eadbdda271 | ||
|
|
c4ec755a4a | ||
|
|
14aa7c0b54 | ||
|
|
5bf9da411d | ||
|
|
4f6e6fd0aa | ||
|
|
a79eeedbc4 | ||
|
|
b948c2020b | ||
|
|
0c96bed448 | ||
|
|
e3f7cc9773 | ||
|
|
353de489d7 | ||
|
|
d8b2e812c5 | ||
|
|
3a0692fdf2 | ||
|
|
e86ddcc4af | ||
|
|
389bc3346e | ||
|
|
c6c1150836 | ||
|
|
98e2effcff | ||
|
|
431515b60f | ||
|
|
34c9cfdb32 | ||
|
|
8ddb7ebf3d | ||
|
|
32c33e4abf | ||
|
|
2dd0f3ac47 | ||
|
|
57995bcd1f | ||
|
|
d57e8d8448 | ||
|
|
5a34633986 | ||
|
|
479b70b337 | ||
|
|
2e0881b4fa | ||
|
|
cce31e19c2 | ||
|
|
bd4fa3a6f4 | ||
|
|
8ffa0f2449 | ||
|
|
8a2601d73d | ||
|
|
572417bd7b | ||
|
|
cf73229756 | ||
|
|
febdab5b53 | ||
|
|
90cdfd6666 | ||
|
|
6f598d18fd | ||
|
|
9bbe5fa602 | ||
|
|
ceeb949e6b | ||
|
|
03a8b8c4ba | ||
|
|
e699a3f8eb | ||
|
|
7bef834b16 | ||
|
|
86b9744227 | ||
|
|
b0947d7a2f | ||
|
|
3f97c85c3c | ||
|
|
fd162c1e2e | ||
|
|
54ebddfe98 | ||
|
|
fe93dd3e67 | ||
|
|
ef585c0db1 | ||
|
|
32795bd5a5 | ||
|
|
3c8ca65902 | ||
|
|
11a91ef85c | ||
|
|
163f7908c4 | ||
|
|
e121b06fd8 | ||
|
|
19ca966913 | ||
|
|
56fb9080a7 | ||
|
|
6df19eba3e | ||
|
|
7694c97d68 | ||
|
|
a5e644d71a | ||
|
|
929ecfb349 | ||
|
|
4cc87aaea6 | ||
|
|
36d0a9d780 | ||
|
|
cb6361b49b | ||
|
|
c510f9e2e5 | ||
|
|
05a7d50c3f | ||
|
|
badfd846d8 | ||
|
|
ff364ffd69 | ||
|
|
2b3f1de75a | ||
|
|
6d7d5861d6 | ||
|
|
444ce29e57 | ||
|
|
23ef5ef7ab | ||
|
|
178ee3405e | ||
|
|
601c05329d | ||
|
|
8ae9673eb2 | ||
|
|
dbafc43f2e | ||
|
|
ae8ed85fee | ||
|
|
00bcaea712 | ||
|
|
a898d23b13 | ||
|
|
3e53257fe2 | ||
|
|
a1eab9ac79 | ||
|
|
c9c16b9eec | ||
|
|
b17addd60f | ||
|
|
ac613f05b3 | ||
|
|
3a0eefab11 | ||
|
|
96558a9ca8 | ||
|
|
7c48b3d806 | ||
|
|
396459b511 | ||
|
|
43a9541104 | ||
|
|
e09fe1609b | ||
|
|
9dcac5cf08 | ||
|
|
f91dd1f0b1 | ||
|
|
0411429654 | ||
|
|
1aa5e955fe | ||
|
|
ce75e895c4 | ||
|
|
9aef0a986a | ||
|
|
f7aa2412df | ||
|
|
87162efaa2 | ||
|
|
c79a3bbda5 | ||
|
|
ba11f58d9e | ||
|
|
45ff036f09 | ||
|
|
de02f267f4 | ||
|
|
0f123004d0 | ||
|
|
3048b35dd8 | ||
|
|
f7837f82b9 | ||
|
|
3eb41eef5a | ||
|
|
41422e5bb4 | ||
|
|
6c2e203563 | ||
|
|
1522e5632d | ||
|
|
1d994ae359 | ||
|
|
700f3e514b | ||
|
|
d78652c862 | ||
|
|
727e6a2302 | ||
|
|
c4bd1ca39c | ||
|
|
a449a9e467 | ||
|
|
388b1286c9 | ||
|
|
508f22acaf | ||
|
|
3f8e0372d1 | ||
|
|
bd90e29e7a | ||
|
|
4895de3a46 | ||
|
|
0f503dd112 | ||
|
|
d7cf0c0491 | ||
|
|
5ab83d6e6f | ||
|
|
2e107e537e | ||
|
|
986b3c8c07 | ||
|
|
afc05b0b23 | ||
|
|
b0553aecf7 | ||
|
|
69a420b8aa | ||
|
|
1c34a97598 | ||
|
|
be874b8594 | ||
|
|
f01aa93d0e | ||
|
|
852205317a | ||
|
|
77b94c29a2 | ||
|
|
c0014ee623 | ||
|
|
d39731a8b9 | ||
|
|
99a95abf1f | ||
|
|
429e840a9f | ||
|
|
45a8757f58 | ||
|
|
54fc9e362b | ||
|
|
b70c9d614f | ||
|
|
40c9ee638b | ||
|
|
2f9b2a54e1 | ||
|
|
53f53a77ff | ||
|
|
6afef78721 | ||
|
|
1604ed10d4 | ||
|
|
8aab946c6f | ||
|
|
5cbe0079cb | ||
|
|
5c00504261 | ||
|
|
d7231ebbd8 | ||
|
|
eb3169efa1 | ||
|
|
10b51644c9 | ||
|
|
7802d90186 | ||
|
|
978f5a14cf | ||
|
|
a501df25b5 | ||
|
|
079c48f6fe | ||
|
|
ee5391c126 | ||
|
|
e741246098 | ||
|
|
9827bea8d5 | ||
|
|
dcc184ed39 |
No files matched your search
@@ -1,15 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{yml,yaml,qml}]
|
||||
indent_size = 2
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
- name: Check
|
||||
run: |
|
||||
bash -n dist/steamify.sh
|
||||
python3 -c 'import ast, sys; [ast.parse(open(f).read(), f) for f in sys.argv[1:]]' ui/steamify-ui patches/*.py
|
||||
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).
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
# Lint: shell, Python and QML. Separate from bundle.yml (which builds and
|
||||
# releases) so a style problem shows up as its own check. Runs on GitHub and on
|
||||
# the Gitea mirror; the same checks locally: make check.
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, 'release/**']
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
shell:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# The sources, not only the bundle (bundle.yml checks that): .shellcheckrc
|
||||
# lists the exceptions (variables shared across modules).
|
||||
- name: shellcheck the modules
|
||||
uses: ludeeus/action-shellcheck@2.0.0
|
||||
with:
|
||||
severity: warning
|
||||
scandir: lib
|
||||
additional_files: steamify.sh ui/steamify-app.sh .github/tools/bundle.sh
|
||||
# steamify-os-release.sh is a template (@STEAMOS@ is filled in at install time).
|
||||
- name: shellcheck the helper scripts
|
||||
uses: ludeeus/action-shellcheck@2.0.0
|
||||
with:
|
||||
severity: warning
|
||||
scandir: patches
|
||||
ignore_names: steamify-os-release.sh
|
||||
|
||||
python:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: ruff (ruff.toml)
|
||||
uses: astral-sh/ruff-action@v4.1.0
|
||||
with:
|
||||
args: check ui/steamify-ui patches
|
||||
|
||||
qml:
|
||||
runs-on: ubuntu-latest
|
||||
# Not blocking until it has run once on the runner image (Qt packages).
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install qmllint and the QML modules the app imports
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq qt6-declarative-dev-tools qml6-module-qtquick qml6-module-qtquick-controls qml6-module-qtquick-window
|
||||
# "unqualified" (a screen reading `app` from its parent) is how the screens get their
|
||||
# AppState, so it is off; everything else must be clean.
|
||||
- name: qmllint
|
||||
run: |
|
||||
lint="$(command -v qmllint6 || echo /usr/lib/qt6/bin/qmllint)"
|
||||
"$lint" --unqualified disable --max-warnings 0 ui/qml/*.qml
|
||||
@@ -1,2 +0,0 @@
|
||||
# The modules share variables across files once bundled (see AGENTS.md).
|
||||
disable=SC2034,SC2154
|
||||
@@ -47,7 +47,7 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
|
||||
idempotent. `<id>_enable` is also used to re-apply. Optional
|
||||
`<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 are the `REQUIRES`, `FOLLOWS` and `EXCLUDES` tables in `lib/menu.sh`, used by `toggle_component` (the menu) and `normalize_wanted` (the app).
|
||||
`gaming` must stay first. Dependencies live in `toggle_component`.
|
||||
- **NVIDIA** (`lib/nvidia.sh`): gamescope's session is broken on NVIDIA, so the SteamOS conversion (`gaming`, `boot`, `single`,
|
||||
`glyphs`) is hidden there (unless already on) and `nvidia` + its sub-option `bigpicture` ("Gaming on NVIDIA") replace it:
|
||||
Steam on the Plasma desktop, started at login. Never reintroduce a gamescope session for NVIDIA without re-testing on the
|
||||
@@ -90,16 +90,6 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
|
||||
|
||||
- Bash, `set -uo pipefail` (no `-e`): check exit codes of steps that matter
|
||||
explicitly (`|| { err ...; exit 1; }` or `|| return 1`).
|
||||
- **Failure and quiet steps.** A function returns 0 when what it was asked
|
||||
to do is in place and 1 when it isn't (after `err`); a step that is only an
|
||||
extra (an icon cache, a service that may not exist) warns or stays silent and
|
||||
doesn't change the return value. Never `cmd 2>/dev/null` on a step that
|
||||
changes the system: use `best_effort cmd...` (same exit status, its errors go
|
||||
to `$STATE_DIR/log/steamify.log`). `2>/dev/null` stays for probes (`pacman -Q`,
|
||||
`grep -q`, `is-active`) whose failure is the answer.
|
||||
- **Temp dirs.** `make_tmpdir <variable>` (not `$(mktemp -d)`, whose
|
||||
subshell can't register it): the entry point's `on_exit` removes them, also
|
||||
when a run stops half way. That handler is the only `EXIT` trap.
|
||||
- Use the helpers from `lib/common.sh` (`info`, `ok`, `warn`, `err`,
|
||||
`ask_yn`, `backup_file`) for all output and prompts.
|
||||
- Runs as the normal user; use `sudo` per command, never require root.
|
||||
|
||||
+1
-25
@@ -5,33 +5,9 @@ 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.
|
||||
|
||||
## Unreleased
|
||||
|
||||
- **refactor: Valve's package mirror in one module (`lib/valve-mirror.sh`: newest repo, package database fields, checksummed download); the SteamOS extras, CEC driver, BIOS lookup and os-release version used four copies**
|
||||
- **refactor: the rules between menu items are three tables (`REQUIRES`, `FOLLOWS`, `EXCLUDES` in `lib/menu.sh`) used by the menu's toggles and, as `normalize_wanted`, by the app's selection (it had its own copy of the rules). The app now also ticks the Steamify shortcut for "Add as non-Steam game" and unticks the game entry with it, like the menu**
|
||||
- **refactor: the entry point picks its mode once (`MODE`) and `--defaults` and `--boot` are functions with one failure report, instead of four booleans and two copies of the apply-and-report code; `NO_COLOR` gives plain output**
|
||||
- **refactor: `is_wanted` and `is_current` instead of repeated `[[ "${WANTED[x]}" == 1 ]]` checks in the menu, the app's backend and the entry point**
|
||||
- **refactor: the start of every item's JSON (id, label, hint, kind, parent) is built in one place (`json_item_head`) for the app and the installer page, and `json_bool` replaces the `&& echo true || echo false` copies**
|
||||
- **ci: a Lint workflow runs shellcheck on the modules and helper scripts, ruff on the app and `patches/*.py` (`ruff.toml`) and qmllint on the QML (not blocking until it has run once on the runner); `make lint` runs the same locally**
|
||||
- **refactor: `best_effort` for the steps whose failure doesn't matter (stopping or disabling units, removing packages, `modprobe -r`, ...): same behaviour, but their errors go to `~/.local/state/steamify/log/steamify.log` instead of `/dev/null`; temp dirs are registered with `make_tmpdir` and removed by the one exit handler (also when a run is interrupted), which replaces the menu's own `EXIT` trap**
|
||||
- **refactor: the retired kernel pin moved out of `lib/steam-machine.sh` into `lib/kernel-pin.sh`**
|
||||
- **refactor: the shortcut's two start scripts are files in `patches/` (`steamify-start.sh`, `steamify-app-start.sh`) instead of heredocs in `launcher_enable`, and the 83-line LED driver install is split into steps (`led_write_dkms_override`, `led_install_package`, `led_build_all_kernels`, `led_load_module`); what gets installed is byte-identical**
|
||||
- **fix: the daily update check no longer crashes on a release tag that isn't a plain version number (`TypeError` comparing it, found by the new unit tests); it is simply never offered**
|
||||
- **refactor: `theme_enable` and `create_desktop_shortcut` split into steps; the Return to Gaming Mode shortcut is written with its mode already set (`install_executable`), like the other shortcuts, instead of chmod afterwards**
|
||||
- **test: the menu rules, the plan and the app's and installer's JSON are checked against a golden file (`tests/menu-test.sh` in steamify-cachyos-dev); `make check`, `.editorconfig`, `.shellcheckrc`**
|
||||
- **docs: README lists the sources and projects Steamify builds on, with licences and thanks**
|
||||
|
||||
## 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**
|
||||
- `5da1833` **feat: "Start Steam silently" sub-option: under the SteamOS conversion (`silent`, on by default, as before) and under Gaming on NVIDIA (`nvsilent`, opt-in, not together with Big Picture); on a Steam Machine only when it boots into the desktop**
|
||||
|
||||
## 2.10.0 - 2026-10-01
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# Local checks, the same as CI's: make check
|
||||
SH := steamify.sh $(wildcard lib/*.sh) ui/steamify-app.sh .github/tools/bundle.sh
|
||||
# The unit tests live in ../steamify-cachyos-dev/tests (REPO points at this checkout).
|
||||
DEV ?= ../steamify-cachyos-dev
|
||||
# Not the hardware test: it is for a real NVIDIA PC and writes a report in $$HOME.
|
||||
TESTS := $(filter-out %-hardware-test.sh,$(wildcard $(DEV)/tests/*-test.sh))
|
||||
|
||||
.PHONY: check syntax lint lint-sh lint-py lint-qml test bundle fmt-check
|
||||
check: syntax lint test bundle
|
||||
|
||||
syntax:
|
||||
@for f in $(SH); do bash -n "$$f" || exit 1; done
|
||||
python3 -c 'import ast, sys; [ast.parse(open(f).read(), f) for f in sys.argv[1:]]' ui/steamify-ui patches/*.py
|
||||
|
||||
lint: lint-sh lint-py lint-qml
|
||||
|
||||
lint-sh:
|
||||
@command -v shellcheck >/dev/null && shellcheck -S warning $(SH) || echo "shellcheck not installed: skipped"
|
||||
|
||||
lint-py:
|
||||
@command -v ruff >/dev/null && ruff check ui/steamify-ui patches || echo "ruff not installed: skipped"
|
||||
|
||||
lint-qml:
|
||||
@Q="$$(command -v qmllint6 || command -v qmllint || ls /usr/lib/qt6/bin/qmllint 2>/dev/null)"; \
|
||||
[ -n "$$Q" ] && "$$Q" --unqualified disable --max-warnings 0 ui/qml/*.qml || echo "qmllint not installed: skipped"
|
||||
|
||||
test:
|
||||
@for t in $(TESTS); do echo "== $$t"; REPO="$(CURDIR)" bash "$$t" || exit 1; done
|
||||
|
||||
bundle:
|
||||
.github/tools/bundle.sh dist/steamify.sh
|
||||
|
||||
fmt-check:
|
||||
@command -v shfmt >/dev/null && shfmt -d -i 4 -ci $(SH) || echo "shfmt not installed: skipped"
|
||||
@@ -27,10 +27,6 @@ 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
|
||||
@@ -44,7 +40,7 @@ Steam Machine.
|
||||
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 (an option of the SteamOS conversion).
|
||||
3. **Steam Deck/Machine icons** - Steam Deck button icons in gaming mode.
|
||||
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
|
||||
@@ -187,51 +183,6 @@ Contributing (or an AI agent)? Read [AGENTS.md](AGENTS.md).
|
||||
|
||||
MIT - do whatever you want with it. See [LICENSE.md](LICENSE.md).
|
||||
|
||||
## Sources and credits
|
||||
|
||||
Steamify is glue around other people's work. It installs, configures or
|
||||
downloads the following, and doesn't claim any of it:
|
||||
|
||||
| Project | What Steamify uses it for |
|
||||
|---|---|
|
||||
| [CachyOS](https://cachyos.org) | The distribution it targets, and its packages: `cachyos-vapor` (the Vapor theme), the gamescope session packages (`gamescope-session-cachyos` and the autologin helpers), `dmemcg-booster` and `plasma-foreground-booster` (VRAM booster), and the `xone` / `xpadneo` DKMS packages (extended controller support) |
|
||||
| [Valve / SteamOS](https://github.com/ValveSoftware/SteamOS) | The behaviour it imitates: SteamOS's session switching (`steam-set-session`, `steamos-manager`) and the Steam Deck UI. Downloaded from Valve's package mirror at install time, never shipped here: `steamdeck-kde-presets` (the SteamOS extras, installed under `/usr/local`), the Steam Machine BIOS package and its checksums, and the SteamOS release number shown in Steam's System settings. The gear icon (`assets/steam-gaming-settings.svg`) is Valve's *Return to Gaming Mode* icon (GPL-2.0) with a gear |
|
||||
| [Steam](https://store.steampowered.com), [gamescope](https://github.com/ValveSoftware/gamescope) and [MangoHud](https://github.com/flightlessmango/MangoHud) | The client, the compositor the gaming session is built on, and the overlay; installed through CachyOS's packages |
|
||||
| [evlaV/linux-integration](https://github.com/evlaV/linux-integration) | An unofficial mirror of Valve's kernel: its `cros_ec_cec` driver (GPL-2.0) for the Steam Machine's HDMI-CEC, downloaded at a pinned commit and checksum, then patched by `patches/cros-ec-cec-single-port.patch` |
|
||||
| `leds-valve-dkms-git` (AUR) | The Steam Machine's front LED bar driver |
|
||||
| [KDE Plasma](https://kde.org/plasma-desktop/), SDDM, plasma-login-manager | The desktop and the login managers whose settings it edits (`kwriteconfig6`, `lookandfeeltool`) |
|
||||
| [DKMS](https://github.com/dell/dkms), [fwupd](https://fwupd.org), [yay](https://github.com/Jguer/yay) / [paru](https://github.com/Morganamilo/paru) | Building kernel modules, flashing the BIOS, and installing AUR packages (yay is built from the AUR's `yay-bin` when neither is installed) |
|
||||
| [PySide6 / Qt](https://doc.qt.io/qtforpython-6/) | The graphical app |
|
||||
|
||||
Licences: Steamify itself is MIT. `patches/steamify-fremont-poweroff.c` is a
|
||||
kernel module and therefore GPL-2.0 (see its SPDX header), and the CEC
|
||||
driver it patches is GPL-2.0 too. Each of the projects above keeps its own
|
||||
licence.
|
||||
|
||||
The problems other people found first are credited where they were used: the
|
||||
[SteamOS](https://github.com/ValveSoftware/SteamOS/issues/2626) and
|
||||
[CachyOS gamescope-session](https://github.com/CachyOS/gamescope-session/issues/9)
|
||||
issues linked in [TECHNICAL.md](TECHNICAL.md).
|
||||
|
||||
### Mentions
|
||||
|
||||
Thanks to:
|
||||
|
||||
- The **CachyOS** team and packagers, for a distribution that makes this
|
||||
possible and for the gamescope session packages Steamify builds on.
|
||||
- **Valve**, for SteamOS, its public package repositories, Steam and
|
||||
gamescope.
|
||||
- **evlaV** for the public `linux-integration` mirror of Valve's kernel, the
|
||||
source of the Steam Machine's CEC driver.
|
||||
- The maintainers of **`leds-valve-dkms-git`**, **xone**, **xpadneo**,
|
||||
**dmemcg-booster** and **plasma-foreground-booster**.
|
||||
- The **KDE**, **Arch Linux** and **AUR** communities, and the authors of
|
||||
**yay** and **paru**.
|
||||
- Everyone who reported problems or tested on real hardware.
|
||||
|
||||
If something here is missing or wrongly credited, open an issue or a pull
|
||||
request and it will be fixed.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
Steamify CachyOS is an unofficial community project. It is not affiliated
|
||||
|
||||
+9
-14
@@ -109,10 +109,8 @@ tools write to the conf.d fragment into the base config.
|
||||
installs `/usr/local/bin/sync-steamos-session.sh` plus
|
||||
`sync-steamos-session.path`/`.service`;
|
||||
- `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;
|
||||
("Start Steam silently", on by default) adds a `steam-desktop-autostart`
|
||||
systemd user service that starts Steam silently in Plasma only;
|
||||
- the **Return to Gaming Mode** shortcut; on plasma-login-manager with a
|
||||
narrow sudoers rule (`/etc/sudoers.d/gamescope-session-switch`) so it can
|
||||
restart the login manager without a password prompt.
|
||||
@@ -126,8 +124,7 @@ The conversion also sets what Steam's System settings show, see
|
||||
|
||||
**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. 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.
|
||||
file), which makes Steam show Steam Deck button glyphs in gaming mode.
|
||||
|
||||
## SteamOS desktop look
|
||||
|
||||
@@ -227,7 +224,7 @@ DKMS modules, built for every installed kernel, so `install_kernel_headers` (`li
|
||||
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`).
|
||||
plugged in again, or the controller reconnected. `tests/controllers-test.sh` tests it with a stub `pacman`.
|
||||
|
||||
## Gaming on NVIDIA
|
||||
|
||||
@@ -259,7 +256,7 @@ and boot entries are rebuilt and checked for the parameters. The early-load drop
|
||||
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).
|
||||
all of it. `tests/nvidia-hardware-test.sh check|apply|visual` is for a real NVIDIA PC (writes `~/steamify-nvidia-report.txt`). `tests/nvidia-test.sh` tests it against a fake GPU, a stub `pacman` and `systemctl`, and a temp home.
|
||||
|
||||
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);
|
||||
@@ -347,7 +344,8 @@ 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 (`steam -silent`). That's refused in gaming
|
||||
closed (`steam -shutdown`) for the edit and started again (through
|
||||
`steam-desktop-autostart.service` when it's there). 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
|
||||
@@ -492,8 +490,7 @@ 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/autostart/steam.desktop` | the `silent` option (Steam's "Run Steam when my computer starts") |
|
||||
| `~/.config/systemd/user/` | `steamify-update-check.*` |
|
||||
| `~/.config/systemd/user/` | `steamify-update-check.*`, `steam-desktop-autostart.service` |
|
||||
|
||||
System-wide: `/usr/local/lib/steamify/` (kernel headers script),
|
||||
`/usr/src/steamify-*` (DKMS modules), `/var/cache/steamify/kernel` (the old
|
||||
@@ -539,9 +536,7 @@ steamify.sh --defaults --list # what --defaults can se
|
||||
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`).
|
||||
Plasma session waits for the first login (`lib/first-login.sh`).
|
||||
|
||||
With `Relogin=true`, a gamescope session that fails to start is restarted
|
||||
immediately, which can turn into a loop - see
|
||||
|
||||
@@ -2,13 +2,6 @@
|
||||
|
||||
See [TECHNICAL.md](TECHNICAL.md) for how each part works.
|
||||
|
||||
## Where are the errors of the quiet steps?
|
||||
|
||||
Steps that may fail without it mattering (stopping a service that isn't
|
||||
running, removing a package that isn't installed) don't show their errors in the
|
||||
wizard. They are kept in `~/.local/state/steamify/log/steamify.log` (the last
|
||||
512 KB), with the command and its exit status: attach it when you ask for help.
|
||||
|
||||
## Gaming mode shows a black screen or keeps restarting
|
||||
|
||||
Press **Ctrl+Alt+F3**, log in with your username and password, and run:
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 225 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 322 KiB After Width: | Height: | Size: 465 KiB |
+35
-27
@@ -31,41 +31,31 @@ json_list() {
|
||||
printf '[%s]' "$out"
|
||||
}
|
||||
|
||||
json_bool() {
|
||||
# json_bool <command>...: true or false by the command's exit status.
|
||||
if "$@"; then printf true; else printf false; fi
|
||||
}
|
||||
|
||||
json_item_head() {
|
||||
# json_item_head <id> <kind>: the start of an item's object (no closing
|
||||
# brace) with what the app and the installer page both read: id, label,
|
||||
# hint (the part of the label after the colon), kind, parent.
|
||||
local c="$1" hint=""
|
||||
[[ "${LABEL[$c]}" == *:* ]] && hint="${LABEL[$c]#*: }"
|
||||
printf '{"id":%s,"label":%s,"hint":%s,"kind":"%s","parent":%s,"needs":""' \
|
||||
"$(json_str "$c")" "$(json_str "${LABEL[$c]%%:*}")" "$(json_str "$hint")" "$2" "$(json_str "${PARENT[$c]:-}")"
|
||||
}
|
||||
|
||||
backend_event() {
|
||||
# backend_event <event> [<json fields without braces>]
|
||||
printf '{"event":%s%s}\n' "$(json_str "$1")" "${2:+,$2}"
|
||||
}
|
||||
|
||||
backend_status() {
|
||||
local c first_run=true items="" kind
|
||||
local c first_run=true items="" kind parent now
|
||||
detect_components
|
||||
for c in "${COMPONENTS[@]}"; do
|
||||
component_available "$c" && ! is_action "$c" && is_current "$c" && first_run=false
|
||||
component_available "$c" && ! is_action "$c" && [[ "${CURRENT[$c]}" == 1 ]] && first_run=false
|
||||
done
|
||||
for c in "${COMPONENTS[@]}"; do
|
||||
component_available "$c" || continue
|
||||
kind=toggle; is_action "$c" && kind=action; [[ "$c" == boot ]] && kind=choice
|
||||
items+="${items:+,}$(json_item_head "$c" "$kind")"
|
||||
items+=",\"on\":$(json_bool is_current "$c")"
|
||||
items+=",\"wanted\":$(json_bool is_wanted "$c")"
|
||||
items+=",\"update\":$(json_bool feature_outdated "$c")"
|
||||
items+=",\"new\":$(json_bool feature_any_new "$c")"
|
||||
items+=",\"selectable\":$(json_bool component_selectable "$c")"
|
||||
parent="${PARENT[$c]:-}"
|
||||
now="${CURRENT[$c]:-0}"
|
||||
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\":$(json_str "$([[ "$c" == silent ]] && silent_needs_boot && echo boot)")"
|
||||
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" || 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:-}" ]] &&
|
||||
items+=",\"note\":$(json_str "$(vram_nvidia_note "$VRAM_NVIDIA_CASE")")"
|
||||
@@ -75,11 +65,11 @@ backend_status() {
|
||||
for f in /dev/cec*; do [[ -e "$f" ]] && cec+="${cec:+ }$(basename "$f")"; done
|
||||
local bios='null'
|
||||
if bios_available; then
|
||||
bios="{\"current\":$(json_str "$(bios_current)"),\"newest\":$(json_str "${BIOS_NEWEST:-}"),\"selectable\":$(json_bool bios_selectable),\"dryRun\":$([[ -n "$BIOS_DRY_RUN" ]] && echo true || echo false)}"
|
||||
bios="{\"current\":$(json_str "$(bios_current)"),\"newest\":$(json_str "${BIOS_NEWEST:-}"),\"selectable\":$(bios_selectable && echo true || echo false),\"dryRun\":$([[ -n "$BIOS_DRY_RUN" ]] && echo true || echo false)}"
|
||||
fi
|
||||
printf '{"version":%s,"firstRun":%s,"steamMachine":%s,"kernel":%s,"pinnedKernel":%s,"cecDevices":%s,"leds":%s,"bios":%s,"items":[%s]}\n' \
|
||||
"$(json_str "$VERSION")" "$first_run" \
|
||||
"$(json_bool detect_valve_fremont)" \
|
||||
"$(detect_valve_fremont && echo true || echo false)" \
|
||||
"$(json_str "$(uname -r)")" "$(json_str "${PINNED_KERNEL_VER:-}")" \
|
||||
"$(json_str "$cec")" \
|
||||
"$(compgen -G '/sys/class/leds/valve-leds*' | wc -l)" \
|
||||
@@ -156,10 +146,28 @@ backend_apply() {
|
||||
WANTED[$c]=0
|
||||
for id in "${want[@]}"; do [[ "$id" == "$c" ]] && WANTED[$c]=1; done
|
||||
done
|
||||
normalize_wanted
|
||||
# The same rules as the menu's toggles.
|
||||
# (Single user mode only needs the conversion where it is offered: not on NVIDIA PCs.)
|
||||
[[ "${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 ]] && component_available gaming && WANTED[gaming]=1
|
||||
[[ "${WANTED[gaming]:-0}" == 0 ]] && WANTED[silent]=0
|
||||
[[ "${WANTED[poweroff]:-0}" == 1 ]] && WANTED[machine]=1
|
||||
[[ "${WANTED[machine]:-0}" == 0 ]] && WANTED[poweroff]=0
|
||||
[[ "${WANTED[kpin]:-0}" == 1 ]] && WANTED[machine]=1
|
||||
[[ "${WANTED[machine]:-0}" == 0 ]] && WANTED[kpin]=0
|
||||
# Retired: only ever removed.
|
||||
WANTED[hdmi]=0
|
||||
case "$boot" in desktop) WANTED[boot]=1; WANTED[gaming]=1 ;; gamescope) WANTED[boot]=0 ;; esac
|
||||
silent_normalize
|
||||
|
||||
REAPPLY=$reapply
|
||||
plan_changes
|
||||
@@ -173,7 +181,7 @@ backend_apply() {
|
||||
backend_sudo || return 1
|
||||
|
||||
LOGIN_MANAGER=plasmalogin
|
||||
is_wanted single && LOGIN_MANAGER=sddm
|
||||
[[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm
|
||||
local -a failed=()
|
||||
for c in "${TO_DISABLE[@]}"; do
|
||||
if backend_run_component "$c" disable; then feature_record "$c" disable; else failed+=("$c"); fi
|
||||
|
||||
+7
-7
@@ -30,16 +30,16 @@ bios_lookup_newest() {
|
||||
[[ -n "${BIOS_LOOKED_UP:-}" ]] && return 0
|
||||
BIOS_LOOKED_UP=1; BIOS_NEWEST=""
|
||||
local tmp desc
|
||||
BIOS_REPO="$(valve_newest_repo "$BIOS_REPO_PREFIX" 10)"
|
||||
BIOS_REPO="$(curl -fsL --max-time 10 "$VALVE_MIRROR/" | grep -oE "$BIOS_REPO_PREFIX-[0-9]+\.[0-9]+/" | tr -d / | sort -uV | tail -n 1)"
|
||||
[[ -n "$BIOS_REPO" ]] || return 1
|
||||
make_tmpdir tmp
|
||||
if curl -fsL --max-time 30 "$(valve_repo_url "$BIOS_REPO")/$BIOS_REPO.files" -o "$tmp/files" &&
|
||||
curl -fsL --max-time 30 "$(valve_repo_url "$BIOS_REPO")/$BIOS_REPO.db" -o "$tmp/db"; then
|
||||
tmp="$(mktemp -d)"
|
||||
if curl -fsL --max-time 30 "$VALVE_MIRROR/$BIOS_REPO/os/x86_64/$BIOS_REPO.files" -o "$tmp/files" &&
|
||||
curl -fsL --max-time 30 "$VALVE_MIRROR/$BIOS_REPO/os/x86_64/$BIOS_REPO.db" -o "$tmp/db"; then
|
||||
desc="$(tar -tf "$tmp/files" 2>/dev/null | grep -E '^fremont-hw-support-[0-9][^/]*/files$' | head -n 1)"
|
||||
[[ -n "$desc" ]] && BIOS_CAB="$(tar -xOf "$tmp/files" "$desc" | grep -E '^usr/share/fwupd/.*\.cab$' | head -n 1)"
|
||||
desc="${desc%/files}/desc"
|
||||
BIOS_PKG="$(valve_desc_field "$tmp/db" "$desc" FILENAME)"
|
||||
BIOS_SHA256="$(valve_desc_field "$tmp/db" "$desc" SHA256SUM)"
|
||||
BIOS_PKG="$(tar -xOf "$tmp/db" "$desc" 2>/dev/null | awk '/^%FILENAME%$/ { getline; print }')"
|
||||
BIOS_SHA256="$(tar -xOf "$tmp/db" "$desc" 2>/dev/null | awk '/^%SHA256SUM%$/ { getline; print }')"
|
||||
[[ -n "${BIOS_CAB:-}" && -n "$BIOS_PKG" && -n "$BIOS_SHA256" ]] && BIOS_NEWEST="$(basename "$BIOS_CAB" .cab)"
|
||||
fi
|
||||
rm -rf "$tmp"
|
||||
@@ -143,7 +143,7 @@ bios_prepare() {
|
||||
{ err "Installing fwupd failed."; return 1; }
|
||||
rm -rf "$BIOS_STAGE_DIR"; mkdir -p "$BIOS_STAGE_DIR"; chmod 700 "$BIOS_STAGE_DIR"
|
||||
info "Downloading $BIOS_PKG ($BIOS_REPO)..."
|
||||
if ! curl -fsSL "$(valve_repo_url "$BIOS_REPO")/$BIOS_PKG" -o "$BIOS_STAGE_DIR/pkg.tar.zst" ||
|
||||
if ! curl -fsSL "$VALVE_MIRROR/$BIOS_REPO/os/x86_64/$BIOS_PKG" -o "$BIOS_STAGE_DIR/pkg.tar.zst" ||
|
||||
! echo "$BIOS_SHA256 $BIOS_STAGE_DIR/pkg.tar.zst" | sha256sum -c --quiet - ||
|
||||
! tar -I unzstd -xf "$BIOS_STAGE_DIR/pkg.tar.zst" -C "$BIOS_STAGE_DIR" "$BIOS_CAB"; then
|
||||
err "Downloading or verifying $BIOS_PKG failed; the BIOS was not touched."
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ boot_enable() {
|
||||
|
||||
boot_disable() {
|
||||
info "Setting the PC to boot into gaming mode again..."
|
||||
best_effort sudo systemctl disable "$BOOT_UNIT_NAME"
|
||||
sudo systemctl disable "$BOOT_UNIT_NAME" 2>/dev/null
|
||||
sudo rm -f "$BOOT_UNIT"
|
||||
sudo systemctl daemon-reload
|
||||
# Next boot into gamescope, as the conversion sets it up.
|
||||
|
||||
+36
-20
@@ -39,10 +39,10 @@ cec_link_steamos_manager() {
|
||||
# this item ran.
|
||||
pacman -Q steamos-manager >/dev/null 2>&1 || return 0
|
||||
user_systemctl daemon-reload
|
||||
best_effort user_systemctl enable steamos-manager-configure-cecd.service
|
||||
best_effort user_systemctl start steamos-manager-configure-cecd.service
|
||||
best_effort user_systemctl restart cecd.service
|
||||
best_effort user_systemctl restart steamos-manager.service
|
||||
user_systemctl enable steamos-manager-configure-cecd.service 2>/dev/null
|
||||
user_systemctl start steamos-manager-configure-cecd.service 2>/dev/null
|
||||
user_systemctl restart cecd.service 2>/dev/null
|
||||
user_systemctl restart steamos-manager.service 2>/dev/null
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -55,10 +55,10 @@ cec_status() { cec_installed && [[ -f "$CEC_STEAM_DROPIN" ]] && cec_driver_ok; }
|
||||
|
||||
cec_reload_driver() {
|
||||
# cecd keeps /dev/cec0 open, so the old module can't be unloaded under it.
|
||||
best_effort user_systemctl stop cecd.service
|
||||
best_effort sudo modprobe -r cros_ec_cec
|
||||
best_effort sudo modprobe cros_ec_cec
|
||||
best_effort user_systemctl start cecd.service
|
||||
user_systemctl stop cecd.service 2>/dev/null
|
||||
sudo modprobe -r cros_ec_cec 2>/dev/null
|
||||
sudo modprobe cros_ec_cec 2>/dev/null
|
||||
user_systemctl start cecd.service 2>/dev/null
|
||||
}
|
||||
|
||||
cec_driver_enable() {
|
||||
@@ -68,7 +68,7 @@ cec_driver_enable() {
|
||||
sudo pacman -S --needed --noconfirm dkms patch || { err "Installing dkms failed."; return 1; }
|
||||
fi
|
||||
install_kernel_headers || return 1
|
||||
make_tmpdir tmp
|
||||
tmp="$(mktemp -d)"
|
||||
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
|
||||
@@ -80,7 +80,7 @@ cec_driver_enable() {
|
||||
return 1
|
||||
fi
|
||||
# The name carries the checksum: a newer pin gets its own file, the old one goes.
|
||||
best_effort sudo find "${CEC_DRIVER_CACHE%/*}" -maxdepth 1 -name 'cros-ec-cec-*.c' ! -name "${CEC_DRIVER_CACHE##*/}" -delete
|
||||
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).
|
||||
@@ -113,8 +113,24 @@ cec_repair() { cec_installed && ! cec_status; }
|
||||
|
||||
fetch_holo_pkg() {
|
||||
# fetch_holo_pkg <dir> <package>: download the newest <package> from
|
||||
# Valve's newest holo repository into <dir> and print its path.
|
||||
valve_fetch_pkg holo "$2" "$1"
|
||||
# Valve's newest holo repository into <dir> and print its path. The
|
||||
# SHA-256 comes from Valve's package index and is verified.
|
||||
local dir="$1" name="$2" repo desc file_name sha256
|
||||
repo="$(curl -fsL "$VALVE_MIRROR/" | grep -oE 'holo-[0-9]+\.[0-9]+/' | tr -d / | sort -V | tail -n 1)"
|
||||
if [[ ! -f "$dir/holo.db" ]]; then
|
||||
[[ -n "$repo" ]] && curl -fsL "$VALVE_MIRROR/$repo/os/x86_64/$repo.db" -o "$dir/holo.db" ||
|
||||
{ err "Couldn't read Valve's SteamOS package index ($VALVE_MIRROR)."; return 1; }
|
||||
fi
|
||||
desc="$(tar -tf "$dir/holo.db" 2>/dev/null | grep -E "^$name-[0-9][^/]*/desc$" | head -n 1)"
|
||||
[[ -n "$desc" ]] || { err "No $name in Valve's $repo repository."; return 1; }
|
||||
file_name="$(tar -xOf "$dir/holo.db" "$desc" | awk '/^%FILENAME%$/ { getline; print }')"
|
||||
sha256="$(tar -xOf "$dir/holo.db" "$desc" | awk '/^%SHA256SUM%$/ { getline; print }')"
|
||||
info "Downloading $file_name ($repo)..." >&2
|
||||
curl -fsL "$VALVE_MIRROR/$repo/os/x86_64/$file_name" -o "$dir/$file_name" ||
|
||||
{ err "Downloading $file_name failed."; return 1; }
|
||||
echo "$sha256 $dir/$file_name" | sha256sum -c --quiet - >&2 ||
|
||||
{ err "Checksum mismatch for $file_name; not using it."; return 1; }
|
||||
echo "$dir/$file_name"
|
||||
}
|
||||
|
||||
cec_enable() {
|
||||
@@ -124,7 +140,7 @@ cec_enable() {
|
||||
sudo pacman -S --needed --noconfirm linuxconsole || { err "Installing linuxconsole failed."; return 1; }
|
||||
state_set cec installed_linuxconsole 1
|
||||
fi
|
||||
make_tmpdir tmp
|
||||
tmp="$(mktemp -d)"
|
||||
for p in "${CEC_PKGS[@]}"; do
|
||||
f="$(fetch_holo_pkg "$tmp" "$p")" || { rm -rf "$tmp"; return 1; }
|
||||
files+=("$f")
|
||||
@@ -152,10 +168,10 @@ cec_enable() {
|
||||
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.
|
||||
best_effort user_systemctl enable --now cec-audio-control.socket
|
||||
user_systemctl enable --now cec-audio-control.socket 2>/dev/null
|
||||
cec_link_steamos_manager
|
||||
if compgen -G "/dev/cec*" >/dev/null; then
|
||||
best_effort user_systemctl restart cecd.service
|
||||
user_systemctl restart cecd.service 2>/dev/null
|
||||
ok "HDMI-CEC on ($(cd /dev && echo cec*)); Steam shows its settings from the next gaming mode start."
|
||||
info "Turn on CEC on your TV too (e.g. Sony: BRAVIA Sync, Samsung: Anynet+, LG: SimpLink)."
|
||||
else
|
||||
@@ -166,15 +182,15 @@ cec_enable() {
|
||||
|
||||
cec_disable() {
|
||||
info "Removing HDMI-CEC..."
|
||||
best_effort user_systemctl disable --now cecd.service cec-audio-control.socket cec-audio-control.service
|
||||
best_effort user_systemctl disable steamos-manager-configure-cecd.service
|
||||
best_effort sudo pacman -Rns --noconfirm "${CEC_PKGS[@]}"
|
||||
user_systemctl disable --now cecd.service cec-audio-control.socket cec-audio-control.service 2>/dev/null
|
||||
user_systemctl disable steamos-manager-configure-cecd.service 2>/dev/null
|
||||
sudo pacman -Rns --noconfirm "${CEC_PKGS[@]}" 2>/dev/null
|
||||
cec_driver_disable
|
||||
sudo rm -f "$CEC_ORDER_DROPIN" "$CEC_STEAM_DROPIN" "$CEC_STEAM_ENV"
|
||||
best_effort sudo rmdir "$(dirname "$CEC_ORDER_DROPIN")" "$(dirname "$CEC_STEAM_DROPIN")" "$(dirname "$CEC_STEAM_ENV")"
|
||||
sudo rmdir "$(dirname "$CEC_ORDER_DROPIN")" "$(dirname "$CEC_STEAM_DROPIN")" "$(dirname "$CEC_STEAM_ENV")" 2>/dev/null
|
||||
user_systemctl daemon-reload
|
||||
if [[ -n "$(state_get cec installed_linuxconsole)" ]]; then
|
||||
best_effort sudo pacman -Rns --noconfirm linuxconsole
|
||||
sudo pacman -Rns --noconfirm linuxconsole 2>/dev/null
|
||||
state_clear cec
|
||||
fi
|
||||
sudo udevadm control --reload
|
||||
|
||||
+3
-41
@@ -2,12 +2,7 @@
|
||||
# Output helpers, prompts and small shared utilities.
|
||||
# Sourced by steamify.sh; not meant to be run on its own.
|
||||
|
||||
# NO_COLOR (https://no-color.org): plain output, e.g. for a log file.
|
||||
if [[ -n "${NO_COLOR:-}" ]]; then
|
||||
c_reset=""; c_bold=""; c_green=""; c_yellow=""; c_red=""; c_cyan=""; c_dim=""
|
||||
else
|
||||
c_reset="\033[0m"; c_bold="\033[1m"; c_green="\033[32m"; c_yellow="\033[33m"; c_red="\033[31m"; c_cyan="\033[36m"; c_dim="\033[2m"
|
||||
fi
|
||||
c_reset="\033[0m"; c_bold="\033[1m"; c_green="\033[32m"; c_yellow="\033[33m"; c_red="\033[31m"; c_cyan="\033[36m"; c_dim="\033[2m"
|
||||
|
||||
info() { echo -e "${c_cyan}[INFO]${c_reset} $*"; }
|
||||
ok() { echo -e "${c_green}[OK]${c_reset} $*"; }
|
||||
@@ -39,39 +34,6 @@ ask_yn() {
|
||||
[[ "$reply" =~ ^[Yy]$ ]]
|
||||
}
|
||||
|
||||
best_effort() {
|
||||
# best_effort <command>...: for a step whose failure is fine (stopping a
|
||||
# unit that isn't running, removing what isn't installed). Same exit status
|
||||
# and output as the command, but its errors go to the log
|
||||
# (~/.local/state/steamify/log/steamify.log) instead of vanishing, so a problem
|
||||
# can still be found afterwards.
|
||||
local log="${STEAMIFY_LOG:-${STATE_DIR:-$HOME/.local/state/steamify}/log/steamify.log}" e rc
|
||||
e="$(mktemp)" || { "$@" 2>/dev/null; return; }
|
||||
"$@" 2>"$e"; rc=$?
|
||||
if [[ -s "$e" ]]; then
|
||||
mkdir -p "${log%/*}" 2>/dev/null
|
||||
# Kept small: a run that fails the same way every time shouldn't fill the disk.
|
||||
[[ "$(stat -c %s "$log" 2>/dev/null || echo 0)" -gt 524288 ]] && : > "$log"
|
||||
{ echo "$(date -Is) exit $rc: $*"; sed 's/^/ /' "$e"; } >> "$log" 2>/dev/null
|
||||
fi
|
||||
rm -f "$e"
|
||||
return $rc
|
||||
}
|
||||
|
||||
# Temp dirs of this run, removed at exit (on_exit in steamify.sh), also when it
|
||||
# stops half way: make_tmpdir <variable>.
|
||||
TMP_DIRS=()
|
||||
make_tmpdir() {
|
||||
local d
|
||||
d="$(mktemp -d)" || return 1
|
||||
TMP_DIRS+=("$d")
|
||||
printf -v "$1" '%s' "$d"
|
||||
}
|
||||
|
||||
cleanup_tmpdirs() {
|
||||
[[ ${#TMP_DIRS[@]} -eq 0 ]] || rm -rf "${TMP_DIRS[@]}"
|
||||
}
|
||||
|
||||
require_root_helper() {
|
||||
# Re-exec any privileged step through sudo rather than requiring the
|
||||
# whole script to run as root, so $HOME/whoami stay correct for the
|
||||
@@ -135,7 +97,7 @@ stop_plasmashell_for_edit() {
|
||||
PLASMASHELL_WAS_RUNNING=false
|
||||
if pgrep -u "$USER" -x plasmashell >/dev/null; then
|
||||
PLASMASHELL_WAS_RUNNING=true
|
||||
best_effort systemctl --user stop plasma-plasmashell.service
|
||||
systemctl --user stop plasma-plasmashell.service 2>/dev/null
|
||||
pkill -u "$USER" -x plasmashell && sleep 2
|
||||
fi
|
||||
}
|
||||
@@ -143,7 +105,7 @@ stop_plasmashell_for_edit() {
|
||||
restart_plasmashell_if_stopped() {
|
||||
if [[ "${PLASMASHELL_WAS_RUNNING:-false}" == true ]]; then
|
||||
rm -rf ~/.cache/plasmashell* ~/.cache/org.kde.dirmodel-qml.kcache
|
||||
best_effort systemctl --user start plasma-plasmashell.service ||
|
||||
systemctl --user start plasma-plasmashell.service 2>/dev/null ||
|
||||
{ setsid plasmashell >/dev/null 2>&1 & }
|
||||
fi
|
||||
}
|
||||
+29
-30
@@ -2,46 +2,41 @@
|
||||
# "Return to Gaming Mode" desktop shortcut and the sudoers rule it needs.
|
||||
# Sourced by steamify.sh; not meant to be run on its own.
|
||||
|
||||
shortcut_desktop_dir() {
|
||||
# The target user's Desktop folder, from their user-dirs.dirs.
|
||||
local user_home desktop_dir
|
||||
create_desktop_shortcut() {
|
||||
# Determine the home directory of the target user
|
||||
local user_home
|
||||
user_home="$(getent passwd "$TARGET_USER" | cut -d: -f6)"
|
||||
|
||||
# Locate the correct Desktop directory
|
||||
local desktop_dir
|
||||
if [[ -f "$user_home/.config/user-dirs.dirs" ]]; then
|
||||
desktop_dir=$(grep '^XDG_DESKTOP_DIR=' "$user_home/.config/user-dirs.dirs" | cut -d '"' -f 2)
|
||||
desktop_dir="${desktop_dir/\$HOME/$user_home}"
|
||||
fi
|
||||
echo "${desktop_dir:-$user_home/Desktop}"
|
||||
}
|
||||
desktop_dir="${desktop_dir:-$user_home/Desktop}"
|
||||
|
||||
shortcut_install_sudoers() {
|
||||
# plasma-login-manager needs the sync bridge run and a restart, which the
|
||||
# shortcut can't sudo for interactively (Terminal=false), so it gets a
|
||||
# password-less rule scoped to exactly those two commands.
|
||||
local sudoers_tmp
|
||||
sudoers_tmp="$(mktemp)"
|
||||
cat > "$sudoers_tmp" << EOF
|
||||
$TARGET_USER ALL=(root) NOPASSWD: /usr/bin/systemctl start sync-steamos-session.service, /usr/bin/systemctl restart plasmalogin
|
||||
EOF
|
||||
if sudo visudo -cf "$sudoers_tmp" >/dev/null; then
|
||||
sudo install -m 0440 -o root -g root "$sudoers_tmp" /etc/sudoers.d/gamescope-session-switch
|
||||
ok "Installed /etc/sudoers.d/gamescope-session-switch for the shortcut."
|
||||
else
|
||||
warn "Generated sudoers rule failed validation; the shortcut will not be able to restart plasmalogin."
|
||||
fi
|
||||
rm -f "$sudoers_tmp"
|
||||
}
|
||||
|
||||
create_desktop_shortcut() {
|
||||
local desktop_dir
|
||||
desktop_dir="$(shortcut_desktop_dir)"
|
||||
mkdir -p "$desktop_dir"
|
||||
|
||||
# On SDDM, steamos-session-select logging out is enough: Relogin=true
|
||||
# logs straight back in to the newly selected session, like SteamOS.
|
||||
# plasma-login-manager needs the sync bridge run and a restart, which
|
||||
# the shortcut can't sudo for interactively (Terminal=false), so it gets
|
||||
# a password-less rule scoped to exactly those two commands.
|
||||
local exec_line="steamos-session-select gamescope"
|
||||
if [[ "$LOGIN_MANAGER" == "plasmalogin" ]]; then
|
||||
exec_line="sh -c 'steamos-session-select gamescope && sudo -n /usr/bin/systemctl start sync-steamos-session.service && sudo -n /usr/bin/systemctl restart plasmalogin'"
|
||||
shortcut_install_sudoers
|
||||
local sudoers_tmp
|
||||
sudoers_tmp="$(mktemp)"
|
||||
cat > "$sudoers_tmp" << EOF
|
||||
$TARGET_USER ALL=(root) NOPASSWD: /usr/bin/systemctl start sync-steamos-session.service, /usr/bin/systemctl restart plasmalogin
|
||||
EOF
|
||||
if sudo visudo -cf "$sudoers_tmp" >/dev/null; then
|
||||
sudo install -m 0440 -o root -g root "$sudoers_tmp" /etc/sudoers.d/gamescope-session-switch
|
||||
ok "Installed /etc/sudoers.d/gamescope-session-switch for the shortcut."
|
||||
else
|
||||
warn "Generated sudoers rule failed validation; the shortcut will not be able to restart plasmalogin."
|
||||
fi
|
||||
rm -f "$sudoers_tmp"
|
||||
else
|
||||
sudo rm -f /etc/sudoers.d/gamescope-session-switch
|
||||
fi
|
||||
@@ -54,11 +49,11 @@ create_desktop_shortcut() {
|
||||
[[ -f /usr/local/share/icons/hicolor/scalable/actions/gaming-return.svg ]] &&
|
||||
shortcut_icon="gaming-return"
|
||||
|
||||
# 2. Generate the .desktop shortcut with instant session switcher strings
|
||||
local shortcut_path="$desktop_dir/Return to Gaming Mode.desktop"
|
||||
info "Creating 'Return to Gaming Mode' desktop shortcut at: $shortcut_path"
|
||||
# Written with its mode already set: Plasma opens a desktop icon it first
|
||||
# saw non-executable in an editor.
|
||||
install_executable "$shortcut_path" 755 << EOF
|
||||
|
||||
cat << EOF > "$shortcut_path"
|
||||
[Desktop Entry]
|
||||
Name=Return to Gaming Mode
|
||||
Comment=Switch session back to Gamescope
|
||||
@@ -68,6 +63,10 @@ Terminal=false
|
||||
Type=Application
|
||||
Categories=System;
|
||||
EOF
|
||||
|
||||
# Fix permissions for the target user and KDE Plasma desktop ecosystem
|
||||
chmod +x "$shortcut_path"
|
||||
|
||||
ok "Desktop shortcut created successfully."
|
||||
}
|
||||
|
||||
|
||||
+6
-17
@@ -7,25 +7,13 @@
|
||||
|
||||
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
|
||||
mkdir -p ~/.cache
|
||||
{ $run; } > ~/.cache/steamify-first-login.log 2>&1
|
||||
curl -fsSL --max-time 30 "$WIZARD_URL" | bash -s -- --first-login
|
||||
rm -f "$FIRST_LOGIN_ENTRY" "\$0"
|
||||
[[ -x "$WIZARD_APP_LAUNCHER" ]] && exec "$WIZARD_APP_LAUNCHER"
|
||||
EOF2
|
||||
@@ -43,9 +31,10 @@ first_login_run() {
|
||||
[[ -n "$(plasma_applets org.kde.plasma.kickoff)" ]] && break
|
||||
sleep 2
|
||||
done
|
||||
stop_plasmashell_for_edit
|
||||
taskbar_drop_discover
|
||||
single_status && single_launcher
|
||||
restart_plasmashell_if_stopped
|
||||
if single_status; then
|
||||
stop_plasmashell_for_edit
|
||||
single_launcher
|
||||
restart_plasmashell_if_stopped
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
@@ -25,7 +25,7 @@ poweroff_fix_enable() {
|
||||
sudo pacman -S --needed --noconfirm dkms || { err "Installing dkms failed."; return 1; }
|
||||
fi
|
||||
install_kernel_headers || return 1
|
||||
make_tmpdir tmp
|
||||
tmp="$(mktemp -d)"
|
||||
patch_file "$POWEROFF_DKMS_NAME.c" > "$tmp/$POWEROFF_DKMS_NAME.c"
|
||||
echo "obj-m += $POWEROFF_DKMS_NAME.o" >"$tmp/Makefile"
|
||||
patch_file "$POWEROFF_DKMS_NAME.dkms.conf" | fill NAME="$POWEROFF_DKMS_NAME" VERSION="$POWEROFF_DKMS_VER" >"$tmp/dkms.conf"
|
||||
@@ -43,13 +43,13 @@ poweroff_fix_enable() {
|
||||
warn "Building the power-off fix for $k failed; with that kernel it may start again after shutting down."
|
||||
done
|
||||
echo "$POWEROFF_DKMS_NAME" | sudo tee "$POWEROFF_MODULES_LOAD" >/dev/null
|
||||
best_effort sudo modprobe "$POWEROFF_DKMS_NAME"
|
||||
sudo modprobe "$POWEROFF_DKMS_NAME" 2>/dev/null
|
||||
return 0
|
||||
}
|
||||
|
||||
poweroff_fix_disable() {
|
||||
sudo rm -f "$POWEROFF_MODULES_LOAD"
|
||||
best_effort sudo modprobe -r "$POWEROFF_DKMS_NAME"
|
||||
sudo modprobe -r "$POWEROFF_DKMS_NAME" 2>/dev/null
|
||||
[[ -d "$POWEROFF_DKMS_SRC" ]] || return 0
|
||||
sudo dkms remove "$POWEROFF_DKMS_NAME/$POWEROFF_DKMS_VER" --all >/dev/null 2>&1
|
||||
sudo rm -rf "$POWEROFF_DKMS_SRC"
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
#!/bin/bash
|
||||
# "Pin the kernel" (kpin), retired: newer linux-cachyos releases rebooted the
|
||||
# Steam Machine instead of shutting down, which the power-off fix
|
||||
# (lib/fremont-poweroff.sh) made unnecessary. Only what removes an existing pin
|
||||
# is still offered (kpin_available), a sub-option of Steam Machine support.
|
||||
# Sourced by steamify.sh; not meant to be run on its own.
|
||||
|
||||
# Kernel pinned on a Steam Machine (optional): newer linux-cachyos releases
|
||||
# rebooted it instead of shutting down, which the power-off fix now handles. The packages are kept in
|
||||
# PINNED_KERNEL_DIR, so re-applying (or reinstalling after an update slipped
|
||||
# through) needs no download.
|
||||
PINNED_KERNEL_VER="7.1.6-1"
|
||||
PINNED_KERNEL_KVER="7.1.6-1-cachyos"
|
||||
PINNED_KERNEL_PKGS=(linux-cachyos linux-cachyos-headers)
|
||||
PINNED_KERNEL_DIR="${PINNED_KERNEL_DIR:-/var/cache/steamify/kernel}"
|
||||
# SHA-256 of each package, so a file from any source is the one reviewed
|
||||
# here; its CachyOS signature is checked as well.
|
||||
declare -A PINNED_KERNEL_SHA256=(
|
||||
[linux-cachyos]=417fcd07102192b86e78e92ed7171d5a49378e9f57faea3fa2c7c541e9f68d08
|
||||
[linux-cachyos-headers]=d069866a11d9092746e1d5133cefd8924f027da2c9bf5af38d30b3aed6ded9bf
|
||||
)
|
||||
# Tried in order, after the kernel dir and pacman's cache. Our own release
|
||||
# always has these files; the archive keeps every release; the mirror only
|
||||
# the current one. PINNED_KERNEL_URL puts another source (a directory
|
||||
# holding the files) in front.
|
||||
PINNED_KERNEL_SOURCES=(
|
||||
${PINNED_KERNEL_URL:+"$PINNED_KERNEL_URL"}
|
||||
"https://github.com/theupriser/steamify-cachyos/releases/download/kernel-$PINNED_KERNEL_VER"
|
||||
"https://archive.cachyos.org/archive/cachyos"
|
||||
"https://mirror.cachyos.org/repo/x86_64/cachyos"
|
||||
)
|
||||
|
||||
pinned_kernel_installed() {
|
||||
local p
|
||||
for p in "${PINNED_KERNEL_PKGS[@]}"; do
|
||||
[[ "$(pacman -Q "$p" 2>/dev/null)" == "$p $PINNED_KERNEL_VER" ]] || return 1
|
||||
done
|
||||
}
|
||||
|
||||
fetch_pinned_kernel_file() {
|
||||
# $1 = file name. Local kernel dir first, then pacman's cache, then the
|
||||
# download sources. Downloads go to a .part file so a broken one is never
|
||||
# mistaken for a finished one.
|
||||
local f="$1" dest="$PINNED_KERNEL_DIR/$1" src
|
||||
[[ -s "$dest" ]] && return 0
|
||||
if [[ -s "/var/cache/pacman/pkg/$f" ]]; then
|
||||
sudo cp "/var/cache/pacman/pkg/$f" "$dest" && return 0
|
||||
fi
|
||||
for src in "${PINNED_KERNEL_SOURCES[@]}"; do
|
||||
if sudo curl -fL --retry 2 --connect-timeout 15 -o "$dest.part" "$src/$f" 2>/dev/null; then
|
||||
sudo mv "$dest.part" "$dest"
|
||||
return 0
|
||||
fi
|
||||
warn "Couldn't get $f from $src, trying the next source..."
|
||||
done
|
||||
sudo rm -f "$dest.part"
|
||||
return 1
|
||||
}
|
||||
|
||||
verify_pinned_kernel_pkg() {
|
||||
# $1 = package name, $2 = file. Both checks must pass: the SHA-256 from
|
||||
# this script, and the CachyOS signature (pacman on its own installs a
|
||||
# local file without a .sig: LocalFileSigLevel = Optional).
|
||||
local sum
|
||||
sum="$(sha256sum "$2" | cut -d' ' -f1)"
|
||||
if [[ "$sum" != "${PINNED_KERNEL_SHA256[$1]}" ]]; then
|
||||
err "$(basename "$2") doesn't match its expected checksum."
|
||||
return 1
|
||||
fi
|
||||
if ! sudo pacman-key --verify "$2.sig" "$2" >/dev/null 2>&1; then
|
||||
err "$(basename "$2") doesn't have a valid CachyOS signature."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
pin_kernel_in_pacman_conf() {
|
||||
# Adds our packages to IgnorePkg in [options], keeping what's there.
|
||||
local p
|
||||
for p in "${PINNED_KERNEL_PKGS[@]}"; do
|
||||
grep -Eq "^IgnorePkg\s*=.*(\s|=)$p(\s|$)" /etc/pacman.conf && continue
|
||||
if grep -Eq '^IgnorePkg\s*=' /etc/pacman.conf; then
|
||||
sudo sed -i -E "0,/^IgnorePkg\s*=/s/^(IgnorePkg\s*=.*)$/\1 $p/" /etc/pacman.conf
|
||||
else
|
||||
sudo sed -i -E "0,/^\[options\]/s//[options]\nIgnorePkg = $p/" /etc/pacman.conf
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
unpin_kernel_in_pacman_conf() {
|
||||
local p
|
||||
for p in "${PINNED_KERNEL_PKGS[@]}"; do
|
||||
sudo sed -i -E "/^IgnorePkg\s*=/s/\s$p(\s|$)/\1/" /etc/pacman.conf
|
||||
done
|
||||
# Drop the line if nothing is left on it.
|
||||
sudo sed -i -E '/^IgnorePkg\s*=\s*$/d' /etc/pacman.conf
|
||||
}
|
||||
|
||||
install_pinned_kernel() {
|
||||
sudo mkdir -p "$PINNED_KERNEL_DIR"
|
||||
pin_kernel_in_pacman_conf
|
||||
if pinned_kernel_installed; then
|
||||
ok "Kernel $PINNED_KERNEL_VER already installed and pinned."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local p f files=()
|
||||
info "Getting kernel $PINNED_KERNEL_VER (kept in $PINNED_KERNEL_DIR)..."
|
||||
for p in "${PINNED_KERNEL_PKGS[@]}"; do
|
||||
f="$p-$PINNED_KERNEL_VER-x86_64.pkg.tar.zst"
|
||||
fetch_pinned_kernel_file "$f" && fetch_pinned_kernel_file "$f.sig" ||
|
||||
{ err "Couldn't download $f from any source."; return 1; }
|
||||
if ! verify_pinned_kernel_pkg "$p" "$PINNED_KERNEL_DIR/$f"; then
|
||||
# Removed, so the next run fetches it again.
|
||||
sudo rm -f "$PINNED_KERNEL_DIR/$f" "$PINNED_KERNEL_DIR/$f.sig"
|
||||
err "Removed it; run the wizard again to download it once more."
|
||||
return 1
|
||||
fi
|
||||
files+=("$PINNED_KERNEL_DIR/$f")
|
||||
done
|
||||
|
||||
# pacman checks each package against the .sig next to it.
|
||||
info "Installing kernel $PINNED_KERNEL_VER..."
|
||||
if ! sudo pacman -U --noconfirm "${files[@]}"; then
|
||||
err "Installing kernel $PINNED_KERNEL_VER failed."
|
||||
return 1
|
||||
fi
|
||||
[[ "$(uname -r)" != "$PINNED_KERNEL_KVER" ]] && RESTART_FOR_LOGIN=true
|
||||
ok "Kernel $PINNED_KERNEL_VER installed and pinned (restart to use it)."
|
||||
}
|
||||
|
||||
remove_kernel_pin() {
|
||||
# Back to CachyOS's current kernel. The packages stay in
|
||||
# PINNED_KERNEL_DIR, so turning support on again doesn't download.
|
||||
unpin_kernel_in_pacman_conf
|
||||
pinned_kernel_installed || return 0
|
||||
info "Updating the kernel back to CachyOS's current version..."
|
||||
sudo pacman -Syu --noconfirm ||
|
||||
{ warn "Updating the kernel failed; run: sudo pacman -Syu"; return 0; }
|
||||
RESTART_FOR_LOGIN=true
|
||||
}
|
||||
|
||||
# "Pin the kernel" menu item, a sub-option of Steam Machine support. The
|
||||
# power-off fix made it unnecessary: only shown while on, to remove it.
|
||||
kpin_available() { detect_valve_fremont && kpin_status; }
|
||||
kpin_status() {
|
||||
pinned_kernel_installed && grep -Eq '^IgnorePkg\s*=.*\slinux-cachyos(\s|$)' /etc/pacman.conf
|
||||
}
|
||||
|
||||
kpin_enable() {
|
||||
install_pinned_kernel || return 1
|
||||
# The DKMS pacman hook built leds-valve for the new headers; make sure.
|
||||
if pacman -Qi leds-valve-dkms-git >/dev/null 2>&1 &&
|
||||
! dkms status -k "$PINNED_KERNEL_KVER" leds-valve-dkms 2>/dev/null | grep -q installed; then
|
||||
sudo dkms install leds-valve-dkms/0.1 -k "$PINNED_KERNEL_KVER" ||
|
||||
warn "Building the LED driver for $PINNED_KERNEL_KVER failed."
|
||||
fi
|
||||
}
|
||||
|
||||
kpin_disable() {
|
||||
# Saved HDMI refresh boost EDIDs were for the pinned kernel only (newer
|
||||
# ones read the whole EDID), also those of displays not connected now.
|
||||
hdmi_status && hdmi_forget_all
|
||||
remove_kernel_pin
|
||||
}
|
||||
@@ -35,7 +35,7 @@ steamos_release() {
|
||||
# capitals: steamos-3.9). Offline (e.g. in the installer): the one set
|
||||
# before, if any.
|
||||
local v
|
||||
v="$(valve_newest_repo jupiter 15)"
|
||||
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
|
||||
}
|
||||
@@ -210,7 +210,7 @@ remove_session_sync() {
|
||||
# The plasmalogin sync bridge from an earlier run isn't needed on SDDM.
|
||||
if [[ -f /etc/systemd/system/sync-steamos-session.path ]]; then
|
||||
info "Removing the plasma-login-manager sync bridge (not needed with SDDM)..."
|
||||
best_effort sudo systemctl disable --now sync-steamos-session.path
|
||||
sudo systemctl disable --now sync-steamos-session.path 2>/dev/null
|
||||
sudo rm -f /etc/systemd/system/sync-steamos-session.path \
|
||||
/etc/systemd/system/sync-steamos-session.service \
|
||||
/usr/local/bin/sync-steamos-session.sh
|
||||
|
||||
+65
-78
@@ -5,33 +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 silent glyphs nvidia bigpicture nvsilent theme single launcher steamgame notify extended_controller_support vram cec machine poweroff kpin hdmi bios)
|
||||
COMPONENTS=(gaming boot silent nvidia bigpicture nvsilent theme glyphs 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 [silent]=gaming [glyphs]=gaming [bigpicture]=nvidia [nvsilent]=nvidia [steamgame]=launcher [poweroff]=machine [kpin]=machine [hdmi]=machine [bios]=machine)
|
||||
# What ticking one item does to another, for the menu's toggles (toggle_component)
|
||||
# and the app's ticks (normalize_wanted). A sub-option needs its parent: ticking it
|
||||
# ticks the parent, unticking the parent unticks it (Where to boot is chosen with
|
||||
# --boot, so the app's ticks never bring the conversion for it; HDMI refresh boost is
|
||||
# retired and never brings Steam Machine support). Single user mode only makes sense
|
||||
# on top of the SteamOS conversion (where that is offered: on NVIDIA it logs in by
|
||||
# itself, see lib/single-user.sh).
|
||||
declare -A REQUIRES=([single]=gaming)
|
||||
for _c in "${!PARENT[@]}"; do [[ "$_c" == hdmi ]] || REQUIRES[$_c]=${PARENT[$_c]}; done
|
||||
unset _c
|
||||
# Opt-out sub-options are ticked and unticked along with their parent.
|
||||
declare -A FOLLOWS=([nvidia]=bigpicture [machine]=poweroff [launcher]=steamgame)
|
||||
# Ticking one unticks the other; in normalize_wanted the first in COMPONENTS wins.
|
||||
declare -A EXCLUDES=([bigpicture]=nvsilent [nvsilent]=bigpicture)
|
||||
declare -A PARENT=([boot]=gaming [silent]=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. Extended controller support builds kernel modules for
|
||||
# every installed kernel: opt-in.
|
||||
NO_PRESELECT=(boot silent nvsilent cec kpin hdmi extended_controller_support)
|
||||
NO_PRESELECT=(boot 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
|
||||
@@ -40,7 +26,7 @@ NO_PRESELECT=(boot silent nvsilent cec kpin hdmi extended_controller_support)
|
||||
# FEATURE_BASELINE.
|
||||
FEATURE_BASELINE=2.1.0
|
||||
declare -A FEATURE_VERSION=(
|
||||
[gaming]=2.9.0 [boot]=2.1.0 [silent]=2.11.0 [nvidia]=2.10.0 [bigpicture]=2.10.0 [nvsilent]=2.11.0 [theme]=2.1.0 [glyphs]=2.1.0 [single]=2.1.0
|
||||
[gaming]=2.9.0 [boot]=2.1.0 [silent]=2.1.0 [nvidia]=2.10.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
|
||||
)
|
||||
@@ -49,7 +35,7 @@ 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"
|
||||
[silent]="Start Steam silently: 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)"
|
||||
@@ -87,8 +73,6 @@ component_available() {
|
||||
esac
|
||||
}
|
||||
|
||||
is_wanted() { [[ "${WANTED[$1]:-0}" == 1 ]]; }
|
||||
is_current() { [[ "${CURRENT[$1]:-0}" == 1 ]]; }
|
||||
is_action() { [[ " ${ACTIONS[*]} " == *" $1 "* ]]; }
|
||||
|
||||
boot_mode() {
|
||||
@@ -97,12 +81,25 @@ boot_mode() {
|
||||
if [[ "$1" == 1 ]]; then echo desktop; else echo gamescope; fi
|
||||
}
|
||||
|
||||
silent_needs_boot() {
|
||||
# On a Steam Machine, silent start only makes sense when it boots into
|
||||
# the desktop (gaming mode starts its own Steam): the option is hidden,
|
||||
# and off, while "Boot into" is gaming mode.
|
||||
machine_available
|
||||
}
|
||||
|
||||
silent_normalize() {
|
||||
silent_needs_boot && [[ "${WANTED[boot]:-0}" != 1 ]] && WANTED[silent]=0
|
||||
return 0
|
||||
}
|
||||
|
||||
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
|
||||
[[ "$1" == silent ]] && silent_needs_boot && [[ "${WANTED[boot]:-0}" != 1 ]] && return 1
|
||||
[[ -z "${PARENT[$1]:-}" || "${WANTED[${PARENT[$1]}]:-0}" == 1 ]]
|
||||
}
|
||||
|
||||
@@ -131,7 +128,7 @@ 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
|
||||
is_current "$1" || return 1
|
||||
[[ "${CURRENT[$1]:-0}" == 1 ]] || return 1
|
||||
have="$(state_get features "$1" "$FEATURE_BASELINE")"
|
||||
[[ "$have" == off ]] && have="$FEATURE_BASELINE"
|
||||
want="${FEATURE_VERSION[$1]:-$FEATURE_BASELINE}"
|
||||
@@ -170,8 +167,6 @@ feature_new() {
|
||||
feature_added "$1" && [[ " ${NO_PRESELECT[*]} " != *" $1 "* ]]
|
||||
}
|
||||
|
||||
feature_any_new() { feature_new "$1" || feature_new_optin "$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.
|
||||
@@ -225,6 +220,7 @@ detect_components() {
|
||||
done
|
||||
machine_available && WANTED[cec]=1
|
||||
fi
|
||||
silent_normalize
|
||||
}
|
||||
|
||||
defaults_options() {
|
||||
@@ -270,17 +266,18 @@ defaults_options() {
|
||||
warn "Leaving out ${LABEL[$c]%%:*}: not available on this PC."
|
||||
fi
|
||||
done
|
||||
is_wanted single && component_available gaming && WANTED[gaming]=1
|
||||
[[ "${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)
|
||||
is_wanted gaming || { err "--boot desktop needs the SteamOS conversion (gaming)."; return 1; }
|
||||
[[ "${WANTED[gaming]}" == 1 ]] || { err "--boot desktop needs the SteamOS conversion (gaming)."; return 1; }
|
||||
WANTED[boot]=1 ;;
|
||||
gamescope) WANTED[boot]=0 ;;
|
||||
esac
|
||||
silent_normalize
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -302,53 +299,42 @@ defaults_list() {
|
||||
on=false
|
||||
[[ "$sel" == true && " ${NO_PRESELECT[*]} " != *" $c "* ]] && on=true
|
||||
[[ "$c" == cec ]] && machine_available && on=true
|
||||
items+="${items:+,}$(json_item_head "$c" "$kind"),\"hideWhen\":$(json_str "$([[ "$c" == nvsilent ]] && echo bigpicture)"),\"on\":$on,\"selectable\":$sel}"
|
||||
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\":$(json_str "$([[ "$c" == silent ]] && silent_needs_boot && echo boot)"),\"hideWhen\":$(json_str "$([[ "$c" == nvsilent ]] && echo bigpicture)"),\"on\":$on,\"selectable\":$sel}"
|
||||
done
|
||||
printf '[%s]\n' "$items"
|
||||
}
|
||||
|
||||
children_of() {
|
||||
# children_of <id>: the items that need <id> (REQUIRES), one per line.
|
||||
local c
|
||||
for c in "${COMPONENTS[@]}"; do [[ "${REQUIRES[$c]:-}" == "$1" ]] && echo "$c"; done
|
||||
return 0
|
||||
}
|
||||
|
||||
toggle_component() {
|
||||
local c="$1" p x
|
||||
local c="$1"
|
||||
component_selectable "$c" || return 1
|
||||
WANTED[$c]=$(( 1 - WANTED[$c] ))
|
||||
if is_wanted "$c"; then
|
||||
p="${REQUIRES[$c]:-}"
|
||||
[[ -n "$p" ]] && component_available "$p" && WANTED[$p]=1
|
||||
x="${EXCLUDES[$c]:-}"
|
||||
[[ -n "$x" ]] && WANTED[$x]=0
|
||||
else
|
||||
for x in $(children_of "$c"); do WANTED[$x]=0; done
|
||||
fi
|
||||
x="${FOLLOWS[$c]:-}"
|
||||
[[ -n "$x" ]] && component_available "$x" && WANTED[$x]=${WANTED[$c]}
|
||||
}
|
||||
|
||||
normalize_wanted() {
|
||||
# The same rules for a whole selection (the app ticks exactly the items it
|
||||
# sends): a ticked sub-option brings its parent, two that exclude each other
|
||||
# keep the first, and an unticked parent takes its sub-options with it
|
||||
# (only where the parent is offered: single user mode stays on NVIDIA PCs).
|
||||
local c p x
|
||||
for c in "${COMPONENTS[@]}"; do
|
||||
p="${REQUIRES[$c]:-}"
|
||||
[[ -n "$p" && "$c" != boot && "${WANTED[$c]:-0}" == 1 ]] && component_available "$p" && WANTED[$p]=1
|
||||
done
|
||||
for c in "${COMPONENTS[@]}"; do
|
||||
x="${EXCLUDES[$c]:-}"
|
||||
[[ -n "$x" && "${WANTED[$c]:-0}" == 1 ]] && WANTED[$x]=0
|
||||
done
|
||||
for c in "${COMPONENTS[@]}"; do
|
||||
p="${REQUIRES[$c]:-}"
|
||||
[[ -n "$p" && "${WANTED[$p]:-0}" == 0 ]] && component_available "$p" && WANTED[$c]=0
|
||||
done
|
||||
return 0
|
||||
# Single user mode only makes sense on top of the SteamOS conversion.
|
||||
# (On NVIDIA the conversion isn't offered: single user mode logs in by itself, see lib/single-user.sh.)
|
||||
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; fi
|
||||
if [[ "$c" == silent && "${WANTED[silent]}" == 1 ]]; then WANTED[gaming]=1; fi
|
||||
silent_normalize
|
||||
# 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
|
||||
if [[ "$c" == machine && "${WANTED[machine]}" == 0 ]]; then WANTED[kpin]=0; WANTED[bios]=0; fi
|
||||
if [[ "$c" == kpin && "${WANTED[kpin]}" == 1 ]]; then WANTED[machine]=1; fi
|
||||
if [[ "$c" == bios && "${WANTED[bios]}" == 1 ]]; then WANTED[machine]=1; fi
|
||||
# Steam starts Steamify through the shortcut's start script.
|
||||
# Opt-out, like the power-off fix: ticked along with the shortcut.
|
||||
if [[ "$c" == launcher ]] && component_available steamgame; then WANTED[steamgame]=${WANTED[launcher]}; fi
|
||||
if [[ "$c" == steamgame && "${WANTED[steamgame]}" == 1 ]]; then WANTED[launcher]=1; fi
|
||||
}
|
||||
|
||||
show_menu() {
|
||||
@@ -360,16 +346,16 @@ show_menu() {
|
||||
menu_visible "$c" || continue
|
||||
i=$((i + 1)); MENU_ITEMS[$i]=$c
|
||||
# Pad the plain word, then colour it: colour codes would count as width.
|
||||
now="off"; is_current "$c" && now="on"
|
||||
now="off"; [[ "${CURRENT[$c]}" == 1 ]] && now="on"
|
||||
is_action "$c" && now="-"
|
||||
now="$(printf '%-6s' "$now")"
|
||||
is_current "$c" && now="${now/on/${c_green}on${c_reset}}"
|
||||
want="[ ]"; is_wanted "$c" && want="[x]"
|
||||
[[ "${CURRENT[$c]}" == 1 ]] && now="${now/on/${c_green}on${c_reset}}"
|
||||
want="[ ]"; [[ "${WANTED[$c]}" == 1 ]] && want="[x]"
|
||||
if [[ "$c" == boot ]]; then
|
||||
# A choice rather than a checkbox: Now/Want show the mode.
|
||||
now="gaming"; is_current boot && now="desk"
|
||||
is_current gaming || now="-"
|
||||
want="gaming"; is_wanted boot && want="desk"
|
||||
now="gaming"; [[ "${CURRENT[boot]}" == 1 ]] && now="desk"
|
||||
[[ "${CURRENT[gaming]}" == 1 ]] || now="-"
|
||||
want="gaming"; [[ "${WANTED[boot]}" == 1 ]] && want="desk"
|
||||
printf " %-3s %-6s %-6s └ %s\n" "$i" "$now" "$want" "$(boot_choice "${WANTED[boot]}")"
|
||||
elif ! component_selectable "$c"; then
|
||||
local tree=""; [[ -n "${PARENT[$c]:-}" ]] && tree=" └ "
|
||||
@@ -408,8 +394,8 @@ draw_menu_tui() {
|
||||
for c in "${COMPONENTS[@]}"; do
|
||||
menu_visible "$c" || continue
|
||||
MENU_ITEMS[$i]=$c
|
||||
box="[ ]"; is_wanted "$c" && box="[${c_green}x${c_reset}]"
|
||||
state=" (now: off)"; is_current "$c" && state=" (now: ${c_green}on${c_reset})"
|
||||
box="[ ]"; [[ "${WANTED[$c]}" == 1 ]] && box="[${c_green}x${c_reset}]"
|
||||
state=" (now: off)"; [[ "${CURRENT[$c]}" == 1 ]] && state=" (now: ${c_green}on${c_reset})"
|
||||
is_action "$c" && state=" (opt-in, runs once)"
|
||||
line="$box ${LABEL[$c]}"
|
||||
if [[ "$c" == boot ]]; then
|
||||
@@ -417,9 +403,9 @@ draw_menu_tui() {
|
||||
# Indented under the conversion, whose sub-option it is.
|
||||
line="$(boot_choice "${WANTED[boot]}")"
|
||||
line=" └ ${line/\[/[${c_green}}"; line="${line/\]/${c_reset}]}"
|
||||
local mode=gamescope; is_current boot && mode=desktop
|
||||
local mode=gamescope; [[ "${CURRENT[boot]}" == 1 ]] && mode=desktop
|
||||
state=" (${c_bold}←/→${c_reset} choose)"
|
||||
is_current gaming && state=" (now: $mode; ${c_bold}←/→${c_reset} choose)"
|
||||
[[ "${CURRENT[gaming]}" == 1 ]] && state=" (now: $mode; ${c_bold}←/→${c_reset} choose)"
|
||||
elif [[ -n "${PARENT[$c]:-}" ]]; then
|
||||
line=" └ $line"
|
||||
fi
|
||||
@@ -446,6 +432,7 @@ draw_menu_tui() {
|
||||
run_menu_tui() {
|
||||
local cursor=0 key rest count
|
||||
tput civis 2>/dev/null
|
||||
trap 'tput cnorm 2>/dev/null' EXIT
|
||||
while true; do
|
||||
draw_menu_tui "$cursor"
|
||||
count=${#MENU_ITEMS[@]}
|
||||
@@ -507,7 +494,7 @@ plan_changes() {
|
||||
done
|
||||
for c in "${COMPONENTS[@]}"; do
|
||||
component_available "$c" || continue
|
||||
is_wanted "$c" || continue
|
||||
[[ "${WANTED[$c]}" == 1 ]] || continue
|
||||
if is_action "$c"; then TO_ENABLE+=("$c"); continue; fi
|
||||
if [[ "${CURRENT[$c]}" == 0 || "$REAPPLY" == true ]] || feature_outdated "$c" ||
|
||||
[[ "$c" == gaming && "${CURRENT[single]}" != "${WANTED[single]}" ]]; then
|
||||
@@ -519,7 +506,7 @@ plan_changes() {
|
||||
apply_changes() {
|
||||
local c failed=()
|
||||
LOGIN_MANAGER=plasmalogin
|
||||
is_wanted single && LOGIN_MANAGER=sddm
|
||||
[[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm
|
||||
|
||||
for c in "${TO_DISABLE[@]}"; do
|
||||
if [[ "$c" == boot ]]; then echo; echo -e "${c_bold}Boot into: gamescope${c_reset}"
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ bootstrap_yay() {
|
||||
sudo pacman -S --needed --noconfirm base-devel git || return 1
|
||||
|
||||
local tmp_dir
|
||||
make_tmpdir tmp_dir
|
||||
tmp_dir="$(mktemp -d)"
|
||||
info "Cloning yay-bin from the AUR..."
|
||||
if ! git clone --depth 1 https://aur.archlinux.org/yay-bin.git "$tmp_dir/yay-bin"; then
|
||||
err "Failed to clone yay-bin from the AUR."
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ single_wallet_enable() {
|
||||
ok "Single user mode's wallet (no password) is back; your own is kept as kdewallet.kwl$WALLET_BACKUP."
|
||||
return 0
|
||||
fi
|
||||
make_tmpdir tmp
|
||||
tmp="$(mktemp -d)"
|
||||
if ! fetch_valve_presets "$tmp"; then
|
||||
rm -rf "$tmp"
|
||||
warn "Couldn't get Valve's empty wallet; apps may still ask for the wallet password."
|
||||
|
||||
+1
-1
@@ -123,7 +123,7 @@ migrate_layout() {
|
||||
"$(wizard_desktop_dir)/$WIZARD_DESKTOP_NAME"; do
|
||||
[[ -f "$f" ]] && sed -i "s|$old_bin/|$STEAMIFY_BIN/|g" "$f"
|
||||
done
|
||||
best_effort user_systemctl daemon-reload
|
||||
user_systemctl daemon-reload 2>/dev/null
|
||||
fi
|
||||
if [[ -f "$old_icon" ]]; then
|
||||
if [[ -e "$WIZARD_ICON" ]]; then rm -f "$old_icon"; else mv "$old_icon" "$WIZARD_ICON"; fi
|
||||
|
||||
+14
-31
@@ -16,51 +16,34 @@ steam_enable() {
|
||||
}
|
||||
|
||||
# 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
|
||||
|
||||
# no window) on the Plasma 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" ]]
|
||||
}
|
||||
|
||||
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
|
||||
best_effort user_systemctl disable --now steam-desktop-autostart.service
|
||||
rm -f "$unit"
|
||||
user_systemctl daemon-reload
|
||||
[[ -f "$HOME/.config/systemd/user/steam-desktop-autostart.service" ]]
|
||||
}
|
||||
|
||||
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."
|
||||
local systemd_user_dir="$HOME/.config/systemd/user"
|
||||
mkdir -p "$systemd_user_dir"
|
||||
service_file steam-desktop-autostart.service > "$systemd_user_dir/steam-desktop-autostart.service"
|
||||
# Clean out old .desktop shortcut so they don't fight
|
||||
rm -f "$HOME/.config/autostart/steam.desktop"
|
||||
user_systemctl daemon-reload
|
||||
user_systemctl enable --now steam-desktop-autostart.service
|
||||
ok "Steam starts silently on the desktop."
|
||||
}
|
||||
|
||||
silent_disable() {
|
||||
silent_old_unit_remove
|
||||
silent_status && rm -f "$STEAM_AUTOSTART"
|
||||
user_systemctl disable --now steam-desktop-autostart.service 2>/dev/null
|
||||
rm -f "$HOME/.config/systemd/user/steam-desktop-autostart.service"
|
||||
user_systemctl daemon-reload
|
||||
ok "Steam no longer starts by itself on the desktop."
|
||||
}
|
||||
|
||||
steam_disable() {
|
||||
info "Removing Steam desktop settings..."
|
||||
# The autostart entry is the `silent` option's: removed with it, and here
|
||||
# The autostart unit 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)."
|
||||
}
|
||||
|
||||
+6
-2
@@ -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 (silently, in the tray).
|
||||
# again if it was running (through our autostart unit when that's there).
|
||||
local was_running=false i
|
||||
if pgrep -x steam >/dev/null; then
|
||||
steamgame_steam_busy && return 1
|
||||
@@ -72,7 +72,11 @@ steamgame_edit() {
|
||||
fi
|
||||
"$1"; local rc=$?
|
||||
if [[ "$was_running" == true ]]; then
|
||||
systemd-run --user --collect -q steam -silent >/dev/null 2>&1
|
||||
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
|
||||
info "Steam is starting again."
|
||||
fi
|
||||
return "$rc"
|
||||
|
||||
+194
-48
@@ -15,6 +15,164 @@ detect_valve_fremont() {
|
||||
[[ "$vendor" == "OEM" && "$product" == "F7F" ]]
|
||||
}
|
||||
|
||||
# Kernel pinned on a Steam Machine (optional): newer linux-cachyos releases
|
||||
# rebooted it instead of shutting down, which the power-off fix now handles. The packages are kept in
|
||||
# PINNED_KERNEL_DIR, so re-applying (or reinstalling after an update slipped
|
||||
# through) needs no download.
|
||||
PINNED_KERNEL_VER="7.1.6-1"
|
||||
PINNED_KERNEL_KVER="7.1.6-1-cachyos"
|
||||
PINNED_KERNEL_PKGS=(linux-cachyos linux-cachyos-headers)
|
||||
PINNED_KERNEL_DIR="${PINNED_KERNEL_DIR:-/var/cache/steamify/kernel}"
|
||||
# SHA-256 of each package, so a file from any source is the one reviewed
|
||||
# here; its CachyOS signature is checked as well.
|
||||
declare -A PINNED_KERNEL_SHA256=(
|
||||
[linux-cachyos]=417fcd07102192b86e78e92ed7171d5a49378e9f57faea3fa2c7c541e9f68d08
|
||||
[linux-cachyos-headers]=d069866a11d9092746e1d5133cefd8924f027da2c9bf5af38d30b3aed6ded9bf
|
||||
)
|
||||
# Tried in order, after the kernel dir and pacman's cache. Our own release
|
||||
# always has these files; the archive keeps every release; the mirror only
|
||||
# the current one. PINNED_KERNEL_URL puts another source (a directory
|
||||
# holding the files) in front.
|
||||
PINNED_KERNEL_SOURCES=(
|
||||
${PINNED_KERNEL_URL:+"$PINNED_KERNEL_URL"}
|
||||
"https://github.com/theupriser/steamify-cachyos/releases/download/kernel-$PINNED_KERNEL_VER"
|
||||
"https://archive.cachyos.org/archive/cachyos"
|
||||
"https://mirror.cachyos.org/repo/x86_64/cachyos"
|
||||
)
|
||||
|
||||
pinned_kernel_installed() {
|
||||
local p
|
||||
for p in "${PINNED_KERNEL_PKGS[@]}"; do
|
||||
[[ "$(pacman -Q "$p" 2>/dev/null)" == "$p $PINNED_KERNEL_VER" ]] || return 1
|
||||
done
|
||||
}
|
||||
|
||||
fetch_pinned_kernel_file() {
|
||||
# $1 = file name. Local kernel dir first, then pacman's cache, then the
|
||||
# download sources. Downloads go to a .part file so a broken one is never
|
||||
# mistaken for a finished one.
|
||||
local f="$1" dest="$PINNED_KERNEL_DIR/$1" src
|
||||
[[ -s "$dest" ]] && return 0
|
||||
if [[ -s "/var/cache/pacman/pkg/$f" ]]; then
|
||||
sudo cp "/var/cache/pacman/pkg/$f" "$dest" && return 0
|
||||
fi
|
||||
for src in "${PINNED_KERNEL_SOURCES[@]}"; do
|
||||
if sudo curl -fL --retry 2 --connect-timeout 15 -o "$dest.part" "$src/$f" 2>/dev/null; then
|
||||
sudo mv "$dest.part" "$dest"
|
||||
return 0
|
||||
fi
|
||||
warn "Couldn't get $f from $src, trying the next source..."
|
||||
done
|
||||
sudo rm -f "$dest.part"
|
||||
return 1
|
||||
}
|
||||
|
||||
verify_pinned_kernel_pkg() {
|
||||
# $1 = package name, $2 = file. Both checks must pass: the SHA-256 from
|
||||
# this script, and the CachyOS signature (pacman on its own installs a
|
||||
# local file without a .sig: LocalFileSigLevel = Optional).
|
||||
local sum
|
||||
sum="$(sha256sum "$2" | cut -d' ' -f1)"
|
||||
if [[ "$sum" != "${PINNED_KERNEL_SHA256[$1]}" ]]; then
|
||||
err "$(basename "$2") doesn't match its expected checksum."
|
||||
return 1
|
||||
fi
|
||||
if ! sudo pacman-key --verify "$2.sig" "$2" >/dev/null 2>&1; then
|
||||
err "$(basename "$2") doesn't have a valid CachyOS signature."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
pin_kernel_in_pacman_conf() {
|
||||
# Adds our packages to IgnorePkg in [options], keeping what's there.
|
||||
local p
|
||||
for p in "${PINNED_KERNEL_PKGS[@]}"; do
|
||||
grep -Eq "^IgnorePkg\s*=.*(\s|=)$p(\s|$)" /etc/pacman.conf && continue
|
||||
if grep -Eq '^IgnorePkg\s*=' /etc/pacman.conf; then
|
||||
sudo sed -i -E "0,/^IgnorePkg\s*=/s/^(IgnorePkg\s*=.*)$/\1 $p/" /etc/pacman.conf
|
||||
else
|
||||
sudo sed -i -E "0,/^\[options\]/s//[options]\nIgnorePkg = $p/" /etc/pacman.conf
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
unpin_kernel_in_pacman_conf() {
|
||||
local p
|
||||
for p in "${PINNED_KERNEL_PKGS[@]}"; do
|
||||
sudo sed -i -E "/^IgnorePkg\s*=/s/\s$p(\s|$)/\1/" /etc/pacman.conf
|
||||
done
|
||||
# Drop the line if nothing is left on it.
|
||||
sudo sed -i -E '/^IgnorePkg\s*=\s*$/d' /etc/pacman.conf
|
||||
}
|
||||
|
||||
install_pinned_kernel() {
|
||||
sudo mkdir -p "$PINNED_KERNEL_DIR"
|
||||
pin_kernel_in_pacman_conf
|
||||
if pinned_kernel_installed; then
|
||||
ok "Kernel $PINNED_KERNEL_VER already installed and pinned."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local p f files=()
|
||||
info "Getting kernel $PINNED_KERNEL_VER (kept in $PINNED_KERNEL_DIR)..."
|
||||
for p in "${PINNED_KERNEL_PKGS[@]}"; do
|
||||
f="$p-$PINNED_KERNEL_VER-x86_64.pkg.tar.zst"
|
||||
fetch_pinned_kernel_file "$f" && fetch_pinned_kernel_file "$f.sig" ||
|
||||
{ err "Couldn't download $f from any source."; return 1; }
|
||||
if ! verify_pinned_kernel_pkg "$p" "$PINNED_KERNEL_DIR/$f"; then
|
||||
# Removed, so the next run fetches it again.
|
||||
sudo rm -f "$PINNED_KERNEL_DIR/$f" "$PINNED_KERNEL_DIR/$f.sig"
|
||||
err "Removed it; run the wizard again to download it once more."
|
||||
return 1
|
||||
fi
|
||||
files+=("$PINNED_KERNEL_DIR/$f")
|
||||
done
|
||||
|
||||
# pacman checks each package against the .sig next to it.
|
||||
info "Installing kernel $PINNED_KERNEL_VER..."
|
||||
if ! sudo pacman -U --noconfirm "${files[@]}"; then
|
||||
err "Installing kernel $PINNED_KERNEL_VER failed."
|
||||
return 1
|
||||
fi
|
||||
[[ "$(uname -r)" != "$PINNED_KERNEL_KVER" ]] && RESTART_FOR_LOGIN=true
|
||||
ok "Kernel $PINNED_KERNEL_VER installed and pinned (restart to use it)."
|
||||
}
|
||||
|
||||
remove_kernel_pin() {
|
||||
# Back to CachyOS's current kernel. The packages stay in
|
||||
# PINNED_KERNEL_DIR, so turning support on again doesn't download.
|
||||
unpin_kernel_in_pacman_conf
|
||||
pinned_kernel_installed || return 0
|
||||
info "Updating the kernel back to CachyOS's current version..."
|
||||
sudo pacman -Syu --noconfirm ||
|
||||
{ warn "Updating the kernel failed; run: sudo pacman -Syu"; return 0; }
|
||||
RESTART_FOR_LOGIN=true
|
||||
}
|
||||
|
||||
# "Pin the kernel" menu item, a sub-option of Steam Machine support. The
|
||||
# power-off fix made it unnecessary: only shown while on, to remove it.
|
||||
kpin_available() { detect_valve_fremont && kpin_status; }
|
||||
kpin_status() {
|
||||
pinned_kernel_installed && grep -Eq '^IgnorePkg\s*=.*\slinux-cachyos(\s|$)' /etc/pacman.conf
|
||||
}
|
||||
|
||||
kpin_enable() {
|
||||
install_pinned_kernel || return 1
|
||||
# The DKMS pacman hook built leds-valve for the new headers; make sure.
|
||||
if pacman -Qi leds-valve-dkms-git >/dev/null 2>&1 &&
|
||||
! dkms status -k "$PINNED_KERNEL_KVER" leds-valve-dkms 2>/dev/null | grep -q installed; then
|
||||
sudo dkms install leds-valve-dkms/0.1 -k "$PINNED_KERNEL_KVER" ||
|
||||
warn "Building the LED driver for $PINNED_KERNEL_KVER failed."
|
||||
fi
|
||||
}
|
||||
|
||||
kpin_disable() {
|
||||
# Saved HDMI refresh boost EDIDs were for the pinned kernel only (newer
|
||||
# ones read the whole EDID), also those of displays not connected now.
|
||||
hdmi_status && hdmi_forget_all
|
||||
remove_kernel_pin
|
||||
}
|
||||
|
||||
install_kernel_headers() {
|
||||
# DKMS can only build leds-valve against kernels whose headers are
|
||||
# installed. CachyOS ships several kernels (linux-cachyos, -lts, -bore,
|
||||
@@ -42,7 +200,24 @@ install_kernel_headers() {
|
||||
sudo pacman -S --needed --noconfirm "${headers[@]}"
|
||||
}
|
||||
|
||||
led_write_dkms_override() {
|
||||
install_valve_led_driver() {
|
||||
# Installs the leds-valve DKMS driver from the AUR so the front LED
|
||||
# bar is exposed under /sys/class/leds/valve-leds[N].
|
||||
local aur_helper
|
||||
aur_helper="$(find_aur_helper)"
|
||||
if [[ -z "$aur_helper" ]]; then
|
||||
warn "No AUR helper available - can't install leds-valve-dkms-git."
|
||||
warn "Install one yourself, then run: yay -S leds-valve-dkms-git"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local -a flags
|
||||
read -ra flags <<< "$(aur_noninteractive_flags "$aur_helper")"
|
||||
|
||||
# Headers first: without them the package installs fine but DKMS
|
||||
# silently builds nothing, so modprobe later finds no module.
|
||||
install_kernel_headers || return 1
|
||||
|
||||
# The upstream Makefile builds against `uname -r` instead of the kernel
|
||||
# DKMS targets, so every other installed kernel (e.g. linux-cachyos-lts)
|
||||
# got the running kernel's tree and failed (wrong kernel, and gcc against
|
||||
@@ -57,22 +232,17 @@ led_write_dkms_override() {
|
||||
err "Couldn't write $LED_DKMS_OVERRIDE; without it the LED driver only builds for the running kernel."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
led_install_package() {
|
||||
# led_install_package <aur helper>
|
||||
local aur_helper="$1"
|
||||
local -a flags
|
||||
read -ra flags <<< "$(aur_noninteractive_flags "$aur_helper")"
|
||||
if pacman -Qi leds-valve-dkms-git >/dev/null 2>&1; then
|
||||
if ! pacman -Qi leds-valve-dkms-git >/dev/null 2>&1; then
|
||||
info "Installing leds-valve-dkms-git from the AUR via $aur_helper (non-interactive)..."
|
||||
if ! "$aur_helper" -S "${flags[@]}" leds-valve-dkms-git; then
|
||||
err "Installing leds-valve-dkms-git failed."
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
ok "leds-valve-dkms-git already installed."
|
||||
return 0
|
||||
fi
|
||||
info "Installing leds-valve-dkms-git from the AUR via $aur_helper (non-interactive)..."
|
||||
"$aur_helper" -S "${flags[@]}" leds-valve-dkms-git || { err "Installing leds-valve-dkms-git failed."; return 1; }
|
||||
}
|
||||
|
||||
led_build_all_kernels() {
|
||||
# Build for every installed kernel that has headers, so booting another
|
||||
# one (e.g. the LTS kernel) still has the driver.
|
||||
local kdir kver
|
||||
@@ -87,9 +257,7 @@ led_build_all_kernels() {
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
led_load_module() {
|
||||
# Load now, and on every boot.
|
||||
echo leds-valve | sudo tee /etc/modules-load.d/leds-valve.conf >/dev/null
|
||||
|
||||
@@ -114,26 +282,7 @@ led_load_module() {
|
||||
warn "No valve-leds nodes under /sys/class/leds/ yet. Reboot, then check with:"
|
||||
warn " ls /sys/class/leds/ | grep valve ; sudo dmesg | grep -i valve"
|
||||
fi
|
||||
}
|
||||
|
||||
install_valve_led_driver() {
|
||||
# Installs the leds-valve DKMS driver from the AUR so the front LED
|
||||
# bar is exposed under /sys/class/leds/valve-leds[N].
|
||||
local aur_helper
|
||||
aur_helper="$(find_aur_helper)"
|
||||
if [[ -z "$aur_helper" ]]; then
|
||||
warn "No AUR helper available - can't install leds-valve-dkms-git."
|
||||
warn "Install one yourself, then run: yay -S leds-valve-dkms-git"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Headers first: without them the package installs fine but DKMS
|
||||
# silently builds nothing, so modprobe later finds no module.
|
||||
install_kernel_headers || return 1
|
||||
led_write_dkms_override || return 1
|
||||
led_install_package "$aur_helper" || return 1
|
||||
led_build_all_kernels || return 1
|
||||
led_load_module
|
||||
}
|
||||
|
||||
LED_UDEV_RULE="/etc/udev/rules.d/70-valve-leds-user.rules"
|
||||
@@ -258,17 +407,14 @@ wifi_backend_disable() {
|
||||
|
||||
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 ||
|
||||
'z /sys/class/dmi/id/product_serial 0444 - - -' | sudo tee "$SERIAL_TMPFILES" > /dev/null &&
|
||||
sudo systemd-tmpfiles --create "$SERIAL_TMPFILES" ||
|
||||
{ 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).
|
||||
best_effort sudo systemd-tmpfiles --create "$SERIAL_TMPFILES" ||
|
||||
info "The serial number becomes readable for Steam at the next boot."
|
||||
return 0
|
||||
}
|
||||
|
||||
serial_disable() {
|
||||
sudo rm -f "$SERIAL_TMPFILES"
|
||||
best_effort sudo chmod 0400 /sys/class/dmi/id/product_serial
|
||||
sudo chmod 0400 /sys/class/dmi/id/product_serial 2>/dev/null
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -301,7 +447,7 @@ EOF
|
||||
wifi_backend_enable || return 1
|
||||
sudo systemctl enable --now inputplumber.service
|
||||
sudo systemctl enable --now steamos-manager.service
|
||||
best_effort user_systemctl enable steamos-manager.service
|
||||
user_systemctl enable steamos-manager.service 2>/dev/null
|
||||
# HDMI-CEC ran before this and couldn't link cecd to steamos-manager yet.
|
||||
cec_status && cec_link_steamos_manager
|
||||
|
||||
@@ -318,19 +464,19 @@ EOF
|
||||
|
||||
machine_disable() {
|
||||
info "Removing Steam Machine support..."
|
||||
best_effort user_systemctl disable --now steamos-manager.service
|
||||
best_effort sudo systemctl disable --now steamos-manager.service
|
||||
best_effort sudo systemctl disable --now inputplumber.service
|
||||
best_effort sudo pacman -Rns --noconfirm steamos-manager inputplumber
|
||||
user_systemctl disable --now steamos-manager.service 2>/dev/null
|
||||
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
|
||||
best_effort sudo modprobe -r leds-valve
|
||||
best_effort sudo pacman -Rns --noconfirm leds-valve-dkms-git
|
||||
sudo modprobe -r leds-valve 2>/dev/null
|
||||
sudo pacman -Rns --noconfirm leds-valve-dkms-git 2>/dev/null
|
||||
krevert machine
|
||||
reload_powerdevil
|
||||
best_effort sudo systemctl disable ensure-kernel-headers.service
|
||||
sudo systemctl disable ensure-kernel-headers.service 2>/dev/null
|
||||
led_dkms_override_remove
|
||||
sudo rm -f "$HEADERS_UNIT" "$HEADERS_SCRIPT"
|
||||
sudo rm -rf "$(dirname "$HEADERS_SCRIPT")" "$(dirname "$HEADERS_SCRIPT_OLD")"
|
||||
|
||||
+21
-7
@@ -8,6 +8,7 @@
|
||||
# package-owned); per-user settings go through the undo journal.
|
||||
# Sourced by steamify.sh; not meant to be run on its own.
|
||||
|
||||
VALVE_MIRROR="https://steamdeck-packages.steamos.cloud/archlinux-mirror"
|
||||
NESTED_DIR=/usr/local/share/applications/steam/holo-nested-desktop
|
||||
EXTRAS_FILES=(
|
||||
/usr/local/bin/holo-add-to-steam
|
||||
@@ -22,12 +23,25 @@ STEAM_KEYBOARD_RULE=cachyos-gamescope-boot-steam-keyboard
|
||||
|
||||
fetch_valve_presets() {
|
||||
# fetch_valve_presets <dir>: download and extract the newest
|
||||
# steamdeck-kde-presets (from the newest SteamOS release repo, jupiter-3.9,
|
||||
# jupiter-3.10, ...) into <dir>.
|
||||
local dir="$1" pkg
|
||||
pkg="$(valve_fetch_pkg jupiter steamdeck-kde-presets "$dir")" || return 1
|
||||
tar -I unzstd -xf "$pkg" -C "$dir" usr/bin usr/share ||
|
||||
{ err "Extracting $(basename "$pkg") failed."; return 1; }
|
||||
# steamdeck-kde-presets into <dir>. The newest SteamOS release repo
|
||||
# (jupiter-3.9, jupiter-3.10, ...) is found on the mirror; its pacman
|
||||
# database gives the exact file name and SHA-256, which is verified.
|
||||
local dir="$1" repo desc file_name sha256
|
||||
repo="$(curl -fsL "$VALVE_MIRROR/" | grep -oE 'jupiter-[0-9]+\.[0-9]+/' | tr -d / | sort -V | tail -n 1)"
|
||||
[[ -n "$repo" ]] && curl -fsL "$VALVE_MIRROR/$repo/os/x86_64/$repo.db" -o "$dir/repo.db" ||
|
||||
{ err "Couldn't read Valve's SteamOS package index ($VALVE_MIRROR)."; return 1; }
|
||||
desc="$(tar -tf "$dir/repo.db" 2>/dev/null | grep -E '^steamdeck-kde-presets-[0-9][^/]*/desc$' | head -n 1)"
|
||||
[[ -n "$desc" ]] || { err "No steamdeck-kde-presets in Valve's $repo repository."; return 1; }
|
||||
file_name="$(tar -xOf "$dir/repo.db" "$desc" | awk '/^%FILENAME%$/ { getline; print }')"
|
||||
sha256="$(tar -xOf "$dir/repo.db" "$desc" | awk '/^%SHA256SUM%$/ { getline; print }')"
|
||||
|
||||
info "Downloading $file_name ($repo)..."
|
||||
curl -fsL "$VALVE_MIRROR/$repo/os/x86_64/$file_name" -o "$dir/presets.pkg.tar.zst" ||
|
||||
{ err "Downloading $file_name failed."; return 1; }
|
||||
echo "$sha256 $dir/presets.pkg.tar.zst" | sha256sum -c --quiet - ||
|
||||
{ err "Checksum mismatch for $file_name; not using it."; return 1; }
|
||||
tar -I unzstd -xf "$dir/presets.pkg.tar.zst" -C "$dir" usr/bin usr/share ||
|
||||
{ err "Extracting $file_name failed."; return 1; }
|
||||
}
|
||||
|
||||
extras_enable() {
|
||||
@@ -40,7 +54,7 @@ extras_enable() {
|
||||
done
|
||||
|
||||
local tmp_dir src
|
||||
make_tmpdir tmp_dir
|
||||
tmp_dir="$(mktemp -d)"
|
||||
if ! fetch_valve_presets "$tmp_dir"; then
|
||||
rm -rf "$tmp_dir"
|
||||
return 1
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Valve's SteamOS package mirror: finding its newest repository and fetching a
|
||||
# verified package from it. Used by the SteamOS extras, the Steam Machine CEC
|
||||
# driver, the BIOS update and the os-release version.
|
||||
# Sourced by steamify.sh; not meant to be run on its own.
|
||||
|
||||
VALVE_MIRROR="https://steamdeck-packages.steamos.cloud/archlinux-mirror"
|
||||
|
||||
valve_newest_repo() {
|
||||
# valve_newest_repo <prefix> [max-seconds]: the newest <prefix>-X.Y repo
|
||||
# (jupiter, holo), compared as versions: 3.10 is newer than 3.9. Prints
|
||||
# nothing when the mirror can't be reached.
|
||||
local -a limit=()
|
||||
[[ -n "${2:-}" ]] && limit=(--max-time "$2")
|
||||
curl -fsL "${limit[@]}" "$VALVE_MIRROR/" 2>/dev/null | grep -oE "$1-[0-9]+\.[0-9]+/" | tr -d / | sort -uV | tail -n 1
|
||||
}
|
||||
|
||||
valve_repo_url() { echo "$VALVE_MIRROR/$1/os/x86_64"; }
|
||||
|
||||
valve_desc_field() {
|
||||
# valve_desc_field <db> <desc path> <FIELD>: one value from a package's
|
||||
# entry in a pacman database (FILENAME, SHA256SUM).
|
||||
tar -xOf "$1" "$2" 2>/dev/null | awk -v f="%$3%" '$0 == f { getline; print }'
|
||||
}
|
||||
|
||||
valve_fetch_pkg() {
|
||||
# valve_fetch_pkg <repo prefix> <package> <dir>: download the newest
|
||||
# <package> from the newest <prefix> repository into <dir> and print its
|
||||
# path. The file name and SHA-256 come from the repository's database and
|
||||
# the checksum is verified; the database is kept in <dir> for more calls.
|
||||
local prefix="$1" name="$2" dir="$3" repo db desc file_name sha256
|
||||
repo="$(valve_newest_repo "$prefix")"
|
||||
db="$dir/$prefix.db"
|
||||
if [[ ! -f "$db" ]]; then
|
||||
[[ -n "$repo" ]] && curl -fsL "$(valve_repo_url "$repo")/$repo.db" -o "$db" ||
|
||||
{ err "Couldn't read Valve's SteamOS package index ($VALVE_MIRROR)."; return 1; }
|
||||
fi
|
||||
desc="$(tar -tf "$db" 2>/dev/null | grep -E "^$name-[0-9][^/]*/desc$" | head -n 1)"
|
||||
[[ -n "$desc" ]] || { err "No $name in Valve's $repo repository."; return 1; }
|
||||
file_name="$(valve_desc_field "$db" "$desc" FILENAME)"
|
||||
sha256="$(valve_desc_field "$db" "$desc" SHA256SUM)"
|
||||
|
||||
info "Downloading $file_name ($repo)..." >&2
|
||||
curl -fsL "$(valve_repo_url "$repo")/$file_name" -o "$dir/$file_name" ||
|
||||
{ err "Downloading $file_name failed."; return 1; }
|
||||
echo "$sha256 $dir/$file_name" | sha256sum -c --quiet - >&2 ||
|
||||
{ err "Checksum mismatch for $file_name; not using it."; return 1; }
|
||||
echo "$dir/$file_name"
|
||||
}
|
||||
+21
-50
@@ -47,27 +47,26 @@ theme_status() {
|
||||
[[ "$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage)" == "$VAPOR_LOOKANDFEEL" ]]
|
||||
}
|
||||
|
||||
theme_install_package() {
|
||||
pacman -Q cachyos-vapor >/dev/null 2>&1 && return 0
|
||||
info "Installing cachyos-vapor..."
|
||||
sudo pacman -S --needed --noconfirm cachyos-vapor || { err "Installing cachyos-vapor failed."; return 1; }
|
||||
# Only remove it again on disable if we were the ones installing it.
|
||||
state_set theme installed_pkg 1
|
||||
}
|
||||
|
||||
theme_check_offline() {
|
||||
theme_enable() {
|
||||
if ! pacman -Q cachyos-vapor >/dev/null 2>&1; then
|
||||
info "Installing cachyos-vapor..."
|
||||
sudo pacman -S --needed --noconfirm cachyos-vapor || { err "Installing cachyos-vapor failed."; return 1; }
|
||||
# Only remove it again on disable if we were the ones installing it.
|
||||
state_set theme installed_pkg 1
|
||||
fi
|
||||
# Without a session (the ISO's installer), for a user who has never
|
||||
# logged in: the layout still CachyOS's default from /etc/skel is
|
||||
# removed (after the backup below), and Plasma builds its first layout
|
||||
# from the look-and-feel package, as --resetLayout does below.
|
||||
local layout=~/.config/plasma-org.kde.plasma.desktop-appletsrc
|
||||
if [[ -e "$layout" ]] && ! cmp -s "$layout" "/etc/skel/.config/${layout##*/}"; then
|
||||
err "Run this from the Plasma desktop: applying the Vapor layout needs a running Plasma session."
|
||||
return 1
|
||||
local offline=false layout=~/.config/plasma-org.kde.plasma.desktop-appletsrc
|
||||
if [[ -z "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ]]; then
|
||||
if [[ -e "$layout" ]] && ! cmp -s "$layout" "/etc/skel/.config/${layout##*/}"; then
|
||||
err "Run this from the Plasma desktop: applying the Vapor layout needs a running Plasma session."
|
||||
return 1
|
||||
fi
|
||||
offline=true
|
||||
fi
|
||||
}
|
||||
|
||||
theme_backup_originals() {
|
||||
# Only the first time, so a re-apply doesn't overwrite the originals.
|
||||
local backup="$STATE_DIR/theme-layout" f
|
||||
if [[ ! -d "$backup" ]]; then
|
||||
@@ -85,9 +84,7 @@ theme_backup_originals() {
|
||||
[[ -f "$f" ]] && kset theme "$f" Settings gtk-cursor-theme-name \
|
||||
"$(kreadconfig6 --file "$f" --group Settings --key gtk-cursor-theme-name)"
|
||||
done
|
||||
}
|
||||
|
||||
theme_set_look() {
|
||||
kset theme kdeglobals KDE LookAndFeelPackage "$VAPOR_LOOKANDFEEL"
|
||||
# Vapor is a dark theme: register it as the dark one, or Plasma's Dark
|
||||
# Mode toggle shows "off" and offers to switch to Breeze Dark.
|
||||
@@ -97,17 +94,6 @@ theme_set_look() {
|
||||
# scheme that is already named in the config, without writing its colors.
|
||||
kset theme kdeglobals General ColorScheme --delete
|
||||
plasma-apply-colorscheme Vapor >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
theme_enable() {
|
||||
theme_install_package || return 1
|
||||
local offline=false
|
||||
if [[ -z "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ]]; then
|
||||
theme_check_offline || return 1
|
||||
offline=true
|
||||
fi
|
||||
theme_backup_originals
|
||||
theme_set_look
|
||||
|
||||
if [[ "$offline" == true ]]; then
|
||||
rm -f ~/.config/plasma-org.kde.plasma.desktop-appletsrc ~/.config/plasmashellrc
|
||||
@@ -120,32 +106,17 @@ 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, and the default taskbar pins.
|
||||
sleep 3
|
||||
stop_plasmashell_for_edit
|
||||
taskbar_drop_discover
|
||||
single_status && single_launcher
|
||||
restart_plasmashell_if_stopped
|
||||
# The new layout has a new launcher, without single user's settings.
|
||||
if single_status; then
|
||||
sleep 3
|
||||
stop_plasmashell_for_edit
|
||||
single_launcher
|
||||
restart_plasmashell_if_stopped
|
||||
fi
|
||||
extras_enable || warn "SteamOS desktop extras not installed (see above); the theme itself is on."
|
||||
ok "Vapor theme active."
|
||||
}
|
||||
|
||||
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..."
|
||||
|
||||
+35
-3
@@ -48,11 +48,43 @@ launcher_repair() { [[ -f "$WIZARD_APPS/$WIZARD_OLD_NAME" ]] && ! launcher_statu
|
||||
|
||||
launcher_enable() {
|
||||
info "Adding the Steamify shortcut (the app on the desktop and in the launcher, Steamify Terminal in the launcher)..."
|
||||
# Both scripts download the newest release when they run (patches/).
|
||||
patch_file steamify-start.sh | fill URL="$WIZARD_URL" | install_executable "$WIZARD_LAUNCHER" 755
|
||||
install_executable "$WIZARD_LAUNCHER" 755 << EOF
|
||||
#!/bin/bash
|
||||
# Installed by Steamify CachyOS: run its newest release.
|
||||
# pipefail: a failed download must count as a failure, not an empty script.
|
||||
set -o pipefail
|
||||
if ! curl -fsSL --max-time 30 "$WIZARD_URL" | bash; then
|
||||
echo
|
||||
echo "Couldn't download or run the wizard. Are you connected to the internet?"
|
||||
echo
|
||||
read -rp "Press Enter to close this window... " _
|
||||
exit 1
|
||||
fi
|
||||
# Done: close the window after a countdown (Enter closes it right away).
|
||||
echo
|
||||
for (( i = 10; i > 0; i-- )); do
|
||||
printf '\rClosing this window in %2d seconds (Enter to close now)... ' "\$i"
|
||||
read -rs -t 1 _ && break
|
||||
done
|
||||
echo
|
||||
EOF
|
||||
|
||||
# The app, without a terminal. Only the first time, when PySide6 still
|
||||
# has to be installed, it runs in Konsole: sudo asks for the password there.
|
||||
patch_file steamify-app-start.sh | fill URL="$WIZARD_APP_URL" | install_executable "$WIZARD_APP_LAUNCHER" 755
|
||||
install_executable "$WIZARD_APP_LAUNCHER" 755 << EOF
|
||||
#!/bin/bash
|
||||
# Installed by Steamify CachyOS: start the newest release of its app.
|
||||
set -o pipefail
|
||||
if [[ "\${1:-}" != --in-terminal ]] && ! python3 -c 'import PySide6.QtQml' 2>/dev/null; then
|
||||
exec konsole -e "\$0" --in-terminal
|
||||
fi
|
||||
if ! curl -fsSL --max-time 30 "$WIZARD_APP_URL" | bash; then
|
||||
msg="Couldn't download or start Steamify. Are you connected to the internet?"
|
||||
if [[ "\${1:-}" == --in-terminal ]]; then echo; echo "\$msg"; read -rp "Press Enter to close this window... " _
|
||||
else kdialog --title Steamify --sorry "\$msg" 2>/dev/null; fi
|
||||
exit 1
|
||||
fi
|
||||
EOF
|
||||
|
||||
# The icon comes with the release; without it, Steam's own icon.
|
||||
local icon=steam tmp
|
||||
|
||||
@@ -16,4 +16,3 @@ read and reviewed on their own. `lib/*.sh` reads them with `patch_file
|
||||
| `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 |
|
||||
| `steamify-start.sh`, `steamify-app-start.sh` | `lib/wizard-shortcut.sh` (Steamify shortcut) | The two start scripts of the shortcut: "Steamify Terminal" runs the newest `steamify.sh` in Konsole, the app's launcher runs the newest `steamify-app.sh` (`fill URL=...`) |
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Installed by Steamify CachyOS: start the newest release of its app.
|
||||
set -o pipefail
|
||||
if [[ "${1:-}" != --in-terminal ]] && ! python3 -c 'import PySide6.QtQml' 2>/dev/null; then
|
||||
exec konsole -e "$0" --in-terminal
|
||||
fi
|
||||
if ! curl -fsSL --max-time 30 "@URL@" | bash; then
|
||||
msg="Couldn't download or start Steamify. Are you connected to the internet?"
|
||||
if [[ "${1:-}" == --in-terminal ]]; then echo; echo "$msg"; read -rp "Press Enter to close this window... " _
|
||||
else kdialog --title Steamify --sorry "$msg" 2>/dev/null; fi
|
||||
exit 1
|
||||
fi
|
||||
@@ -51,9 +51,8 @@ def latest():
|
||||
|
||||
def pending(new):
|
||||
# Newer than the last Steamify that ran here, and not skipped.
|
||||
seen, found = version(state("seen") or "0"), version(new)
|
||||
# A tag that isn't a plain version (a pre-release name) is never offered.
|
||||
return seen is not None and found is not None and found > seen and new != state("skipped")
|
||||
seen = version(state("seen") or "0")
|
||||
return seen is not None and version(new) > seen and new != state("skipped")
|
||||
|
||||
|
||||
def desktop():
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Installed by Steamify CachyOS: run its newest release.
|
||||
# pipefail: a failed download must count as a failure, not an empty script.
|
||||
set -o pipefail
|
||||
if ! curl -fsSL --max-time 30 "@URL@" | bash; then
|
||||
echo
|
||||
echo "Couldn't download or run the wizard. Are you connected to the internet?"
|
||||
echo
|
||||
read -rp "Press Enter to close this window... " _
|
||||
exit 1
|
||||
fi
|
||||
# Done: close the window after a countdown (Enter closes it right away).
|
||||
echo
|
||||
for (( i = 10; i > 0; i-- )); do
|
||||
printf '\rClosing this window in %2d seconds (Enter to close now)... ' "$i"
|
||||
read -rs -t 1 _ && break
|
||||
done
|
||||
echo
|
||||
@@ -1,6 +0,0 @@
|
||||
# Python: the app (ui/steamify-ui, no .py extension) and patches/*.py.
|
||||
line-length = 130
|
||||
target-version = "py312"
|
||||
|
||||
[lint]
|
||||
select = ["E", "F", "W", "B"]
|
||||
@@ -12,5 +12,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` |
|
||||
| `steamify-steam-autostart.service` | `lib/nvidia.sh` (Gaming on NVIDIA) | Steam at login on the Plasma desktop, normal or in Big Picture (user unit) |
|
||||
| `steam-desktop-autostart.service` | `lib/steam-desktop.sh` (SteamOS conversion) | Steam in the background on the Plasma desktop (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) |
|
||||
@@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=Steam Background Autostart for Virtual Keyboard
|
||||
After=graphical-session.target
|
||||
PartOf=graphical-session.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# Only in the Plasma desktop session; gamescope-session starts its own Steam.
|
||||
ExecCondition=/bin/sh -c '[ "$XDG_CURRENT_DESKTOP" = KDE ]'
|
||||
ExecStart=/usr/bin/steam -silent
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
+57
-69
@@ -15,39 +15,34 @@
|
||||
set -uo pipefail
|
||||
|
||||
# Release version, see CHANGELOG.md.
|
||||
VERSION=2.11.1
|
||||
VERSION=2.11.0
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
for lib in common valve-mirror state packages login-manager single-user steam-desktop steam-machine kernel-pin fremont-poweroff vram-booster hdmi-refresh nvidia controllers 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 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
|
||||
|
||||
require_root_helper
|
||||
|
||||
# What this run does, from the first argument:
|
||||
# --backend ... the graphical app's machine-readable mode
|
||||
# --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 --list what --defaults can set up here, as JSON
|
||||
# (an installer page is built from it); nothing else on stdout
|
||||
# --boot gamescope|desktop only change where an installed conversion
|
||||
# starts, everything else stays as it is (for scripts; no menu)
|
||||
# --first-login the installer's one-time follow-up
|
||||
# (none) the menu
|
||||
MODE=menu
|
||||
case "${1:-}" in
|
||||
--backend) MODE=backend ;;
|
||||
--defaults) MODE=defaults; [[ "${2:-}" == --list ]] && MODE=list ;;
|
||||
--boot) MODE=boot ;;
|
||||
--first-login) MODE=first-login ;;
|
||||
esac
|
||||
BACKEND=false; [[ "$MODE" == backend ]] && BACKEND=true
|
||||
BACKEND=false
|
||||
[[ "${1:-}" == --backend ]] && BACKEND=true
|
||||
# --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 [[ "$MODE" != backend && "$MODE" != list ]]; 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."
|
||||
@@ -64,14 +59,6 @@ TARGET_USER="$(id -un)"
|
||||
migrate_layout
|
||||
notify_seen
|
||||
|
||||
# One exit handler (a second trap would replace it): the cursor the menu hid
|
||||
# and the run's temp dirs, also when the run stops half way.
|
||||
on_exit() {
|
||||
[[ -t 1 ]] && tput cnorm 2>/dev/null
|
||||
cleanup_tmpdirs
|
||||
}
|
||||
trap on_exit EXIT
|
||||
|
||||
restart_needed() { [[ -n "${BIOS_NEEDS_RESTART:-}" || "$RESTART_FOR_LOGIN" == true ]]; }
|
||||
|
||||
restart_now() {
|
||||
@@ -131,62 +118,63 @@ quit_prompt() {
|
||||
return 0
|
||||
}
|
||||
|
||||
report_failed() {
|
||||
# Exit status for the scripted modes: 1 when a component had problems.
|
||||
[[ ${#FAILED[@]} -eq 0 ]] || { warn "These had problems (see above): ${FAILED[*]}"; return 1; }
|
||||
}
|
||||
# The graphical app (steamify-ui) drives the same components through
|
||||
# lib/backend.sh instead of the menu.
|
||||
if [[ "$BACKEND" == true ]]; then
|
||||
RESTART_FOR_LOGIN=false
|
||||
shift
|
||||
backend_main "$@"
|
||||
exit $?
|
||||
fi
|
||||
|
||||
run_defaults() {
|
||||
if [[ "${1:-}" == --first-login ]]; then
|
||||
first_login_run
|
||||
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."; return 1; }
|
||||
sudo -n true 2>/dev/null || { err "--defaults needs sudo without a password."; exit 1; }
|
||||
detect_components
|
||||
defaults_options "${@:2}" || return 1
|
||||
defaults_options "${@:2}" || exit 1
|
||||
plan_changes
|
||||
apply_changes
|
||||
# Run by the installer: the rest waits for the first desktop login.
|
||||
user_session || first_login_schedule
|
||||
report_failed || return 1
|
||||
[[ ${#FAILED[@]} -eq 0 ]] || { warn "These had problems (see above): ${FAILED[*]}"; exit 1; }
|
||||
ok "Done; the changes take effect after a restart."
|
||||
}
|
||||
exit 0
|
||||
fi
|
||||
|
||||
run_boot_only() {
|
||||
local mode="${2:-}" c
|
||||
if [[ "$BOOT_ONLY" == true ]]; then
|
||||
RESTART_FOR_LOGIN=false
|
||||
REAPPLY=false
|
||||
case "$mode" in
|
||||
case "${2:-}" in
|
||||
gamescope|desktop) ;;
|
||||
*) err "--boot takes gamescope or desktop"; return 1 ;;
|
||||
*) err "--boot takes gamescope or desktop"; exit 1 ;;
|
||||
esac
|
||||
detect_components
|
||||
is_current gaming || { err "--boot needs the SteamOS conversion; turn it on first."; return 1; }
|
||||
[[ "${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; [[ "$mode" == desktop ]] && WANTED[boot]=1
|
||||
WANTED[boot]=0; [[ "$2" == desktop ]] && WANTED[boot]=1
|
||||
plan_changes
|
||||
if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then
|
||||
ok "Already starting in $mode."
|
||||
return 0
|
||||
ok "Already starting in $2."
|
||||
exit 0
|
||||
fi
|
||||
apply_changes
|
||||
report_failed || return 1
|
||||
ok "Starts in $mode from the next boot on."
|
||||
}
|
||||
|
||||
case "$MODE" in
|
||||
backend)
|
||||
# The graphical app (steamify-ui) drives the same components through
|
||||
# lib/backend.sh instead of the menu.
|
||||
RESTART_FOR_LOGIN=false
|
||||
shift
|
||||
backend_main "$@"
|
||||
exit $? ;;
|
||||
first-login) first_login_run; exit 0 ;;
|
||||
list) defaults_list; exit 0 ;;
|
||||
defaults) run_defaults "$@"; exit $? ;;
|
||||
boot) run_boot_only "$@"; exit $? ;;
|
||||
esac
|
||||
[[ ${#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.
|
||||
@@ -217,7 +205,7 @@ while true; do
|
||||
if [[ "$c" == boot ]]; then echo " - boot into: desktop (from the next boot)"
|
||||
elif is_action "$c"; then echo " - run: ${LABEL[$c]%%:*} at your own risk (checks, then asks twice more)"
|
||||
elif feature_outdated "$c"; then echo " - update: ${LABEL[$c]} (changed in this version)"
|
||||
elif is_current "$c"; then echo " - re-apply: ${LABEL[$c]}"
|
||||
elif [[ "${CURRENT[$c]}" == 1 ]]; then echo " - re-apply: ${LABEL[$c]}"
|
||||
elif feature_new "$c"; then echo " - turn on: ${LABEL[$c]} (new in this version)"
|
||||
else echo " - turn on: ${LABEL[$c]}"; fi
|
||||
done
|
||||
@@ -242,11 +230,11 @@ while true; do
|
||||
for c in "${COMPONENTS[@]}"; do
|
||||
component_available "$c" && ! is_action "$c" || continue
|
||||
if [[ "$c" == boot ]]; then
|
||||
is_current gaming && echo " └ boots into: $(boot_mode "${CURRENT[boot]}")"
|
||||
[[ "${CURRENT[gaming]}" == 1 ]] && echo " └ boots into: $(boot_mode "${CURRENT[boot]}")"
|
||||
elif [[ -n "${PARENT[$c]:-}" ]]; then
|
||||
[[ "${CURRENT[${PARENT[$c]}]}" == 1 ]] || continue
|
||||
if is_current "$c"; then echo -e " └ ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " └ off ${LABEL[$c]}"; fi
|
||||
elif is_current "$c"; then echo -e " ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " off ${LABEL[$c]}"; fi
|
||||
if [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " └ ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " └ off ${LABEL[$c]}"; fi
|
||||
elif [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " off ${LABEL[$c]}"; fi
|
||||
done
|
||||
if [[ ${#FAILED[@]} -gt 0 ]]; then
|
||||
warn "These had problems (see above): ${FAILED[*]}"
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
# Tests lib/controllers.sh (extended controller support: xone, the Xbox wireless dongle, and xpadneo, Xbox Bluetooth) with a stub pacman
|
||||
# and a temp home. No root, VM or hardware needed: bash tests/controllers-test.sh
|
||||
cd "$(dirname "$0")/.." || exit 1
|
||||
T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT
|
||||
fail=0
|
||||
check() { if eval "$2"; then echo "ok $1"; else echo "FAIL $1"; fail=1; fi; }
|
||||
|
||||
SCRIPT_DIR="$PWD"; HOME="$T/home"; mkdir -p "$HOME"
|
||||
source lib/common.sh; source lib/state.sh; source lib/controllers.sh; source lib/menu.sh
|
||||
# Runs as the user, and refuses anything under the real system folders: a test must never touch them.
|
||||
sudo() { local a; for a in "$@"; do case "$a" in /etc/*|/usr/*|/boot/*|/var/*) echo "REFUSED sudo $*" >&2; return 1 ;; esac; done; "$@"; }
|
||||
info() { :; }; ok() { :; }; warn() { :; }; err() { echo "ERR $*"; }
|
||||
kwriteconfig6() { local f="" k="" v=""; while [[ $# -gt 0 ]]; do case "$1" in --file) f="$2"; shift ;; --key) k="$2"; shift ;; --group) shift ;; *) v="$1" ;; esac; shift; done; mkdir -p "$(dirname "$f")"; sed -i "/^$k=/d" "$f" 2>/dev/null; printf '%s=%s\n' "$k" "$v" >> "$f"; }
|
||||
kreadconfig6() { local f="" k=""; while [[ $# -gt 0 ]]; do case "$1" in --file) f="$2"; shift ;; --key) k="$2"; shift ;; --group|--default) shift ;; esac; shift; done; sed -n "s/^$k=//p" "$f" 2>/dev/null; }
|
||||
# A pretend package database.
|
||||
PKGS=""; PACMAN_LOG="$T/pacman.log"; : > "$PACMAN_LOG"; PACMAN_FAIL=0
|
||||
pacman() {
|
||||
case "$1" in
|
||||
-Q) local p; for p in "${@:2}"; do [[ " $PKGS " == *" $p "* ]] || return 1; done ;;
|
||||
-S) [[ $PACMAN_FAIL == 1 ]] && return 1; echo "S ${*:4}" >> "$PACMAN_LOG"; PKGS+=" ${*:4}" ;;
|
||||
-Rns) echo "R ${*:3}" >> "$PACMAN_LOG"; local p; for p in ${*:3}; do PKGS=" ${PKGS// $p / } "; done ;;
|
||||
esac
|
||||
}
|
||||
HEADERS=0; install_kernel_headers() { HEADERS=$((HEADERS + 1)); }
|
||||
|
||||
check "off to begin with" '! extended_controller_support_status'
|
||||
extended_controller_support_enable
|
||||
check "installs the repo's xpadneo-dkms, xone-dkms and the dongle firmware" 'pacman -Q xpadneo-dkms xone-dkms xone-dongle-firmware'
|
||||
check "kernel headers are made sure of first" '[[ $HEADERS == 1 ]]'
|
||||
check "what we installed is recorded" '[[ "$(state_get extended_controller_support installed_pkgs)" == "xpadneo-dkms xone-dkms xone-dongle-firmware" ]]'
|
||||
check "it is on" extended_controller_support_status
|
||||
n="$(wc -l < "$PACMAN_LOG")"; extended_controller_support_enable
|
||||
check "a second run installs nothing again" '[[ "$(wc -l < "$PACMAN_LOG")" == "$n" ]]'
|
||||
extended_controller_support_disable
|
||||
check "off: the three packages are removed again" '! pacman -Q xpadneo-dkms 2>/dev/null && ! pacman -Q xone-dkms 2>/dev/null && ! pacman -Q xone-dongle-firmware 2>/dev/null && [[ -z "$(state_get extended_controller_support installed_pkgs)" ]]'
|
||||
|
||||
# The AUR's xone-dkms-git conflicts with the repo's xone-dkms: it counts, and is kept.
|
||||
PKGS=" xone-dkms-git xone-dongle-firmware xpadneo-dkms"; : > "$PACMAN_LOG"
|
||||
check "xone-dkms-git counts as xone: already on" extended_controller_support_status
|
||||
extended_controller_support_enable; extended_controller_support_disable
|
||||
check "with xone-dkms-git: nothing installed and nothing removed" '[[ ! -s "$PACMAN_LOG" ]] && pacman -Q xone-dkms-git xpadneo-dkms xone-dongle-firmware'
|
||||
PKGS=" xone-dkms-git"
|
||||
extended_controller_support_enable
|
||||
check "xone-dkms-git present: only the missing ones are installed, never xone-dkms" 'grep -q "^S xpadneo-dkms xone-dongle-firmware$" "$PACMAN_LOG" && ! pacman -Q xone-dkms 2>/dev/null'
|
||||
extended_controller_support_disable
|
||||
check "and off removes only those two, xone-dkms-git stays" 'pacman -Q xone-dkms-git && ! pacman -Q xpadneo-dkms 2>/dev/null'
|
||||
|
||||
# A failed install is an error and records nothing.
|
||||
PKGS=""; PACMAN_FAIL=1
|
||||
check "a failed pacman is an error" '! extended_controller_support_enable >/dev/null'
|
||||
check "...and records nothing" '[[ -z "$(state_get extended_controller_support installed_pkgs)" ]]'
|
||||
PACMAN_FAIL=0
|
||||
|
||||
# Packages that were already there are never removed by turning it off.
|
||||
PKGS=" xpadneo-dkms xone-dkms xone-dongle-firmware"; : > "$PACMAN_LOG"
|
||||
extended_controller_support_enable; extended_controller_support_disable
|
||||
check "packages you had before stay" 'pacman -Q xpadneo-dkms xone-dkms xone-dongle-firmware && [[ ! -s "$PACMAN_LOG" ]]'
|
||||
|
||||
# The menu.
|
||||
check "offered on every PC" 'component_available extended_controller_support'
|
||||
check "not preselected: it builds kernel modules for every kernel" '[[ " ${NO_PRESELECT[*]} " == *" extended_controller_support "* ]]'
|
||||
check "a top-level item with a label" '[[ -z "${PARENT[extended_controller_support]:-}" && -n "${LABEL[extended_controller_support]}" ]]'
|
||||
|
||||
# The "new" badge: an opt-in option added after setup shows it, but is never ticked for you.
|
||||
component_selectable() { return 0; }
|
||||
declare -A CURRENT=([gaming]=1 [nvidia]=0 [extended_controller_support]=0 [vram]=0 [boot]=0)
|
||||
rm -f "$STATE_DIR/features.state"
|
||||
check "new opt-in option (conversion on, no record): the new badge" 'feature_new_optin extended_controller_support'
|
||||
check "...but it is not ticked for you (not a new default option)" '! feature_new extended_controller_support'
|
||||
check "...and the menu label says (new)" '[[ "$(menu_label extended_controller_support)" == *"(new)"* ]]'
|
||||
check "a new default option is still ticked for you, and is not the opt-in kind" 'feature_new vram && ! feature_new_optin vram'
|
||||
check "Boot into never gets the badge (it is a choice row)" '! feature_new_optin boot'
|
||||
state_set_features() { kwriteconfig6 --file "$STATE_DIR/features.state" --group State --key "$1" "$2"; }
|
||||
state_set_features extended_controller_support off
|
||||
check "once a run recorded it as off, the badge is gone" '! feature_new_optin extended_controller_support'
|
||||
rm -f "$STATE_DIR/features.state"
|
||||
CURRENT[gaming]=0; CURRENT[nvidia]=1
|
||||
check "on an NVIDIA PC (Gaming on NVIDIA on, no conversion) it shows too" 'feature_new_optin extended_controller_support'
|
||||
CURRENT[nvidia]=0
|
||||
check "with neither the conversion nor Gaming on NVIDIA on (a fresh PC) nothing is new" '! feature_new_optin extended_controller_support && ! feature_new vram'
|
||||
CURRENT[gaming]=1; CURRENT[extended_controller_support]=1
|
||||
check "already on: not new" '! feature_new_optin extended_controller_support'
|
||||
exit $fail
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/bin/bash
|
||||
# Test of the NVIDIA fix on a real PC with an NVIDIA GPU (the unit test fakes
|
||||
# one: tests/nvidia-test.sh). Run from the Steamify checkout:
|
||||
# bash tests/nvidia-hardware-test.sh check read-only: what is detected, what the live kernel uses, what apply would change
|
||||
# bash tests/nvidia-hardware-test.sh apply really applies the fix (sudo), then reboot and run check again
|
||||
# bash tests/nvidia-hardware-test.sh visual after trying gaming mode: was the picture clean? (asks you)
|
||||
# Every run appends to ~/steamify-nvidia-report.txt: paste that file when asking for help.
|
||||
cd "$(dirname "$0")/.." || exit 1
|
||||
# patch_file (lib/common.sh) reads the hook script from $SCRIPT_DIR/patches.
|
||||
SCRIPT_DIR="$PWD"
|
||||
REPORT="${NVIDIA_REPORT:-$HOME/steamify-nvidia-report.txt}"
|
||||
source lib/common.sh; source lib/hdmi-refresh.sh; source lib/vram-booster.sh; source lib/nvidia.sh
|
||||
mode="${1:-check}"
|
||||
fail=0
|
||||
say() { echo "$*" | tee -a "$REPORT"; }
|
||||
pass() { say "PASS $*"; }
|
||||
bad() { say "FAIL $*"; fail=1; }
|
||||
note() { say " $*"; }
|
||||
root_cat() { cat "$1" 2>/dev/null || sudo cat "$1" 2>/dev/null || echo "(unreadable)"; }
|
||||
|
||||
say ""; say "===== $(date '+%F %T') mode=$mode kernel=$(uname -r)"
|
||||
|
||||
report_system() {
|
||||
say "-- GPU and software"
|
||||
lspci -nn 2>/dev/null | grep -iE 'vga|3d|display' | while read -r l; do note "$l"; done
|
||||
command -v nvidia-smi >/dev/null && note "driver: $(nvidia-smi --query-gpu=name,driver_version --format=csv,noheader 2>&1 | head -n 1)"
|
||||
for p in nvidia-open nvidia-utils nvidia-dkms gamescope gamescope-session-cachyos linux-cachyos-nvidia-open; do
|
||||
v="$(pacman -Q "$p" 2>/dev/null)" && note "$v"
|
||||
done
|
||||
note "boot loader file: $(nvidia_boot_file || true)"
|
||||
note "cmdline: $(cat /proc/cmdline)"
|
||||
}
|
||||
|
||||
check_state() {
|
||||
say "-- Steamify's view"
|
||||
if nvidia_present; then pass "a supported NVIDIA GPU (RTX 20 series or newer) with its driver is detected"
|
||||
elif nvidia_legacy_gpu; then bad "the NVIDIA GPU is older than RTX 20 (on chwd's legacy lists in $VRAM_CHWD_IDS): the fix is not applied"
|
||||
else bad "no NVIDIA GPU with its driver detected: the fix does nothing here"; fi
|
||||
for c in /sys/class/drm/card[0-9]*/device; do
|
||||
[[ "$(cat "$c/vendor" 2>/dev/null)" == 0x10de ]] && note "NVIDIA PCI device id $(cat "$c/device") $(vram_nvidia_legacy_id "$(cat "$c/device")" && echo '(on a chwd legacy list)' || echo '(not on a legacy list: RTX 20 or newer)')"
|
||||
done
|
||||
ls "$VRAM_CHWD_IDS"/nvidia-*.ids >/dev/null 2>&1 || note "no chwd legacy lists found: every NVIDIA card counts as supported"
|
||||
local p
|
||||
for p in $NVIDIA_PARAMS; do
|
||||
if grep -qw -- "$p" /proc/cmdline; then pass "$p is on the running kernel's command line"
|
||||
elif nvidia_param_set "$p"; then bad "$p is in the boot loader's file but not booted yet: reboot"
|
||||
else bad "$p is missing (run: bash tests/nvidia-hardware-test.sh apply)"; fi
|
||||
done
|
||||
say "-- the live NVIDIA driver"
|
||||
local m
|
||||
for m in modeset fbdev; do
|
||||
v="$(root_cat /sys/module/nvidia_drm/parameters/$m)"
|
||||
if [[ "$v" == Y ]]; then pass "nvidia_drm $m = Y"; else bad "nvidia_drm $m = $v (want Y)"; fi
|
||||
done
|
||||
lsmod | grep -q '^nvidia_drm' && pass "nvidia_drm is loaded" || bad "nvidia_drm is not loaded"
|
||||
local c found=0
|
||||
for c in /sys/class/drm/card[0-9]*/device; do
|
||||
[[ "$(cat "$c/vendor" 2>/dev/null)" == 0x10de ]] || continue
|
||||
found=1; note "NVIDIA card: ${c%/device} driver=$(basename "$(readlink -f "$c/driver" 2>/dev/null)")"
|
||||
for s in "${c%/device}"/card*-*/status; do [[ -f "$s" ]] && note " $(basename "$(dirname "$s")"): $(cat "$s")"; done
|
||||
done
|
||||
[[ $found == 1 ]] || bad "no NVIDIA card in /sys/class/drm"
|
||||
say "-- initramfs"
|
||||
if nvidia_modules_everywhere; then note "every installed kernel has nvidia_drm: the early-load drop-in is used"
|
||||
else note "not every installed kernel has nvidia_drm: the early-load drop-in is skipped (parameters still apply)"; fi
|
||||
nvidia_initramfs_ok && note "NVIDIA modules are in the initramfs config" || note "NVIDIA modules are not in the initramfs config"
|
||||
say "-- what apply would change"
|
||||
if nvidia_params_missing; then note "adds $NVIDIA_PARAMS to $(nvidia_boot_file)"; else note "kernel parameters: nothing to change"; fi
|
||||
if ! nvidia_initramfs_ok && nvidia_modules_everywhere; then note "writes $NVIDIA_INITRAMFS_CONF"; fi
|
||||
}
|
||||
|
||||
case "$mode" in
|
||||
check)
|
||||
report_system; check_state ;;
|
||||
apply)
|
||||
report_system
|
||||
say "-- applying"
|
||||
if nvidia_enable 2>&1 | tee -a "$REPORT"; [[ ${PIPESTATUS[0]} == 0 ]]; then pass "nvidia_enable finished"
|
||||
else bad "nvidia_enable failed (output above)"; fi
|
||||
say "Now reboot, then run: bash tests/nvidia-hardware-test.sh check" ;;
|
||||
visual)
|
||||
report_system
|
||||
say "-- try gaming mode now (log in to it or reboot into it), look at the screen, then answer"
|
||||
read -rp "Is the gaming mode picture clean (no corruption, no flicker)? [y/n/partly] " a
|
||||
read -rp "What did you see (one line, e.g. 'garbled colours at start, fine after login')? " what
|
||||
say "visual result: $a: $what"
|
||||
[[ "$a" == y ]] && pass "picture clean" || bad "picture not clean ($a)"
|
||||
say "-- gamescope and NVIDIA lines from this boot's log"
|
||||
{ journalctl -b --no-pager 2>/dev/null || sudo journalctl -b --no-pager 2>/dev/null; } |
|
||||
grep -iE 'gamescope|nvidia|drm|nvrm|wlserver|vulkan' | tail -n 80 | tee -a "$REPORT" | tail -n 5
|
||||
note "(more in $REPORT)" ;;
|
||||
*) echo "usage: $0 check|apply|visual"; exit 2 ;;
|
||||
esac
|
||||
say ""; say "Report: $REPORT"
|
||||
exit $fail
|
||||
@@ -0,0 +1,206 @@
|
||||
#!/bin/bash
|
||||
# Tests lib/nvidia.sh ("Gaming on NVIDIA" and its Big Picture option) and the menu rules around it, against a fake RTX 5080
|
||||
# (PCI 10de:2c02) beside an Intel iGPU, a stub modinfo, pacman and systemctl, and a temp home. No root, VM or NVIDIA
|
||||
# hardware needed: bash tests/nvidia-test.sh
|
||||
cd "$(dirname "$0")/.." || exit 1
|
||||
T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT
|
||||
fail=0
|
||||
check() { if eval "$2"; then echo "ok $1"; else echo "FAIL $1"; fail=1; fi; }
|
||||
|
||||
SCRIPT_DIR="$PWD"; HOME="$T/home"; mkdir -p "$HOME"
|
||||
source lib/common.sh; source lib/state.sh; source lib/login-manager.sh; source lib/single-user.sh; source lib/hdmi-refresh.sh; source lib/vram-booster.sh; source lib/nvidia.sh; source lib/menu.sh
|
||||
# Runs as the user, and refuses anything under the real system folders: a test must never touch them.
|
||||
sudo() { local a; for a in "$@"; do case "$a" in /etc/*|/usr/*|/boot/*|/var/*) echo "REFUSED sudo $*" >&2; return 1 ;; esac; done; "$@"; }
|
||||
info() { :; }; ok() { :; }; warn() { :; }; err() { echo "ERR $*"; }
|
||||
# KDE's config tools on plain files under the temp home (a relative file name is relative to ~/.config, as in KDE).
|
||||
kwriteconfig6() {
|
||||
local f="" k="" v="" del=0
|
||||
while [[ $# -gt 0 ]]; do case "$1" in --file) f="$2"; shift ;; --key) k="$2"; shift ;; --group) shift ;; --delete) del=1 ;; *) v="$1" ;; esac; shift; done
|
||||
[[ "$f" == /* ]] || f="$HOME/.config/$f"; mkdir -p "$(dirname "$f")"
|
||||
if [[ $del == 1 ]]; then sed -i "/^$k=/d" "$f" 2>/dev/null; else sed -i "/^$k=/d" "$f" 2>/dev/null; printf '%s=%s\n' "$k" "$v" >> "$f"; fi
|
||||
}
|
||||
kreadconfig6() {
|
||||
local f="" k="" d="" r
|
||||
while [[ $# -gt 0 ]]; do case "$1" in --file) f="$2"; shift ;; --key) k="$2"; shift ;; --default) d="$2"; shift ;; --group) shift ;; esac; shift; done
|
||||
[[ "$f" == /* ]] || f="$HOME/.config/$f"
|
||||
r="$(sed -n "s/^$k=//p" "$f" 2>/dev/null)"; printf '%s\n' "${r:-$d}"
|
||||
}
|
||||
# A pretend package database and systemd: what the tests assert on.
|
||||
PKGS=""; PACMAN_LOG="$T/pacman.log"; : > "$PACMAN_LOG"; ENABLED=0
|
||||
pacman() {
|
||||
case "$1" in
|
||||
-Q) [[ " $PKGS " == *" $2 "* ]] ;;
|
||||
-S) echo "S $*" >> "$PACMAN_LOG"; PKGS+=" ${*: -1}" ;;
|
||||
-Rns) echo "R $*" >> "$PACMAN_LOG"; PKGS=" ${PKGS//${*: -1}/} " ;;
|
||||
esac
|
||||
}
|
||||
user_systemctl() {
|
||||
case "$1" in
|
||||
enable) ENABLED=1 ;;
|
||||
disable) ENABLED=0 ;;
|
||||
is-enabled) [[ $ENABLED == 1 ]] ;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
FAKE_DRIVER=1; modinfo() { [[ "$FAKE_DRIVER" == 1 ]]; }
|
||||
GAMING_ON=0; gaming_status() { [[ "$GAMING_ON" == 1 ]]; }
|
||||
UNIT="$HOME/.config/systemd/user/$NVIDIA_UNIT"
|
||||
|
||||
mkdir -p "$T/drm/card0/device" "$T/drm/card1/device"
|
||||
echo 0x8086 > "$T/drm/card0/device/vendor"; echo 0x030000 > "$T/drm/card0/device/class"
|
||||
echo 0x10de > "$T/drm/card1/device/vendor"; echo 0x030000 > "$T/drm/card1/device/class"
|
||||
echo 0x2c02 > "$T/drm/card1/device/device"
|
||||
NVIDIA_DRM_DIR="$T/drm"
|
||||
backup_file() { [[ -f "$1.bak" ]] || cp "$1" "$1.bak"; }
|
||||
REBUILDS=0; nvidia_rebuild_boot() { REBUILDS=$((REBUILDS + 1)); }
|
||||
: > "$T/cmdline"; NVIDIA_CMDLINE="$T/cmdline" # the running kernel's command line: without the parameters
|
||||
NVIDIA_INITRAMFS_CONF="$T/90-steamify-nvidia.conf"
|
||||
NVIDIA_SCRIPT="$T/libexec/steamify-nvidia-initramfs"; NVIDIA_HOOK="$T/hooks/85-steamify-nvidia-initramfs.hook"
|
||||
mkdir -p "$T/mods/6.1.0-cachyos/kernel"; : > "$T/mods/6.1.0-cachyos/kernel/nvidia-drm.ko.zst"; : > "$T/mods/6.1.0-cachyos/pkgbase"
|
||||
NVIDIA_MODULES_DIR="$T/mods"
|
||||
NVIDIA_LIMINE_CONF="$T/none/limine.conf"; NVIDIA_SDBOOT_DIR="$T/none/entries"; NVIDIA_GRUB_CFG="$T/none/grub.cfg" # not generated here: not checked
|
||||
mkdir -p "$T/chwd"; VRAM_CHWD_IDS="$T/chwd"
|
||||
# The component's kernel step edits this boot loader file (systemd-boot's) in the tests, never a real one.
|
||||
printf 'LINUX_OPTIONS="quiet"\n' > "$T/sdboot-manage.conf"; nvidia_boot_file() { echo "$T/sdboot-manage.conf"; }
|
||||
|
||||
# --- detection
|
||||
check "finds the NVIDIA card as card1 beside an iGPU" nvidia_present
|
||||
FAKE_DRIVER=0; check "no nvidia_drm module (nouveau): not an NVIDIA PC" '! nvidia_present'
|
||||
FAKE_DRIVER=1; NVIDIA_DRM_DIR="$T/none"; check "no NVIDIA GPU: not an NVIDIA PC" '! nvidia_present'
|
||||
NVIDIA_DRM_DIR="$T/drm"
|
||||
|
||||
# --- which items the menu shows
|
||||
check "NVIDIA PC: the SteamOS conversion and its gamescope options are hidden" '! component_available gaming && ! component_available boot && ! component_available glyphs'
|
||||
check "NVIDIA PC: Gaming on NVIDIA and Big Picture are shown" 'component_available nvidia && component_available bigpicture'
|
||||
GAMING_ON=1
|
||||
check "NVIDIA PC with the conversion already on: it is shown, so it can be turned off" 'component_available gaming && component_available boot'
|
||||
check "...and Gaming on NVIDIA is hidden (both would start Steam)" '! component_available nvidia && ! component_available bigpicture'
|
||||
GAMING_ON=0; NVIDIA_DRM_DIR="$T/none"
|
||||
check "no NVIDIA: the conversion is shown as before" 'component_available gaming && component_available boot'
|
||||
check "...and Gaming on NVIDIA is hidden" '! component_available nvidia && ! component_available bigpicture'
|
||||
NVIDIA_DRM_DIR="$T/drm"
|
||||
|
||||
# --- "Add as non-Steam game" is for gaming mode's controller: not offered on an NVIDIA PC (unless already on)
|
||||
steamgame_available() { return 0; }; STEAMGAME_ON=0; steamgame_status() { [[ "$STEAMGAME_ON" == 1 ]]; }
|
||||
check "NVIDIA PC: Add as non-Steam game is hidden" '! component_available steamgame'
|
||||
STEAMGAME_ON=1; check "...unless it is already on (so it can be turned off)" 'component_available steamgame'; STEAMGAME_ON=0
|
||||
NVIDIA_DRM_DIR="$T/none"; check "no NVIDIA: it is offered as before" 'component_available steamgame'
|
||||
steamgame_available() { return 1; }; check "no NVIDIA, no Steam account yet: still not offered" '! component_available steamgame'
|
||||
steamgame_available() { return 0; }; NVIDIA_DRM_DIR="$T/drm"
|
||||
|
||||
# --- turning it on
|
||||
check "off to begin with" '! nvidia_status && ! bigpicture_status'
|
||||
nvidia_enable
|
||||
check "Steam is installed when missing, and recorded as ours" 'grep -q "^S " "$PACMAN_LOG" && [[ "$(state_get nvidia installed_pkgs)" == steam ]]'
|
||||
check "the autostart unit is written: Steam without arguments" 'grep -q "^ExecStart=/usr/bin/steam $" "$UNIT"'
|
||||
check "it is on" 'nvidia_status && ! bigpicture_status'
|
||||
n="$(wc -l < "$PACMAN_LOG")"; nvidia_enable
|
||||
check "a second run installs nothing again" '[[ "$(wc -l < "$PACMAN_LOG")" == "$n" ]]'
|
||||
|
||||
# --- Big Picture
|
||||
bigpicture_enable
|
||||
check "Big Picture: Steam starts with -gamepadui" 'grep -q "^ExecStart=/usr/bin/steam -gamepadui$" "$UNIT" && bigpicture_status'
|
||||
nvidia_enable
|
||||
check "turning Gaming on NVIDIA on again keeps Big Picture" 'bigpicture_status'
|
||||
check "Big Picture: Plasma starts with an empty session" '[[ "$(kreadconfig6 --file ksmserverrc --group General --key loginMode)" == emptySession ]]'
|
||||
bigpicture_disable
|
||||
check "Big Picture off: Steam's normal window again" '! bigpicture_status && nvidia_status'
|
||||
check "...and the session setting is back as it was (not set)" '[[ -z "$(kreadconfig6 --file ksmserverrc --group General --key loginMode)" ]]'
|
||||
|
||||
# --- turning it off
|
||||
bigpicture_enable; nvidia_disable
|
||||
check "off: the unit is gone and Steam, which we installed, is removed" '! nvidia_status && [[ ! -e "$UNIT" ]] && ! pacman -Q steam && [[ -z "$(state_get nvidia installed_pkgs)" ]]'
|
||||
check "Big Picture without the unit does nothing" 'bigpicture_enable && bigpicture_disable && [[ ! -e "$UNIT" ]]'
|
||||
|
||||
# --- Steam that was there already is never removed
|
||||
PKGS=" steam"; : > "$PACMAN_LOG"
|
||||
nvidia_enable; nvidia_disable
|
||||
check "Steam installed before is kept, and not installed again" '[[ ! -s "$PACMAN_LOG" ]] && pacman -Q steam'
|
||||
|
||||
# --- kernel parameters and early modules (a supported card only: RTX 20 series or newer)
|
||||
check "RTX 5080 (2c02) is supported: no chwd legacy lists" nvidia_supported
|
||||
printf '1c03\n1b80\n' > "$T/chwd/nvidia-580.ids" # a GTX 1060 and a GTX 1080: chwd's 580xx legacy list
|
||||
check "RTX 5080 is not on a legacy list" 'nvidia_supported && ! nvidia_legacy_gpu'
|
||||
echo 0x1c03 > "$T/drm/card1/device/device"
|
||||
check "GTX 1060 (older than RTX 20): no kernel setup, but still an NVIDIA PC" '! nvidia_supported && nvidia_legacy_gpu && nvidia_present'
|
||||
echo 0x2c02 > "$T/drm/card1/device/device"
|
||||
|
||||
for loader in limine limineplus sdboot grub; do
|
||||
case $loader in
|
||||
limine) F="$T/limine"; printf 'KERNEL_CMDLINE[default]="quiet splash"\n' > "$F" ;;
|
||||
# CachyOS writes its line as "+=" and may already have nvidia_drm.modeset=1 in it.
|
||||
limineplus) F="$T/limine"; printf 'KERNEL_CMDLINE[default]+="quiet splash nvidia_drm.modeset=1"\n' > "$F" ;;
|
||||
sdboot) F="$T/sdboot-manage.conf"; printf 'LINUX_OPTIONS="quiet splash"\n' > "$F" ;;
|
||||
grub) F="$T/grub"; printf 'GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"\n' > "$F" ;;
|
||||
esac
|
||||
nvidia_boot_file() { echo "$F"; }
|
||||
rm -f "$NVIDIA_INITRAMFS_CONF"; REBUILDS=0; ORIG="$(cat "$F")"
|
||||
nvidia_kernel_enable
|
||||
check "$loader: parameters added" 'grep -q "nvidia-drm.modeset=1" "$F" && grep -q "nvidia-drm.fbdev=1" "$F"'
|
||||
check "$loader: initramfs drop-in written" 'grep -q nvidia_drm "$NVIDIA_INITRAMFS_CONF"'
|
||||
check "$loader: script and pacman hook installed" '[[ -x "$NVIDIA_SCRIPT" ]] && grep -q "^Exec = $NVIDIA_SCRIPT sync" "$NVIDIA_HOOK"'
|
||||
check "$loader: backup made, rebuilt once" '[[ -f "$F.bak" && $REBUILDS == 1 ]]'
|
||||
SUM="$(cat "$F")"; nvidia_kernel_enable
|
||||
check "$loader: second run changes nothing" '[[ "$(cat "$F")" == "$SUM" && $REBUILDS == 1 ]]'
|
||||
nvidia_kernel_disable
|
||||
check "$loader: disable restores the file, drops the drop-in, script and hook" '[[ "$(cat "$F")" == "$ORIG" && ! -f "$NVIDIA_INITRAMFS_CONF" && ! -e "$NVIDIA_SCRIPT" && ! -e "$NVIDIA_HOOK" ]]'
|
||||
done
|
||||
|
||||
# A missing patches/ file must be an error, never an empty script or hook.
|
||||
( SCRIPT_DIR="$T/no-patches"; NVIDIA_SCRIPT="$T/empty/script"; NVIDIA_HOOK="$T/empty/hook"; ! nvidia_hook_install 2>/dev/null && [[ ! -s "$T/empty/script" ]] ) && echo "ok a missing patch file is an error, nothing empty installed" || { echo "FAIL a missing patch file is an error, nothing empty installed"; fail=1; }
|
||||
# The pacman hook's script decides again at every kernel change.
|
||||
syncit() { NVIDIA_INITRAMFS_CONF="$NVIDIA_INITRAMFS_CONF" NVIDIA_MODULES_DIR="$NVIDIA_MODULES_DIR" bash "$NVIDIA_SCRIPT" sync; }
|
||||
nvidia_hook_install; rm -f "$NVIDIA_INITRAMFS_CONF"
|
||||
syncit; check "hook script: every kernel has the modules -> drop-in written" '[[ -f "$NVIDIA_INITRAMFS_CONF" ]]'
|
||||
mkdir -p "$T/mods/6.6.0-lts/kernel"; : > "$T/mods/6.6.0-lts/pkgbase"
|
||||
syncit; check "hook script: a new kernel without modules -> drop-in removed" '[[ ! -f "$NVIDIA_INITRAMFS_CONF" ]]'
|
||||
: > "$T/mods/6.6.0-lts/kernel/nvidia-drm.ko.zst"
|
||||
syncit; check "hook script: the modules arrive (DKMS built) -> drop-in back" '[[ -f "$NVIDIA_INITRAMFS_CONF" ]]'
|
||||
rm -rf "$T/mods/6.6.0-lts"; mkdir -p "$T/mods/5.0.0-leftover"; : > "$T/mods/5.0.0-leftover/modules.dep"
|
||||
syncit; check "hook script: a leftover folder of a removed kernel is ignored" '[[ -f "$NVIDIA_INITRAMFS_CONF" ]]'
|
||||
rm -rf "$T/mods/5.0.0-leftover"; nvidia_kernel_disable
|
||||
# A second kernel without the NVIDIA modules: no early-load drop-in, parameters still set.
|
||||
mkdir -p "$T/mods/6.6.0-lts/kernel"; : > "$T/mods/6.6.0-lts/pkgbase"; F="$T/sdboot-manage.conf"; printf 'LINUX_OPTIONS="quiet"\n' > "$F"
|
||||
nvidia_boot_file() { echo "$F"; }; rm -f "$NVIDIA_INITRAMFS_CONF"; REBUILDS=0; nvidia_kernel_enable
|
||||
check "kernel without nvidia modules: no drop-in, parameters set" '[[ ! -f "$NVIDIA_INITRAMFS_CONF" ]] && grep -q "nvidia-drm.fbdev=1" "$F"'
|
||||
rm -rf "$T/mods/6.6.0-lts"
|
||||
# Generated boot entries without the parameters (the initramfs build failed): an error.
|
||||
printf 'linux /vmlinuz root=/dev/sda1\n' > "$T/grub.cfg"; NVIDIA_GRUB_CFG="$T/grub.cfg"
|
||||
F="$T/grub"; printf 'GRUB_CMDLINE_LINUX_DEFAULT="quiet"\n' > "$F"; nvidia_boot_file() { echo "$F"; }; rm -f "$NVIDIA_INITRAMFS_CONF"
|
||||
check "boot entries without the parameters: error" '! nvidia_kernel_enable >/dev/null'
|
||||
NVIDIA_GRUB_CFG="$T/none/grub.cfg"
|
||||
# A config file without the setting to edit: an error, not "applied".
|
||||
F="$T/sdboot-manage.conf"; printf '#LINUX_OPTIONS=""\n' > "$F"; nvidia_boot_file() { echo "$F"; }; REBUILDS=0
|
||||
check "no setting to edit: error, nothing rebuilt" '! nvidia_kernel_enable >/dev/null && [[ $REBUILDS == 0 ]]'
|
||||
FAKE_DRIVER=0; NVIDIA_DRM_DIR="$T/drm"; printf 'LINUX_OPTIONS="quiet"\n' > "$T/sdboot"; F="$T/sdboot"; REBUILDS=0; nvidia_kernel_enable
|
||||
check "no NVIDIA driver: nothing touched" '[[ "$(cat "$F")" == "LINUX_OPTIONS=\"quiet\"" && $REBUILDS == 0 ]]'
|
||||
|
||||
# --- single user mode on NVIDIA: its own login step (SDDM, autologin into Plasma) without the conversion
|
||||
FAKE_DRIVER=1; NVIDIA_DRM_DIR="$T/drm" # an earlier section ended without the driver
|
||||
SW="$T/switches.log"; : > "$SW"; TARGET_USER=tester; SINGLE_AUTOLOGIN="$T/sddm.conf.d/zzz-steamify-autologin.conf"
|
||||
switch_to_sddm() { echo sddm >> "$SW"; }; switch_to_plasmalogin() { echo plasmalogin >> "$SW"; }
|
||||
remove_sddm_base_autologin() { :; } # would look at the real /etc/sddm.conf
|
||||
GAMING_ON=0
|
||||
check "single user mode is offered on NVIDIA without the conversion..." 'component_available single'
|
||||
GAMING_ON=1; check "...and with it" 'component_available single'; GAMING_ON=0
|
||||
single_login_enable
|
||||
check "single user: SDDM, autologin of the user into the Plasma session" 'grep -qx "User=tester" "$SINGLE_AUTOLOGIN" && grep -qx "Session=plasma.desktop" "$SINGLE_AUTOLOGIN" && grep -qx "Relogin=true" "$SINGLE_AUTOLOGIN" && grep -qx sddm "$SW"'
|
||||
c1="$(cat "$SINGLE_AUTOLOGIN")"; single_login_enable
|
||||
check "single user login: a second run changes nothing" '[[ "$(cat "$SINGLE_AUTOLOGIN")" == "$c1" ]]'
|
||||
single_login_disable
|
||||
check "single user off: the file is gone and plasma-login-manager is the login manager again" '[[ ! -e "$SINGLE_AUTOLOGIN" ]] && grep -qx plasmalogin "$SW"'
|
||||
: > "$SW"; GAMING_ON=1; single_login_enable
|
||||
check "with the conversion on, single user mode leaves the login to it" '[[ ! -e "$SINGLE_AUTOLOGIN" && ! -s "$SW" ]]'
|
||||
GAMING_ON=0
|
||||
# --- the menu rules
|
||||
declare -A WANTED=([nvidia]=1 [bigpicture]=1 [gaming]=0 [boot]=0 [single]=0 [machine]=0 [poweroff]=0 [launcher]=0 [steamgame]=0)
|
||||
component_selectable() { return 0; }
|
||||
toggle_component nvidia
|
||||
check "unticking Gaming on NVIDIA unticks Big Picture" '[[ "${WANTED[nvidia]}" == 0 && "${WANTED[bigpicture]}" == 0 ]]'
|
||||
toggle_component bigpicture
|
||||
check "ticking Big Picture ticks Gaming on NVIDIA" '[[ "${WANTED[nvidia]}" == 1 && "${WANTED[bigpicture]}" == 1 ]]'
|
||||
WANTED[single]=0; WANTED[gaming]=0; toggle_component single
|
||||
check "ticking single user mode does not tick the conversion where it isn't offered" '[[ "${WANTED[single]}" == 1 && "${WANTED[gaming]}" == 0 ]]'
|
||||
check "Big Picture follows Gaming on NVIDIA in the menu" '[[ " ${COMPONENTS[*]} " == *" nvidia bigpicture "* && "${PARENT[bigpicture]}" == nvidia ]]'
|
||||
|
||||
exit $fail
|
||||
+3
-4
@@ -38,8 +38,8 @@ Item {
|
||||
|
||||
readonly property var status: backend.status || ({})
|
||||
readonly property var items: status.items || []
|
||||
// Hidden: Big Picture excludes a silent start.
|
||||
function hidden(i) { return i.id === "nvsilent" && want.bigpicture; }
|
||||
// Hidden: Big Picture excludes a silent start; on a Steam Machine a silent start needs "Boot into" desktop.
|
||||
function hidden(i) { return (i.id === "nvsilent" && want.bigpicture) || (i.needs === "boot" && boot !== "desktop"); }
|
||||
readonly property var rows: items.filter(function (i) { return (!i.parent || want[i.parent]) && !hidden(i); })
|
||||
readonly property var bios: status.bios || null
|
||||
|
||||
@@ -78,14 +78,13 @@ Item {
|
||||
w[id] = !w[id];
|
||||
// The same rules as the terminal menu (toggle_component).
|
||||
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 === "gaming" && !w.gaming) { w.single = false; w.silent = 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;
|
||||
|
||||
+2
-2
@@ -6,11 +6,11 @@ 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"] },
|
||||
changes: ["Autologin into gamescope", "Return to Gaming Mode shortcut", "Steam starts silently on the desktop"] },
|
||||
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",
|
||||
silent: { label: "Start Steam silently", 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",
|
||||
|
||||
Reference in new issue
Block a user