mirror of
https://github.com/theupriser/steamify-cachyos.git
synced 2026-10-03 17:41:58 +02:00
Compare commits
276
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5110ada40 | ||
|
|
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
|
- name: Check
|
||||||
run: |
|
run: |
|
||||||
bash -n dist/steamify.sh
|
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,
|
# 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).
|
# 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
|
idempotent. `<id>_enable` is also used to re-apply. Optional
|
||||||
`<id>_available` is checked via `component_available` (e.g. `machine`
|
`<id>_available` is checked via `component_available` (e.g. `machine`
|
||||||
only on Fremont). Turn-on order is `COMPONENTS` order, turn-off reverse;
|
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`,
|
- **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:
|
`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
|
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
|
- Bash, `set -uo pipefail` (no `-e`): check exit codes of steps that matter
|
||||||
explicitly (`|| { err ...; exit 1; }` or `|| return 1`).
|
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`,
|
- Use the helpers from `lib/common.sh` (`info`, `ok`, `warn`, `err`,
|
||||||
`ask_yn`, `backup_file`) for all output and prompts.
|
`ask_yn`, `backup_file`) for all output and prompts.
|
||||||
- Runs as the normal user; use `sudo` per command, never require root.
|
- Runs as the normal user; use `sudo` per command, never require root.
|
||||||
|
|||||||
@@ -5,34 +5,8 @@ All notable changes, per version and per commit. Versions follow
|
|||||||
`steamify.sh`. Versions before 0.7.0 were numbered afterwards,
|
`steamify.sh`. Versions before 0.7.0 were numbered afterwards,
|
||||||
one per merged pull request.
|
one per merged pull request.
|
||||||
|
|
||||||
## 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
|
## 2.11.0 - 2026-10-01
|
||||||
|
|
||||||
- `2e51d1d`, `3494924`, `fbdad5a`, `079e725`, `188799e` **feat: "Start Steam silently" sub-option: under the SteamOS conversion (`silent`, opt-in now: it was always on before; an existing one is moved from its systemd unit to the autostart entry by the "update") and under Gaming on NVIDIA (`nvsilent`, opt-in, not together with Big Picture); the SteamOS one is called "Start Steam silently in desktop mode" and is always shown, also on a Steam Machine; it is Steam's own autostart entry (`~/.config/autostart/steam.desktop`), so Steam's setting "Run Steam when my computer starts" follows it**
|
|
||||||
- `2f7b53f` **fix: the Steam Machine's serial number step no longer fails inside the ISO's installer (`/sys` is read-only in its chroot; the tmpfiles.d file applies at the next boot anyway), which made `machine` report an error and the install log end with `exit: 1`**
|
|
||||||
- `45a23af`, `bd8f387` **feat: `STEAMIFY_NO_DOWNLOAD` (the ISO's `steamify.nodownload` kernel parameter): the installed system's first login runs the Steamify bundle of the ISO instead of downloading the newest release (to test an unreleased Steamify)**
|
|
||||||
- `f3810a9` **fix: the SteamOS theme no longer leaves a blank Discover pin in the taskbar (Plasma's default taskbar pins Discover, which CachyOS doesn't install); the taskbar gets CachyOS's own pins (System Settings, files, browser), also on the ISO's first login**
|
|
||||||
- `7869b47` **feat: "Steam Deck/Machine icons" (`glyphs`) is a sub-option of the SteamOS conversion, like "Start Steam silently in desktop mode": it only changes gaming mode**
|
|
||||||
- `4d387b2` **chore: the Steamify unit tests (`controllers-test.sh`, `nvidia-test.sh`) and the NVIDIA hardware test live in steamify-cachyos-dev (`tests/`) now; `tests/` is removed here**
|
|
||||||
|
|
||||||
## 2.10.0 - 2026-10-01
|
## 2.10.0 - 2026-10-01
|
||||||
|
|
||||||
- `d6db4f6` **feat: "Gaming on NVIDIA" (nvidia) replaces the SteamOS conversion on PCs with an NVIDIA GPU, where gamescope's gaming mode shows a corrupted picture (NVIDIA bug 5240452): Steam is installed when missing and starts at login on the Plasma desktop; the sub-option "Steam starts in Big Picture" (bigpicture) starts it in Big Picture, unticked in its normal window, on an empty Plasma session so windows of the last session don't cover it. On a supported card (RTX 20 series or newer) it also sets the NVIDIA kernel parameters and early modules. The conversion and its options are hidden there unless already on**
|
- `d6db4f6` **feat: "Gaming on NVIDIA" (nvidia) replaces the SteamOS conversion on PCs with an NVIDIA GPU, where gamescope's gaming mode shows a corrupted picture (NVIDIA bug 5240452): Steam is installed when missing and starts at login on the Plasma desktop; the sub-option "Steam starts in Big Picture" (bigpicture) starts it in Big Picture, unticked in its normal window, on an empty Plasma session so windows of the last session don't cover it. On a supported card (RTX 20 series or newer) it also sets the NVIDIA kernel parameters and early modules. The conversion and its options are hidden there unless already on**
|
||||||
|
|||||||
@@ -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.
|
on the desktop too.
|
||||||
- **Boot into: gamescope / desktop** - where the PC starts. Gaming mode is
|
- **Boot into: gamescope / desktop** - where the PC starts. Gaming mode is
|
||||||
the default.
|
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
|
- Steam's **System** settings show the SteamOS release Steamify follows
|
||||||
as OS Version (e.g. `steamos-3.9`), `steam-machine` as OS Codename and
|
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
|
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
|
2. **SteamOS theme** - the Vapor look of SteamOS for the desktop, with
|
||||||
**Add to Steam** in right-click menus
|
**Add to Steam** in right-click menus
|
||||||
([details](TECHNICAL.md#steamos-desktop-look)).
|
([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
|
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
|
KDE wallet password prompts (e.g. from Brave). Typing a password with a
|
||||||
controller is no fun
|
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).
|
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
|
## Disclaimer
|
||||||
|
|
||||||
Steamify CachyOS is an unofficial community project. It is not affiliated
|
Steamify CachyOS is an unofficial community project. It is not affiliated
|
||||||
|
|||||||
+11
-17
@@ -108,11 +108,9 @@ tools write to the conf.d fragment into the base config.
|
|||||||
(never `zz-steamos-autologin.conf`, which CachyOS's tools own), and
|
(never `zz-steamos-autologin.conf`, which CachyOS's tools own), and
|
||||||
installs `/usr/local/bin/sync-steamos-session.sh` plus
|
installs `/usr/local/bin/sync-steamos-session.sh` plus
|
||||||
`sync-steamos-session.path`/`.service`;
|
`sync-steamos-session.path`/`.service`;
|
||||||
- `KWIN_IM_SHOW_ALWAYS=1` for the virtual keyboard; the sub-option `silent`
|
- `KWIN_IM_SHOW_ALWAYS=1` for the virtual keyboard and a
|
||||||
("Start Steam silently in desktop mode", opt-in, always shown) writes
|
`steam-desktop-autostart` systemd user service that starts Steam silently
|
||||||
`~/.config/autostart/steam.desktop` (the system steam.desktop with `-silent` in its Exec line). That is the
|
in Plasma only;
|
||||||
file Steam's own setting "Run Steam when my computer starts" creates and removes, so the setting shows it. An older
|
|
||||||
`steam-desktop-autostart` user unit is removed;
|
|
||||||
- the **Return to Gaming Mode** shortcut; on plasma-login-manager with a
|
- the **Return to Gaming Mode** shortcut; on plasma-login-manager with a
|
||||||
narrow sudoers rule (`/etc/sudoers.d/gamescope-session-switch`) so it can
|
narrow sudoers rule (`/etc/sudoers.d/gamescope-session-switch`) so it can
|
||||||
restart the login manager without a password prompt.
|
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"`
|
**Steam Deck/Machine icons:** `STEAM_GAMEPADUI_ARGS="-gamepadui -steamos3"`
|
||||||
in `~/.config/environment.d/` (and gamescope-session's own environment
|
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`):
|
file), which makes Steam show Steam Deck button glyphs in gaming mode.
|
||||||
it does nothing without gaming mode, ticking it ticks the conversion and turning the conversion off turns it off.
|
|
||||||
|
|
||||||
## SteamOS desktop look
|
## 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
|
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
|
`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
|
`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
|
## Gaming on NVIDIA
|
||||||
|
|
||||||
@@ -245,8 +242,7 @@ Steam at login): it installs `steam` when missing (recorded in state `nvidia`, r
|
|||||||
unit `steamify-steam-autostart.service` (`services/`, `ExecStart=/usr/bin/steam @ARGS@`, Plasma only). Its sub-option
|
unit `steamify-steam-autostart.service` (`services/`, `ExecStart=/usr/bin/steam @ARGS@`, Plasma only). Its sub-option
|
||||||
`bigpicture` ("Steam starts in Big Picture", ticked along with it) rewrites the unit with `-gamepadui` and sets
|
`bigpicture` ("Steam starts in Big Picture", ticked along with it) rewrites the unit with `-gamepadui` and sets
|
||||||
`ksmserverrc [General] loginMode=emptySession` (`kset`, undone with `krevert bigpicture`), so windows of the last session
|
`ksmserverrc [General] loginMode=emptySession` (`kset`, undone with `krevert bigpicture`), so windows of the last session
|
||||||
(Discord, a browser) aren't restored on top of Big Picture; unticked, Steam starts in its normal window. The opt-in sub-option `nvsilent` ("Start Steam silently") rewrites the unit with `-silent`
|
(Discord, a browser) aren't restored on top of Big Picture; unticked, Steam starts in its normal window. The PC always boots into the desktop: there is no gamescope session. Takes effect at the next
|
||||||
(tray only); it and `bigpicture` exclude each other. The PC always boots into the desktop: there is no gamescope session. Takes effect at the next
|
|
||||||
login.
|
login.
|
||||||
|
|
||||||
On a supported card (RTX 20 series or newer: not on chwd's legacy lists `/var/lib/chwd/ids/nvidia-*.ids`, the VRAM booster's
|
On a supported card (RTX 20 series or newer: not on chwd's legacy lists `/var/lib/chwd/ids/nvidia-*.ids`, the VRAM booster's
|
||||||
@@ -259,7 +255,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):
|
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
|
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
|
`/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`
|
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);
|
command shadowed in `/usr/local/bin`, and showed the desktop for a few seconds, then a Steam window with a black border);
|
||||||
@@ -347,7 +343,8 @@ desktop through its desktop layout (A = Enter, B = Esc, Y = Space, X =
|
|||||||
Steam's keyboard), in gaming mode as a gamepad.
|
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
|
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
|
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
|
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
|
there (added by hand, or with the path from before 2.5.0) is updated, not
|
||||||
@@ -492,8 +489,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/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/icons/hicolor/scalable/apps/steamify.svg` | The shortcut's icon |
|
||||||
| `~/.local/share/applications/steamify-*.desktop` | The launcher entries |
|
| `~/.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.*`, `steam-desktop-autostart.service` |
|
||||||
| `~/.config/systemd/user/` | `steamify-update-check.*` |
|
|
||||||
|
|
||||||
System-wide: `/usr/local/lib/steamify/` (kernel headers script),
|
System-wide: `/usr/local/lib/steamify/` (kernel headers script),
|
||||||
`/usr/src/steamify-*` (DKMS modules), `/var/cache/steamify/kernel` (the old
|
`/usr/src/steamify-*` (DKMS modules), `/var/cache/steamify/kernel` (the old
|
||||||
@@ -539,9 +535,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
|
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
|
a warning. `--boot desktop` needs `gaming`. Unknown options, ids, `boot` or
|
||||||
`bios` in the list: exit code 1 before anything changes. What needs a
|
`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
|
Plasma session waits for the first login (`lib/first-login.sh`).
|
||||||
(not installed on CachyOS: a blank icon), by CachyOS's own (`taskbar_drop_discover` in `lib/vapor-theme.sh`; skipped when Discover is
|
|
||||||
installed or the taskbar already has its own `launchers`).
|
|
||||||
|
|
||||||
With `Relogin=true`, a gamescope session that fails to start is restarted
|
With `Relogin=true`, a gamescope session that fails to start is restarted
|
||||||
immediately, which can turn into a loop - see
|
immediately, which can turn into a loop - see
|
||||||
|
|||||||
@@ -2,13 +2,6 @@
|
|||||||
|
|
||||||
See [TECHNICAL.md](TECHNICAL.md) for how each part works.
|
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
|
## Gaming mode shows a black screen or keeps restarting
|
||||||
|
|
||||||
Press **Ctrl+Alt+F3**, log in with your username and password, and run:
|
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 |
+29
-27
@@ -31,41 +31,30 @@ json_list() {
|
|||||||
printf '[%s]' "$out"
|
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() {
|
||||||
# backend_event <event> [<json fields without braces>]
|
# backend_event <event> [<json fields without braces>]
|
||||||
printf '{"event":%s%s}\n' "$(json_str "$1")" "${2:+,$2}"
|
printf '{"event":%s%s}\n' "$(json_str "$1")" "${2:+,$2}"
|
||||||
}
|
}
|
||||||
|
|
||||||
backend_status() {
|
backend_status() {
|
||||||
local c first_run=true items="" kind
|
local c first_run=true items="" kind parent now
|
||||||
detect_components
|
detect_components
|
||||||
for c in "${COMPONENTS[@]}"; do
|
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
|
done
|
||||||
for c in "${COMPONENTS[@]}"; do
|
for c in "${COMPONENTS[@]}"; do
|
||||||
component_available "$c" || continue
|
component_available "$c" || continue
|
||||||
kind=toggle; is_action "$c" && kind=action; [[ "$c" == boot ]] && kind=choice
|
kind=toggle; is_action "$c" && kind=action; [[ "$c" == boot ]] && kind=choice
|
||||||
items+="${items:+,}$(json_item_head "$c" "$kind")"
|
parent="${PARENT[$c]:-}"
|
||||||
items+=",\"on\":$(json_bool is_current "$c")"
|
now="${CURRENT[$c]:-0}"
|
||||||
items+=",\"wanted\":$(json_bool is_wanted "$c")"
|
items+="${items:+,}{\"id\":$(json_str "$c"),\"label\":$(json_str "${LABEL[$c]%%:*}")"
|
||||||
items+=",\"update\":$(json_bool feature_outdated "$c")"
|
items+=",\"hint\":$(json_str "$( [[ "${LABEL[$c]}" == *:* ]] && echo "${LABEL[$c]#*: }")")"
|
||||||
items+=",\"new\":$(json_bool feature_any_new "$c")"
|
items+=",\"kind\":\"$kind\",\"parent\":$(json_str "$parent")"
|
||||||
items+=",\"selectable\":$(json_bool component_selectable "$c")"
|
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.
|
# Why it can't be turned on, for the app's explanation.
|
||||||
[[ "$c" == vram && -n "${VRAM_NVIDIA_CASE:-}" ]] &&
|
[[ "$c" == vram && -n "${VRAM_NVIDIA_CASE:-}" ]] &&
|
||||||
items+=",\"note\":$(json_str "$(vram_nvidia_note "$VRAM_NVIDIA_CASE")")"
|
items+=",\"note\":$(json_str "$(vram_nvidia_note "$VRAM_NVIDIA_CASE")")"
|
||||||
@@ -75,11 +64,11 @@ backend_status() {
|
|||||||
for f in /dev/cec*; do [[ -e "$f" ]] && cec+="${cec:+ }$(basename "$f")"; done
|
for f in /dev/cec*; do [[ -e "$f" ]] && cec+="${cec:+ }$(basename "$f")"; done
|
||||||
local bios='null'
|
local bios='null'
|
||||||
if bios_available; then
|
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
|
fi
|
||||||
printf '{"version":%s,"firstRun":%s,"steamMachine":%s,"kernel":%s,"pinnedKernel":%s,"cecDevices":%s,"leds":%s,"bios":%s,"items":[%s]}\n' \
|
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_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 "$(uname -r)")" "$(json_str "${PINNED_KERNEL_VER:-}")" \
|
||||||
"$(json_str "$cec")" \
|
"$(json_str "$cec")" \
|
||||||
"$(compgen -G '/sys/class/leds/valve-leds*' | wc -l)" \
|
"$(compgen -G '/sys/class/leds/valve-leds*' | wc -l)" \
|
||||||
@@ -156,7 +145,20 @@ backend_apply() {
|
|||||||
WANTED[$c]=0
|
WANTED[$c]=0
|
||||||
for id in "${want[@]}"; do [[ "$id" == "$c" ]] && WANTED[$c]=1; done
|
for id in "${want[@]}"; do [[ "$id" == "$c" ]] && WANTED[$c]=1; done
|
||||||
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[nvidia]:-0}" == 0 ]] && WANTED[bigpicture]=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.
|
# Retired: only ever removed.
|
||||||
WANTED[hdmi]=0
|
WANTED[hdmi]=0
|
||||||
case "$boot" in desktop) WANTED[boot]=1; WANTED[gaming]=1 ;; gamescope) WANTED[boot]=0 ;; esac
|
case "$boot" in desktop) WANTED[boot]=1; WANTED[gaming]=1 ;; gamescope) WANTED[boot]=0 ;; esac
|
||||||
@@ -173,7 +175,7 @@ backend_apply() {
|
|||||||
backend_sudo || return 1
|
backend_sudo || return 1
|
||||||
|
|
||||||
LOGIN_MANAGER=plasmalogin
|
LOGIN_MANAGER=plasmalogin
|
||||||
is_wanted single && LOGIN_MANAGER=sddm
|
[[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm
|
||||||
local -a failed=()
|
local -a failed=()
|
||||||
for c in "${TO_DISABLE[@]}"; do
|
for c in "${TO_DISABLE[@]}"; do
|
||||||
if backend_run_component "$c" disable; then feature_record "$c" disable; else failed+=("$c"); fi
|
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
|
[[ -n "${BIOS_LOOKED_UP:-}" ]] && return 0
|
||||||
BIOS_LOOKED_UP=1; BIOS_NEWEST=""
|
BIOS_LOOKED_UP=1; BIOS_NEWEST=""
|
||||||
local tmp desc
|
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
|
[[ -n "$BIOS_REPO" ]] || return 1
|
||||||
make_tmpdir tmp
|
tmp="$(mktemp -d)"
|
||||||
if curl -fsL --max-time 30 "$(valve_repo_url "$BIOS_REPO")/$BIOS_REPO.files" -o "$tmp/files" &&
|
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_repo_url "$BIOS_REPO")/$BIOS_REPO.db" -o "$tmp/db"; then
|
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)"
|
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)"
|
[[ -n "$desc" ]] && BIOS_CAB="$(tar -xOf "$tmp/files" "$desc" | grep -E '^usr/share/fwupd/.*\.cab$' | head -n 1)"
|
||||||
desc="${desc%/files}/desc"
|
desc="${desc%/files}/desc"
|
||||||
BIOS_PKG="$(valve_desc_field "$tmp/db" "$desc" FILENAME)"
|
BIOS_PKG="$(tar -xOf "$tmp/db" "$desc" 2>/dev/null | awk '/^%FILENAME%$/ { getline; print }')"
|
||||||
BIOS_SHA256="$(valve_desc_field "$tmp/db" "$desc" SHA256SUM)"
|
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)"
|
[[ -n "${BIOS_CAB:-}" && -n "$BIOS_PKG" && -n "$BIOS_SHA256" ]] && BIOS_NEWEST="$(basename "$BIOS_CAB" .cab)"
|
||||||
fi
|
fi
|
||||||
rm -rf "$tmp"
|
rm -rf "$tmp"
|
||||||
@@ -143,7 +143,7 @@ bios_prepare() {
|
|||||||
{ err "Installing fwupd failed."; return 1; }
|
{ err "Installing fwupd failed."; return 1; }
|
||||||
rm -rf "$BIOS_STAGE_DIR"; mkdir -p "$BIOS_STAGE_DIR"; chmod 700 "$BIOS_STAGE_DIR"
|
rm -rf "$BIOS_STAGE_DIR"; mkdir -p "$BIOS_STAGE_DIR"; chmod 700 "$BIOS_STAGE_DIR"
|
||||||
info "Downloading $BIOS_PKG ($BIOS_REPO)..."
|
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 - ||
|
! 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
|
! 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."
|
err "Downloading or verifying $BIOS_PKG failed; the BIOS was not touched."
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ boot_enable() {
|
|||||||
|
|
||||||
boot_disable() {
|
boot_disable() {
|
||||||
info "Setting the PC to boot into gaming mode again..."
|
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 rm -f "$BOOT_UNIT"
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
# Next boot into gamescope, as the conversion sets it up.
|
# Next boot into gamescope, as the conversion sets it up.
|
||||||
|
|||||||
+36
-20
@@ -39,10 +39,10 @@ cec_link_steamos_manager() {
|
|||||||
# this item ran.
|
# this item ran.
|
||||||
pacman -Q steamos-manager >/dev/null 2>&1 || return 0
|
pacman -Q steamos-manager >/dev/null 2>&1 || return 0
|
||||||
user_systemctl daemon-reload
|
user_systemctl daemon-reload
|
||||||
best_effort user_systemctl enable steamos-manager-configure-cecd.service
|
user_systemctl enable steamos-manager-configure-cecd.service 2>/dev/null
|
||||||
best_effort user_systemctl start steamos-manager-configure-cecd.service
|
user_systemctl start steamos-manager-configure-cecd.service 2>/dev/null
|
||||||
best_effort user_systemctl restart cecd.service
|
user_systemctl restart cecd.service 2>/dev/null
|
||||||
best_effort user_systemctl restart steamos-manager.service
|
user_systemctl restart steamos-manager.service 2>/dev/null
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,10 +55,10 @@ cec_status() { cec_installed && [[ -f "$CEC_STEAM_DROPIN" ]] && cec_driver_ok; }
|
|||||||
|
|
||||||
cec_reload_driver() {
|
cec_reload_driver() {
|
||||||
# cecd keeps /dev/cec0 open, so the old module can't be unloaded under it.
|
# cecd keeps /dev/cec0 open, so the old module can't be unloaded under it.
|
||||||
best_effort user_systemctl stop cecd.service
|
user_systemctl stop cecd.service 2>/dev/null
|
||||||
best_effort sudo modprobe -r cros_ec_cec
|
sudo modprobe -r cros_ec_cec 2>/dev/null
|
||||||
best_effort sudo modprobe cros_ec_cec
|
sudo modprobe cros_ec_cec 2>/dev/null
|
||||||
best_effort user_systemctl start cecd.service
|
user_systemctl start cecd.service 2>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
cec_driver_enable() {
|
cec_driver_enable() {
|
||||||
@@ -68,7 +68,7 @@ cec_driver_enable() {
|
|||||||
sudo pacman -S --needed --noconfirm dkms patch || { err "Installing dkms failed."; return 1; }
|
sudo pacman -S --needed --noconfirm dkms patch || { err "Installing dkms failed."; return 1; }
|
||||||
fi
|
fi
|
||||||
install_kernel_headers || return 1
|
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
|
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"
|
cp "$CEC_DRIVER_CACHE" "$tmp/cros-ec-cec.c"
|
||||||
else
|
else
|
||||||
@@ -80,7 +80,7 @@ cec_driver_enable() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
# The name carries the checksum: a newer pin gets its own file, the old one goes.
|
# 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"
|
sudo install -Dm644 "$tmp/cros-ec-cec.c" "$CEC_DRIVER_CACHE"
|
||||||
fi
|
fi
|
||||||
# So it finds amdgpu's HDMI port (see the patch for why).
|
# 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() {
|
||||||
# fetch_holo_pkg <dir> <package>: download the newest <package> from
|
# fetch_holo_pkg <dir> <package>: download the newest <package> from
|
||||||
# Valve's newest holo repository into <dir> and print its path.
|
# Valve's newest holo repository into <dir> and print its path. The
|
||||||
valve_fetch_pkg holo "$2" "$1"
|
# 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() {
|
cec_enable() {
|
||||||
@@ -124,7 +140,7 @@ cec_enable() {
|
|||||||
sudo pacman -S --needed --noconfirm linuxconsole || { err "Installing linuxconsole failed."; return 1; }
|
sudo pacman -S --needed --noconfirm linuxconsole || { err "Installing linuxconsole failed."; return 1; }
|
||||||
state_set cec installed_linuxconsole 1
|
state_set cec installed_linuxconsole 1
|
||||||
fi
|
fi
|
||||||
make_tmpdir tmp
|
tmp="$(mktemp -d)"
|
||||||
for p in "${CEC_PKGS[@]}"; do
|
for p in "${CEC_PKGS[@]}"; do
|
||||||
f="$(fetch_holo_pkg "$tmp" "$p")" || { rm -rf "$tmp"; return 1; }
|
f="$(fetch_holo_pkg "$tmp" "$p")" || { rm -rf "$tmp"; return 1; }
|
||||||
files+=("$f")
|
files+=("$f")
|
||||||
@@ -152,10 +168,10 @@ cec_enable() {
|
|||||||
user_systemctl daemon-reload
|
user_systemctl daemon-reload
|
||||||
# The TV remote's volume keys: SteamOS enables this socket through its
|
# The TV remote's volume keys: SteamOS enables this socket through its
|
||||||
# preset, which pacman doesn't apply, so nothing else does on CachyOS.
|
# 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
|
cec_link_steamos_manager
|
||||||
if compgen -G "/dev/cec*" >/dev/null; then
|
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."
|
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)."
|
info "Turn on CEC on your TV too (e.g. Sony: BRAVIA Sync, Samsung: Anynet+, LG: SimpLink)."
|
||||||
else
|
else
|
||||||
@@ -166,15 +182,15 @@ cec_enable() {
|
|||||||
|
|
||||||
cec_disable() {
|
cec_disable() {
|
||||||
info "Removing HDMI-CEC..."
|
info "Removing HDMI-CEC..."
|
||||||
best_effort user_systemctl disable --now cecd.service cec-audio-control.socket cec-audio-control.service
|
user_systemctl disable --now cecd.service cec-audio-control.socket cec-audio-control.service 2>/dev/null
|
||||||
best_effort user_systemctl disable steamos-manager-configure-cecd.service
|
user_systemctl disable steamos-manager-configure-cecd.service 2>/dev/null
|
||||||
best_effort sudo pacman -Rns --noconfirm "${CEC_PKGS[@]}"
|
sudo pacman -Rns --noconfirm "${CEC_PKGS[@]}" 2>/dev/null
|
||||||
cec_driver_disable
|
cec_driver_disable
|
||||||
sudo rm -f "$CEC_ORDER_DROPIN" "$CEC_STEAM_DROPIN" "$CEC_STEAM_ENV"
|
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
|
user_systemctl daemon-reload
|
||||||
if [[ -n "$(state_get cec installed_linuxconsole)" ]]; then
|
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
|
state_clear cec
|
||||||
fi
|
fi
|
||||||
sudo udevadm control --reload
|
sudo udevadm control --reload
|
||||||
|
|||||||
+3
-41
@@ -2,12 +2,7 @@
|
|||||||
# Output helpers, prompts and small shared utilities.
|
# Output helpers, prompts and small shared utilities.
|
||||||
# Sourced by steamify.sh; not meant to be run on its own.
|
# 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.
|
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"
|
||||||
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
|
|
||||||
|
|
||||||
info() { echo -e "${c_cyan}[INFO]${c_reset} $*"; }
|
info() { echo -e "${c_cyan}[INFO]${c_reset} $*"; }
|
||||||
ok() { echo -e "${c_green}[OK]${c_reset} $*"; }
|
ok() { echo -e "${c_green}[OK]${c_reset} $*"; }
|
||||||
@@ -39,39 +34,6 @@ ask_yn() {
|
|||||||
[[ "$reply" =~ ^[Yy]$ ]]
|
[[ "$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() {
|
require_root_helper() {
|
||||||
# Re-exec any privileged step through sudo rather than requiring the
|
# Re-exec any privileged step through sudo rather than requiring the
|
||||||
# whole script to run as root, so $HOME/whoami stay correct for 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
|
PLASMASHELL_WAS_RUNNING=false
|
||||||
if pgrep -u "$USER" -x plasmashell >/dev/null; then
|
if pgrep -u "$USER" -x plasmashell >/dev/null; then
|
||||||
PLASMASHELL_WAS_RUNNING=true
|
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
|
pkill -u "$USER" -x plasmashell && sleep 2
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -143,7 +105,7 @@ stop_plasmashell_for_edit() {
|
|||||||
restart_plasmashell_if_stopped() {
|
restart_plasmashell_if_stopped() {
|
||||||
if [[ "${PLASMASHELL_WAS_RUNNING:-false}" == true ]]; then
|
if [[ "${PLASMASHELL_WAS_RUNNING:-false}" == true ]]; then
|
||||||
rm -rf ~/.cache/plasmashell* ~/.cache/org.kde.dirmodel-qml.kcache
|
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 & }
|
{ setsid plasmashell >/dev/null 2>&1 & }
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
+29
-30
@@ -2,46 +2,41 @@
|
|||||||
# "Return to Gaming Mode" desktop shortcut and the sudoers rule it needs.
|
# "Return to Gaming Mode" desktop shortcut and the sudoers rule it needs.
|
||||||
# Sourced by steamify.sh; not meant to be run on its own.
|
# Sourced by steamify.sh; not meant to be run on its own.
|
||||||
|
|
||||||
shortcut_desktop_dir() {
|
create_desktop_shortcut() {
|
||||||
# The target user's Desktop folder, from their user-dirs.dirs.
|
# Determine the home directory of the target user
|
||||||
local user_home desktop_dir
|
local user_home
|
||||||
user_home="$(getent passwd "$TARGET_USER" | cut -d: -f6)"
|
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
|
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=$(grep '^XDG_DESKTOP_DIR=' "$user_home/.config/user-dirs.dirs" | cut -d '"' -f 2)
|
||||||
desktop_dir="${desktop_dir/\$HOME/$user_home}"
|
desktop_dir="${desktop_dir/\$HOME/$user_home}"
|
||||||
fi
|
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"
|
mkdir -p "$desktop_dir"
|
||||||
|
|
||||||
# On SDDM, steamos-session-select logging out is enough: Relogin=true
|
# On SDDM, steamos-session-select logging out is enough: Relogin=true
|
||||||
# logs straight back in to the newly selected session, like SteamOS.
|
# 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"
|
local exec_line="steamos-session-select gamescope"
|
||||||
if [[ "$LOGIN_MANAGER" == "plasmalogin" ]]; then
|
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'"
|
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
|
else
|
||||||
sudo rm -f /etc/sudoers.d/gamescope-session-switch
|
sudo rm -f /etc/sudoers.d/gamescope-session-switch
|
||||||
fi
|
fi
|
||||||
@@ -54,11 +49,11 @@ create_desktop_shortcut() {
|
|||||||
[[ -f /usr/local/share/icons/hicolor/scalable/actions/gaming-return.svg ]] &&
|
[[ -f /usr/local/share/icons/hicolor/scalable/actions/gaming-return.svg ]] &&
|
||||||
shortcut_icon="gaming-return"
|
shortcut_icon="gaming-return"
|
||||||
|
|
||||||
|
# 2. Generate the .desktop shortcut with instant session switcher strings
|
||||||
local shortcut_path="$desktop_dir/Return to Gaming Mode.desktop"
|
local shortcut_path="$desktop_dir/Return to Gaming Mode.desktop"
|
||||||
info "Creating 'Return to Gaming Mode' desktop shortcut at: $shortcut_path"
|
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.
|
cat << EOF > "$shortcut_path"
|
||||||
install_executable "$shortcut_path" 755 << EOF
|
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Name=Return to Gaming Mode
|
Name=Return to Gaming Mode
|
||||||
Comment=Switch session back to Gamescope
|
Comment=Switch session back to Gamescope
|
||||||
@@ -68,6 +63,10 @@ Terminal=false
|
|||||||
Type=Application
|
Type=Application
|
||||||
Categories=System;
|
Categories=System;
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# Fix permissions for the target user and KDE Plasma desktop ecosystem
|
||||||
|
chmod +x "$shortcut_path"
|
||||||
|
|
||||||
ok "Desktop shortcut created successfully."
|
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_ENTRY="${XDG_CONFIG_HOME:-$HOME/.config}/autostart/steamify-first-login.desktop"
|
||||||
FIRST_LOGIN_SCRIPT="$STEAMIFY_BIN/first-login"
|
FIRST_LOGIN_SCRIPT="$STEAMIFY_BIN/first-login"
|
||||||
FIRST_LOGIN_BUNDLE="$STEAMIFY_BIN/first-login-steamify.sh"
|
|
||||||
|
|
||||||
first_login_schedule() {
|
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
|
install_executable "$FIRST_LOGIN_SCRIPT" 755 << EOF2
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Installed by Steamify CachyOS: runs once, at the first desktop login.
|
# Installed by Steamify CachyOS: runs once, at the first desktop login.
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
mkdir -p ~/.cache
|
curl -fsSL --max-time 30 "$WIZARD_URL" | bash -s -- --first-login
|
||||||
{ $run; } > ~/.cache/steamify-first-login.log 2>&1
|
|
||||||
rm -f "$FIRST_LOGIN_ENTRY" "\$0"
|
rm -f "$FIRST_LOGIN_ENTRY" "\$0"
|
||||||
[[ -x "$WIZARD_APP_LAUNCHER" ]] && exec "$WIZARD_APP_LAUNCHER"
|
[[ -x "$WIZARD_APP_LAUNCHER" ]] && exec "$WIZARD_APP_LAUNCHER"
|
||||||
EOF2
|
EOF2
|
||||||
@@ -43,9 +31,10 @@ first_login_run() {
|
|||||||
[[ -n "$(plasma_applets org.kde.plasma.kickoff)" ]] && break
|
[[ -n "$(plasma_applets org.kde.plasma.kickoff)" ]] && break
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
stop_plasmashell_for_edit
|
if single_status; then
|
||||||
taskbar_drop_discover
|
stop_plasmashell_for_edit
|
||||||
single_status && single_launcher
|
single_launcher
|
||||||
restart_plasmashell_if_stopped
|
restart_plasmashell_if_stopped
|
||||||
|
fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ poweroff_fix_enable() {
|
|||||||
sudo pacman -S --needed --noconfirm dkms || { err "Installing dkms failed."; return 1; }
|
sudo pacman -S --needed --noconfirm dkms || { err "Installing dkms failed."; return 1; }
|
||||||
fi
|
fi
|
||||||
install_kernel_headers || return 1
|
install_kernel_headers || return 1
|
||||||
make_tmpdir tmp
|
tmp="$(mktemp -d)"
|
||||||
patch_file "$POWEROFF_DKMS_NAME.c" > "$tmp/$POWEROFF_DKMS_NAME.c"
|
patch_file "$POWEROFF_DKMS_NAME.c" > "$tmp/$POWEROFF_DKMS_NAME.c"
|
||||||
echo "obj-m += $POWEROFF_DKMS_NAME.o" >"$tmp/Makefile"
|
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"
|
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."
|
warn "Building the power-off fix for $k failed; with that kernel it may start again after shutting down."
|
||||||
done
|
done
|
||||||
echo "$POWEROFF_DKMS_NAME" | sudo tee "$POWEROFF_MODULES_LOAD" >/dev/null
|
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
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
poweroff_fix_disable() {
|
poweroff_fix_disable() {
|
||||||
sudo rm -f "$POWEROFF_MODULES_LOAD"
|
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
|
[[ -d "$POWEROFF_DKMS_SRC" ]] || return 0
|
||||||
sudo dkms remove "$POWEROFF_DKMS_NAME/$POWEROFF_DKMS_VER" --all >/dev/null 2>&1
|
sudo dkms remove "$POWEROFF_DKMS_NAME/$POWEROFF_DKMS_VER" --all >/dev/null 2>&1
|
||||||
sudo rm -rf "$POWEROFF_DKMS_SRC"
|
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
|
# capitals: steamos-3.9). Offline (e.g. in the installer): the one set
|
||||||
# before, if any.
|
# before, if any.
|
||||||
local v
|
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-}"
|
if [[ -n "$v" ]]; then echo "steamos-${v#jupiter-}"
|
||||||
else sed -n 's/^VERSION_ID=\(steamos-.*\)$/\1/p' /etc/os-release 2>/dev/null; fi
|
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.
|
# The plasmalogin sync bridge from an earlier run isn't needed on SDDM.
|
||||||
if [[ -f /etc/systemd/system/sync-steamos-session.path ]]; then
|
if [[ -f /etc/systemd/system/sync-steamos-session.path ]]; then
|
||||||
info "Removing the plasma-login-manager sync bridge (not needed with SDDM)..."
|
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 \
|
sudo rm -f /etc/systemd/system/sync-steamos-session.path \
|
||||||
/etc/systemd/system/sync-steamos-session.service \
|
/etc/systemd/system/sync-steamos-session.service \
|
||||||
/usr/local/bin/sync-steamos-session.sh
|
/usr/local/bin/sync-steamos-session.sh
|
||||||
|
|||||||
+45
-83
@@ -5,33 +5,19 @@
|
|||||||
|
|
||||||
# Menu order. Components are turned on in this order and off in reverse;
|
# Menu order. Components are turned on in this order and off in reverse;
|
||||||
# gaming must come first (single user builds on it).
|
# gaming must come first (single user builds on it).
|
||||||
COMPONENTS=(gaming boot silent glyphs nvidia bigpicture nvsilent theme single launcher steamgame notify extended_controller_support vram cec machine poweroff kpin hdmi bios)
|
COMPONENTS=(gaming boot nvidia bigpicture 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
|
# One-off actions rather than on/off components: never preselected, never
|
||||||
# re-applied, not listed as on or off.
|
# re-applied, not listed as on or off.
|
||||||
ACTIONS=(bios)
|
ACTIONS=(bios)
|
||||||
# Sub-options, shown indented under their parent and only while it's ticked.
|
# 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)
|
declare -A PARENT=([boot]=gaming [bigpicture]=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)
|
|
||||||
# Never preselected on a first run: booting into the desktop is a choice,
|
# 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
|
# 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,
|
# 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
|
# 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
|
# screen to confirm each step. Extended controller support builds kernel modules for
|
||||||
# every installed kernel: opt-in.
|
# every installed kernel: opt-in.
|
||||||
NO_PRESELECT=(boot silent nvsilent cec kpin hdmi extended_controller_support)
|
NO_PRESELECT=(boot cec kpin hdmi extended_controller_support)
|
||||||
# Feature versions: the Steamify version in which what a component's enable
|
# 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.
|
# 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
|
# 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.
|
||||||
FEATURE_BASELINE=2.1.0
|
FEATURE_BASELINE=2.1.0
|
||||||
declare -A FEATURE_VERSION=(
|
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 [nvidia]=2.10.0 [bigpicture]=2.10.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
|
[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
|
[kpin]=2.1.0 [hdmi]=2.1.0
|
||||||
)
|
)
|
||||||
@@ -49,8 +35,6 @@ declare -A LABEL=(
|
|||||||
[gaming]="SteamOS conversion: boot into gaming mode, Steam on the desktop"
|
[gaming]="SteamOS conversion: boot into gaming mode, Steam on the desktop"
|
||||||
[boot]="Boot into the desktop instead of gaming mode"
|
[boot]="Boot into the desktop instead of gaming mode"
|
||||||
[nvidia]="Gaming on NVIDIA: Steam on the desktop, started at login"
|
[nvidia]="Gaming on NVIDIA: Steam on the desktop, started at login"
|
||||||
[silent]="Start Steam silently in desktop mode: in the tray at login on the desktop, no window"
|
|
||||||
[nvsilent]="Start Steam silently: in the tray at login, no window (untick: Steam's normal window)"
|
|
||||||
[bigpicture]="Steam starts in Big Picture: the controller-friendly Steam (untick: Steam's normal window)"
|
[bigpicture]="Steam starts in Big Picture: the controller-friendly Steam (untick: Steam's normal window)"
|
||||||
[theme]="Install SteamOS theme: Vapor look (cachyos-vapor)"
|
[theme]="Install SteamOS theme: Vapor look (cachyos-vapor)"
|
||||||
[glyphs]="Install Steam Deck/Machine icons: Deck button icons in gaming mode"
|
[glyphs]="Install Steam Deck/Machine icons: Deck button icons in gaming mode"
|
||||||
@@ -73,9 +57,9 @@ component_available() {
|
|||||||
case "$1" in
|
case "$1" in
|
||||||
# gamescope's own session is broken on NVIDIA: the conversion is replaced by "Gaming on NVIDIA" there (shown
|
# gamescope's own session is broken on NVIDIA: the conversion is replaced by "Gaming on NVIDIA" there (shown
|
||||||
# anyway when it's already on, so it can be turned off).
|
# anyway when it's already on, so it can be turned off).
|
||||||
gaming|boot|silent|glyphs) ! nvidia_present || gaming_status ;;
|
gaming|boot|glyphs) ! nvidia_present || gaming_status ;;
|
||||||
nvidia) nvidia_available ;;
|
nvidia) nvidia_available ;;
|
||||||
bigpicture|nvsilent) bigpicture_available ;;
|
bigpicture) bigpicture_available ;;
|
||||||
machine|poweroff) machine_available ;;
|
machine|poweroff) machine_available ;;
|
||||||
vram) vram_available ;;
|
vram) vram_available ;;
|
||||||
# Steamify in the Steam library is for gaming mode's controller: not needed for Big Picture on the desktop (NVIDIA PCs;
|
# Steamify in the Steam library is for gaming mode's controller: not needed for Big Picture on the desktop (NVIDIA PCs;
|
||||||
@@ -87,8 +71,6 @@ component_available() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
is_wanted() { [[ "${WANTED[$1]:-0}" == 1 ]]; }
|
|
||||||
is_current() { [[ "${CURRENT[$1]:-0}" == 1 ]]; }
|
|
||||||
is_action() { [[ " ${ACTIONS[*]} " == *" $1 "* ]]; }
|
is_action() { [[ " ${ACTIONS[*]} " == *" $1 "* ]]; }
|
||||||
|
|
||||||
boot_mode() {
|
boot_mode() {
|
||||||
@@ -101,8 +83,6 @@ menu_visible() {
|
|||||||
# Shown in the menu. A sub-option ("Boot into", the kernel pin) only
|
# Shown in the menu. A sub-option ("Boot into", the kernel pin) only
|
||||||
# while its parent is ticked.
|
# while its parent is ticked.
|
||||||
component_available "$1" || return 1
|
component_available "$1" || return 1
|
||||||
# Silent start is hidden while Steam starts in Big Picture.
|
|
||||||
[[ "$1" == nvsilent && "${WANTED[bigpicture]:-0}" == 1 ]] && return 1
|
|
||||||
[[ -z "${PARENT[$1]:-}" || "${WANTED[${PARENT[$1]}]:-0}" == 1 ]]
|
[[ -z "${PARENT[$1]:-}" || "${WANTED[${PARENT[$1]}]:-0}" == 1 ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +111,7 @@ feature_outdated() {
|
|||||||
# On, but set up by an older version of that feature (compared as
|
# On, but set up by an older version of that feature (compared as
|
||||||
# versions: 2.10.0 is newer than 2.9.0).
|
# versions: 2.10.0 is newer than 2.9.0).
|
||||||
local have want
|
local have want
|
||||||
is_current "$1" || return 1
|
[[ "${CURRENT[$1]:-0}" == 1 ]] || return 1
|
||||||
have="$(state_get features "$1" "$FEATURE_BASELINE")"
|
have="$(state_get features "$1" "$FEATURE_BASELINE")"
|
||||||
[[ "$have" == off ]] && have="$FEATURE_BASELINE"
|
[[ "$have" == off ]] && have="$FEATURE_BASELINE"
|
||||||
want="${FEATURE_VERSION[$1]:-$FEATURE_BASELINE}"
|
want="${FEATURE_VERSION[$1]:-$FEATURE_BASELINE}"
|
||||||
@@ -170,8 +150,6 @@ feature_new() {
|
|||||||
feature_added "$1" && [[ " ${NO_PRESELECT[*]} " != *" $1 "* ]]
|
feature_added "$1" && [[ " ${NO_PRESELECT[*]} " != *" $1 "* ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
feature_any_new() { feature_new "$1" || feature_new_optin "$1"; }
|
|
||||||
|
|
||||||
feature_new_optin() {
|
feature_new_optin() {
|
||||||
# A new opt-in option (unticked by default): the "new" badge only, it stays
|
# 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.
|
# unticked. Boot into is a choice row of the conversion, not an item.
|
||||||
@@ -270,14 +248,14 @@ defaults_options() {
|
|||||||
warn "Leaving out ${LABEL[$c]%%:*}: not available on this PC."
|
warn "Leaving out ${LABEL[$c]%%:*}: not available on this PC."
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
is_wanted single && component_available gaming && WANTED[gaming]=1
|
[[ "${WANTED[single]}" == 1 ]] && component_available gaming && WANTED[gaming]=1
|
||||||
for c in "${COMPONENTS[@]}"; do
|
for c in "${COMPONENTS[@]}"; do
|
||||||
[[ "${WANTED[$c]}" == 1 && -n "${PARENT[$c]:-}" ]] && WANTED[${PARENT[$c]}]=1
|
[[ "${WANTED[$c]}" == 1 && -n "${PARENT[$c]:-}" ]] && WANTED[${PARENT[$c]}]=1
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
case "$boot" in
|
case "$boot" in
|
||||||
desktop)
|
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 ;;
|
WANTED[boot]=1 ;;
|
||||||
gamescope) WANTED[boot]=0 ;;
|
gamescope) WANTED[boot]=0 ;;
|
||||||
esac
|
esac
|
||||||
@@ -302,53 +280,36 @@ defaults_list() {
|
|||||||
on=false
|
on=false
|
||||||
[[ "$sel" == true && " ${NO_PRESELECT[*]} " != *" $c "* ]] && on=true
|
[[ "$sel" == true && " ${NO_PRESELECT[*]} " != *" $c "* ]] && on=true
|
||||||
[[ "$c" == cec ]] && machine_available && 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]:-}"),\"on\":$on,\"selectable\":$sel}"
|
||||||
done
|
done
|
||||||
printf '[%s]\n' "$items"
|
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() {
|
toggle_component() {
|
||||||
local c="$1" p x
|
local c="$1"
|
||||||
component_selectable "$c" || return 1
|
component_selectable "$c" || return 1
|
||||||
WANTED[$c]=$(( 1 - WANTED[$c] ))
|
WANTED[$c]=$(( 1 - WANTED[$c] ))
|
||||||
if is_wanted "$c"; then
|
# Single user mode only makes sense on top of the SteamOS conversion.
|
||||||
p="${REQUIRES[$c]:-}"
|
# (On NVIDIA the conversion isn't offered: single user mode logs in by itself, see lib/single-user.sh.)
|
||||||
[[ -n "$p" ]] && component_available "$p" && WANTED[$p]=1
|
if [[ "$c" == single && "${WANTED[single]}" == 1 ]] && component_available gaming; then WANTED[gaming]=1; fi
|
||||||
x="${EXCLUDES[$c]:-}"
|
if [[ "$c" == gaming && "${WANTED[gaming]}" == 0 ]]; then WANTED[single]=0; WANTED[boot]=0; fi
|
||||||
[[ -n "$x" ]] && WANTED[$x]=0
|
# Where to boot to is part of the conversion, too.
|
||||||
else
|
if [[ "$c" == boot && "${WANTED[boot]}" == 1 ]]; then WANTED[gaming]=1; fi
|
||||||
for x in $(children_of "$c"); do WANTED[$x]=0; done
|
# Big Picture is opt-out: ticked along with "Gaming on NVIDIA".
|
||||||
fi
|
if [[ "$c" == nvidia ]]; then WANTED[bigpicture]=${WANTED[nvidia]}; fi
|
||||||
x="${FOLLOWS[$c]:-}"
|
if [[ "$c" == bigpicture && "${WANTED[bigpicture]}" == 1 ]]; then WANTED[nvidia]=1; fi
|
||||||
[[ -n "$x" ]] && component_available "$x" && WANTED[$x]=${WANTED[$c]}
|
# 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
|
||||||
normalize_wanted() {
|
if [[ "$c" == machine && "${WANTED[machine]}" == 0 ]]; then WANTED[kpin]=0; WANTED[bios]=0; fi
|
||||||
# The same rules for a whole selection (the app ticks exactly the items it
|
if [[ "$c" == kpin && "${WANTED[kpin]}" == 1 ]]; then WANTED[machine]=1; fi
|
||||||
# sends): a ticked sub-option brings its parent, two that exclude each other
|
if [[ "$c" == bios && "${WANTED[bios]}" == 1 ]]; then WANTED[machine]=1; fi
|
||||||
# keep the first, and an unticked parent takes its sub-options with it
|
# Steam starts Steamify through the shortcut's start script.
|
||||||
# (only where the parent is offered: single user mode stays on NVIDIA PCs).
|
# Opt-out, like the power-off fix: ticked along with the shortcut.
|
||||||
local c p x
|
if [[ "$c" == launcher ]] && component_available steamgame; then WANTED[steamgame]=${WANTED[launcher]}; fi
|
||||||
for c in "${COMPONENTS[@]}"; do
|
if [[ "$c" == steamgame && "${WANTED[steamgame]}" == 1 ]]; then WANTED[launcher]=1; fi
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
show_menu() {
|
show_menu() {
|
||||||
@@ -360,16 +321,16 @@ show_menu() {
|
|||||||
menu_visible "$c" || continue
|
menu_visible "$c" || continue
|
||||||
i=$((i + 1)); MENU_ITEMS[$i]=$c
|
i=$((i + 1)); MENU_ITEMS[$i]=$c
|
||||||
# Pad the plain word, then colour it: colour codes would count as width.
|
# Pad the plain word, then colour it: colour codes would count as width.
|
||||||
now="off"; is_current "$c" && now="on"
|
now="off"; [[ "${CURRENT[$c]}" == 1 ]] && now="on"
|
||||||
is_action "$c" && now="-"
|
is_action "$c" && now="-"
|
||||||
now="$(printf '%-6s' "$now")"
|
now="$(printf '%-6s' "$now")"
|
||||||
is_current "$c" && now="${now/on/${c_green}on${c_reset}}"
|
[[ "${CURRENT[$c]}" == 1 ]] && now="${now/on/${c_green}on${c_reset}}"
|
||||||
want="[ ]"; is_wanted "$c" && want="[x]"
|
want="[ ]"; [[ "${WANTED[$c]}" == 1 ]] && want="[x]"
|
||||||
if [[ "$c" == boot ]]; then
|
if [[ "$c" == boot ]]; then
|
||||||
# A choice rather than a checkbox: Now/Want show the mode.
|
# A choice rather than a checkbox: Now/Want show the mode.
|
||||||
now="gaming"; is_current boot && now="desk"
|
now="gaming"; [[ "${CURRENT[boot]}" == 1 ]] && now="desk"
|
||||||
is_current gaming || now="-"
|
[[ "${CURRENT[gaming]}" == 1 ]] || now="-"
|
||||||
want="gaming"; is_wanted boot && want="desk"
|
want="gaming"; [[ "${WANTED[boot]}" == 1 ]] && want="desk"
|
||||||
printf " %-3s %-6s %-6s └ %s\n" "$i" "$now" "$want" "$(boot_choice "${WANTED[boot]}")"
|
printf " %-3s %-6s %-6s └ %s\n" "$i" "$now" "$want" "$(boot_choice "${WANTED[boot]}")"
|
||||||
elif ! component_selectable "$c"; then
|
elif ! component_selectable "$c"; then
|
||||||
local tree=""; [[ -n "${PARENT[$c]:-}" ]] && tree=" └ "
|
local tree=""; [[ -n "${PARENT[$c]:-}" ]] && tree=" └ "
|
||||||
@@ -408,8 +369,8 @@ draw_menu_tui() {
|
|||||||
for c in "${COMPONENTS[@]}"; do
|
for c in "${COMPONENTS[@]}"; do
|
||||||
menu_visible "$c" || continue
|
menu_visible "$c" || continue
|
||||||
MENU_ITEMS[$i]=$c
|
MENU_ITEMS[$i]=$c
|
||||||
box="[ ]"; is_wanted "$c" && box="[${c_green}x${c_reset}]"
|
box="[ ]"; [[ "${WANTED[$c]}" == 1 ]] && box="[${c_green}x${c_reset}]"
|
||||||
state=" (now: off)"; is_current "$c" && state=" (now: ${c_green}on${c_reset})"
|
state=" (now: off)"; [[ "${CURRENT[$c]}" == 1 ]] && state=" (now: ${c_green}on${c_reset})"
|
||||||
is_action "$c" && state=" (opt-in, runs once)"
|
is_action "$c" && state=" (opt-in, runs once)"
|
||||||
line="$box ${LABEL[$c]}"
|
line="$box ${LABEL[$c]}"
|
||||||
if [[ "$c" == boot ]]; then
|
if [[ "$c" == boot ]]; then
|
||||||
@@ -417,9 +378,9 @@ draw_menu_tui() {
|
|||||||
# Indented under the conversion, whose sub-option it is.
|
# Indented under the conversion, whose sub-option it is.
|
||||||
line="$(boot_choice "${WANTED[boot]}")"
|
line="$(boot_choice "${WANTED[boot]}")"
|
||||||
line=" └ ${line/\[/[${c_green}}"; line="${line/\]/${c_reset}]}"
|
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)"
|
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
|
elif [[ -n "${PARENT[$c]:-}" ]]; then
|
||||||
line=" └ $line"
|
line=" └ $line"
|
||||||
fi
|
fi
|
||||||
@@ -446,6 +407,7 @@ draw_menu_tui() {
|
|||||||
run_menu_tui() {
|
run_menu_tui() {
|
||||||
local cursor=0 key rest count
|
local cursor=0 key rest count
|
||||||
tput civis 2>/dev/null
|
tput civis 2>/dev/null
|
||||||
|
trap 'tput cnorm 2>/dev/null' EXIT
|
||||||
while true; do
|
while true; do
|
||||||
draw_menu_tui "$cursor"
|
draw_menu_tui "$cursor"
|
||||||
count=${#MENU_ITEMS[@]}
|
count=${#MENU_ITEMS[@]}
|
||||||
@@ -507,7 +469,7 @@ plan_changes() {
|
|||||||
done
|
done
|
||||||
for c in "${COMPONENTS[@]}"; do
|
for c in "${COMPONENTS[@]}"; do
|
||||||
component_available "$c" || continue
|
component_available "$c" || continue
|
||||||
is_wanted "$c" || continue
|
[[ "${WANTED[$c]}" == 1 ]] || continue
|
||||||
if is_action "$c"; then TO_ENABLE+=("$c"); continue; fi
|
if is_action "$c"; then TO_ENABLE+=("$c"); continue; fi
|
||||||
if [[ "${CURRENT[$c]}" == 0 || "$REAPPLY" == true ]] || feature_outdated "$c" ||
|
if [[ "${CURRENT[$c]}" == 0 || "$REAPPLY" == true ]] || feature_outdated "$c" ||
|
||||||
[[ "$c" == gaming && "${CURRENT[single]}" != "${WANTED[single]}" ]]; then
|
[[ "$c" == gaming && "${CURRENT[single]}" != "${WANTED[single]}" ]]; then
|
||||||
@@ -519,7 +481,7 @@ plan_changes() {
|
|||||||
apply_changes() {
|
apply_changes() {
|
||||||
local c failed=()
|
local c failed=()
|
||||||
LOGIN_MANAGER=plasmalogin
|
LOGIN_MANAGER=plasmalogin
|
||||||
is_wanted single && LOGIN_MANAGER=sddm
|
[[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm
|
||||||
|
|
||||||
for c in "${TO_DISABLE[@]}"; do
|
for c in "${TO_DISABLE[@]}"; do
|
||||||
if [[ "$c" == boot ]]; then echo; echo -e "${c_bold}Boot into: gamescope${c_reset}"
|
if [[ "$c" == boot ]]; then echo; echo -e "${c_bold}Boot into: gamescope${c_reset}"
|
||||||
|
|||||||
+1
-19
@@ -57,8 +57,7 @@ nvidia_enable() {
|
|||||||
state_set nvidia installed_pkgs steam
|
state_set nvidia installed_pkgs steam
|
||||||
fi
|
fi
|
||||||
# Keeps what the Big Picture option set up; a first run starts Steam normally.
|
# Keeps what the Big Picture option set up; a first run starts Steam normally.
|
||||||
local unit_args=""
|
local unit_args=""; bigpicture_status && unit_args=-gamepadui
|
||||||
if bigpicture_status; then unit_args=-gamepadui; elif nvsilent_status; then unit_args=-silent; fi
|
|
||||||
nvidia_write_unit "$unit_args" || return 1
|
nvidia_write_unit "$unit_args" || return 1
|
||||||
user_systemctl enable "$NVIDIA_UNIT" >/dev/null 2>&1 || { err "Enabling $NVIDIA_UNIT failed."; return 1; }
|
user_systemctl enable "$NVIDIA_UNIT" >/dev/null 2>&1 || { err "Enabling $NVIDIA_UNIT failed."; return 1; }
|
||||||
nvidia_kernel_enable || return 1
|
nvidia_kernel_enable || return 1
|
||||||
@@ -97,23 +96,6 @@ bigpicture_disable() {
|
|||||||
nvidia_write_unit "" && ok "Steam starts in its normal window at your next login."
|
nvidia_write_unit "" && ok "Steam starts in its normal window at your next login."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Sub-option: Steam starts silently (tray only, no window). Big Picture and
|
|
||||||
# a silent start exclude each other (the menus untick one when the other is
|
|
||||||
# ticked); a first run with both gets Big Picture.
|
|
||||||
nvsilent_status() { grep -qs -- '-silent' "$(nvidia_unit_file)"; }
|
|
||||||
|
|
||||||
nvsilent_enable() {
|
|
||||||
[[ -f "$(nvidia_unit_file)" ]] || return 0
|
|
||||||
bigpicture_status && return 0
|
|
||||||
nvidia_write_unit -silent || return 1
|
|
||||||
ok "Steam starts silently at your next login."
|
|
||||||
}
|
|
||||||
|
|
||||||
nvsilent_disable() {
|
|
||||||
nvsilent_status || return 0
|
|
||||||
nvidia_write_unit "" && ok "Steam starts in its normal window at your next login."
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Kernel parameters and early modules (a supported card only)
|
# --- Kernel parameters and early modules (a supported card only)
|
||||||
|
|
||||||
nvidia_supported() {
|
nvidia_supported() {
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ bootstrap_yay() {
|
|||||||
sudo pacman -S --needed --noconfirm base-devel git || return 1
|
sudo pacman -S --needed --noconfirm base-devel git || return 1
|
||||||
|
|
||||||
local tmp_dir
|
local tmp_dir
|
||||||
make_tmpdir tmp_dir
|
tmp_dir="$(mktemp -d)"
|
||||||
info "Cloning yay-bin from the AUR..."
|
info "Cloning yay-bin from the AUR..."
|
||||||
if ! git clone --depth 1 https://aur.archlinux.org/yay-bin.git "$tmp_dir/yay-bin"; then
|
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."
|
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."
|
ok "Single user mode's wallet (no password) is back; your own is kept as kdewallet.kwl$WALLET_BACKUP."
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
make_tmpdir tmp
|
tmp="$(mktemp -d)"
|
||||||
if ! fetch_valve_presets "$tmp"; then
|
if ! fetch_valve_presets "$tmp"; then
|
||||||
rm -rf "$tmp"
|
rm -rf "$tmp"
|
||||||
warn "Couldn't get Valve's empty wallet; apps may still ask for the wallet password."
|
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
|
"$(wizard_desktop_dir)/$WIZARD_DESKTOP_NAME"; do
|
||||||
[[ -f "$f" ]] && sed -i "s|$old_bin/|$STEAMIFY_BIN/|g" "$f"
|
[[ -f "$f" ]] && sed -i "s|$old_bin/|$STEAMIFY_BIN/|g" "$f"
|
||||||
done
|
done
|
||||||
best_effort user_systemctl daemon-reload
|
user_systemctl daemon-reload 2>/dev/null
|
||||||
fi
|
fi
|
||||||
if [[ -f "$old_icon" ]]; then
|
if [[ -f "$old_icon" ]]; then
|
||||||
if [[ -e "$WIZARD_ICON" ]]; then rm -f "$old_icon"; else mv "$old_icon" "$WIZARD_ICON"; fi
|
if [[ -e "$WIZARD_ICON" ]]; then rm -f "$old_icon"; else mv "$old_icon" "$WIZARD_ICON"; fi
|
||||||
|
|||||||
+15
-43
@@ -5,63 +5,35 @@
|
|||||||
|
|
||||||
steam_enable() {
|
steam_enable() {
|
||||||
info "Applying non-optional SteamOS environment & keyboard fixes..."
|
info "Applying non-optional SteamOS environment & keyboard fixes..."
|
||||||
local steam_env_dir
|
local steam_env_dir systemd_user_dir
|
||||||
steam_env_dir="$HOME/.config/environment.d"
|
steam_env_dir="$HOME/.config/environment.d"
|
||||||
mkdir -p "$steam_env_dir"
|
mkdir -p "$steam_env_dir"
|
||||||
|
|
||||||
# 2. Prevent KDE Wayland from blocking the virtual keyboard overlay
|
# 2. Prevent KDE Wayland from blocking the virtual keyboard overlay
|
||||||
echo "KWIN_IM_SHOW_ALWAYS=1" > "$steam_env_dir/99-kde-virtual-keyboard.conf"
|
echo "KWIN_IM_SHOW_ALWAYS=1" > "$steam_env_dir/99-kde-virtual-keyboard.conf"
|
||||||
|
|
||||||
ok "Steam UI parameters and keyboard fix deployed successfully."
|
# 3. Setup robust background systemd service for Steam autostart on Desktop Mode
|
||||||
}
|
systemd_user_dir="$HOME/.config/systemd/user"
|
||||||
|
mkdir -p "$systemd_user_dir"
|
||||||
|
|
||||||
# Sub-option of the SteamOS conversion: Steam starts silently (in the tray,
|
service_file steam-desktop-autostart.service > "$systemd_user_dir/steam-desktop-autostart.service"
|
||||||
# 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
|
|
||||||
|
|
||||||
silent_status() {
|
# Clean out old .desktop shortcut so they don't fight
|
||||||
# The old systemd unit counts as on too (until it is migrated by an update of this option).
|
rm -f "$HOME/.config/autostart/steam.desktop"
|
||||||
grep -qs '^Exec=.*-silent' "$STEAM_AUTOSTART" ||
|
|
||||||
[[ -f "$HOME/.config/systemd/user/steam-desktop-autostart.service" ]]
|
|
||||||
}
|
|
||||||
|
|
||||||
silent_old_unit_remove() {
|
# Reload user systemd context and activate the background loop
|
||||||
# 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
|
user_systemctl daemon-reload
|
||||||
}
|
user_systemctl enable --now steam-desktop-autostart.service
|
||||||
|
|
||||||
silent_enable() {
|
ok "Steam UI parameters and keyboard autostart deployed successfully."
|
||||||
silent_old_unit_remove
|
|
||||||
mkdir -p "$(dirname "$STEAM_AUTOSTART")"
|
|
||||||
if [[ -f "$STEAM_SYSTEM_DESKTOP" ]]; then
|
|
||||||
sed 's|^Exec=/usr/bin/steam %U$|Exec=/usr/bin/steam -silent %U|' "$STEAM_SYSTEM_DESKTOP" > "$STEAM_AUTOSTART"
|
|
||||||
fi
|
|
||||||
silent_status || printf '%s\n' '[Desktop Entry]' 'Name=Steam' 'Exec=/usr/bin/steam -silent %U' \
|
|
||||||
'Icon=steam' 'Terminal=false' 'Type=Application' 'Categories=Network;FileTransfer;Game;' > "$STEAM_AUTOSTART"
|
|
||||||
ok "Steam starts silently on the desktop from the next login."
|
|
||||||
}
|
|
||||||
|
|
||||||
silent_disable() {
|
|
||||||
silent_old_unit_remove
|
|
||||||
silent_status && rm -f "$STEAM_AUTOSTART"
|
|
||||||
ok "Steam no longer starts by itself on the desktop."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
steam_disable() {
|
steam_disable() {
|
||||||
info "Removing Steam desktop settings..."
|
info "Removing Steam desktop autostart..."
|
||||||
# The autostart entry is the `silent` option's: removed with it, and here
|
user_systemctl disable --now steam-desktop-autostart.service 2>/dev/null
|
||||||
# too for when the conversion goes off by itself.
|
rm -f "$HOME/.config/systemd/user/steam-desktop-autostart.service" \
|
||||||
silent_status && silent_disable
|
"$HOME/.config/environment.d/99-kde-virtual-keyboard.conf"
|
||||||
silent_old_unit_remove
|
user_systemctl daemon-reload
|
||||||
rm -f "$HOME/.config/environment.d/99-kde-virtual-keyboard.conf"
|
|
||||||
ok "Steam desktop settings removed (takes full effect at next login)."
|
ok "Steam desktop settings removed (takes full effect at next login)."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -56,7 +56,7 @@ steamgame_steam_busy() {
|
|||||||
|
|
||||||
steamgame_edit() {
|
steamgame_edit() {
|
||||||
# steamgame_edit <function>: runs it with Steam closed, then starts Steam
|
# 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
|
local was_running=false i
|
||||||
if pgrep -x steam >/dev/null; then
|
if pgrep -x steam >/dev/null; then
|
||||||
steamgame_steam_busy && return 1
|
steamgame_steam_busy && return 1
|
||||||
@@ -72,7 +72,11 @@ steamgame_edit() {
|
|||||||
fi
|
fi
|
||||||
"$1"; local rc=$?
|
"$1"; local rc=$?
|
||||||
if [[ "$was_running" == true ]]; then
|
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."
|
info "Steam is starting again."
|
||||||
fi
|
fi
|
||||||
return "$rc"
|
return "$rc"
|
||||||
|
|||||||
+194
-48
@@ -15,6 +15,164 @@ detect_valve_fremont() {
|
|||||||
[[ "$vendor" == "OEM" && "$product" == "F7F" ]]
|
[[ "$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() {
|
install_kernel_headers() {
|
||||||
# DKMS can only build leds-valve against kernels whose headers are
|
# DKMS can only build leds-valve against kernels whose headers are
|
||||||
# installed. CachyOS ships several kernels (linux-cachyos, -lts, -bore,
|
# installed. CachyOS ships several kernels (linux-cachyos, -lts, -bore,
|
||||||
@@ -42,7 +200,24 @@ install_kernel_headers() {
|
|||||||
sudo pacman -S --needed --noconfirm "${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
|
# The upstream Makefile builds against `uname -r` instead of the kernel
|
||||||
# DKMS targets, so every other installed kernel (e.g. linux-cachyos-lts)
|
# 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
|
# 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."
|
err "Couldn't write $LED_DKMS_OVERRIDE; without it the LED driver only builds for the running kernel."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
|
||||||
|
|
||||||
led_install_package() {
|
if ! pacman -Qi leds-valve-dkms-git >/dev/null 2>&1; then
|
||||||
# led_install_package <aur helper>
|
info "Installing leds-valve-dkms-git from the AUR via $aur_helper (non-interactive)..."
|
||||||
local aur_helper="$1"
|
if ! "$aur_helper" -S "${flags[@]}" leds-valve-dkms-git; then
|
||||||
local -a flags
|
err "Installing leds-valve-dkms-git failed."
|
||||||
read -ra flags <<< "$(aur_noninteractive_flags "$aur_helper")"
|
return 1
|
||||||
if pacman -Qi leds-valve-dkms-git >/dev/null 2>&1; then
|
fi
|
||||||
|
else
|
||||||
ok "leds-valve-dkms-git already installed."
|
ok "leds-valve-dkms-git already installed."
|
||||||
return 0
|
|
||||||
fi
|
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
|
# Build for every installed kernel that has headers, so booting another
|
||||||
# one (e.g. the LTS kernel) still has the driver.
|
# one (e.g. the LTS kernel) still has the driver.
|
||||||
local kdir kver
|
local kdir kver
|
||||||
@@ -87,9 +257,7 @@ led_build_all_kernels() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
|
||||||
|
|
||||||
led_load_module() {
|
|
||||||
# Load now, and on every boot.
|
# Load now, and on every boot.
|
||||||
echo leds-valve | sudo tee /etc/modules-load.d/leds-valve.conf >/dev/null
|
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 "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"
|
warn " ls /sys/class/leds/ | grep valve ; sudo dmesg | grep -i valve"
|
||||||
fi
|
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"
|
LED_UDEV_RULE="/etc/udev/rules.d/70-valve-leds-user.rules"
|
||||||
@@ -258,17 +407,14 @@ wifi_backend_disable() {
|
|||||||
|
|
||||||
serial_enable() {
|
serial_enable() {
|
||||||
printf '%s\n' "# Written by Steamify: Steam shows the serial number, like on SteamOS." \
|
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; }
|
{ 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() {
|
serial_disable() {
|
||||||
sudo rm -f "$SERIAL_TMPFILES"
|
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
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +447,7 @@ EOF
|
|||||||
wifi_backend_enable || return 1
|
wifi_backend_enable || return 1
|
||||||
sudo systemctl enable --now inputplumber.service
|
sudo systemctl enable --now inputplumber.service
|
||||||
sudo systemctl enable --now steamos-manager.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.
|
# HDMI-CEC ran before this and couldn't link cecd to steamos-manager yet.
|
||||||
cec_status && cec_link_steamos_manager
|
cec_status && cec_link_steamos_manager
|
||||||
|
|
||||||
@@ -318,19 +464,19 @@ EOF
|
|||||||
|
|
||||||
machine_disable() {
|
machine_disable() {
|
||||||
info "Removing Steam Machine support..."
|
info "Removing Steam Machine support..."
|
||||||
best_effort user_systemctl disable --now steamos-manager.service
|
user_systemctl disable --now steamos-manager.service 2>/dev/null
|
||||||
best_effort sudo systemctl disable --now steamos-manager.service
|
sudo systemctl disable --now steamos-manager.service 2>/dev/null
|
||||||
best_effort sudo systemctl disable --now inputplumber.service
|
sudo systemctl disable --now inputplumber.service 2>/dev/null
|
||||||
best_effort sudo pacman -Rns --noconfirm steamos-manager inputplumber
|
sudo pacman -Rns --noconfirm steamos-manager inputplumber 2>/dev/null
|
||||||
serial_disable
|
serial_disable
|
||||||
wifi_backend_disable
|
wifi_backend_disable
|
||||||
sudo rm -f "$LED_UDEV_RULE" /etc/modules-load.d/leds-valve.conf
|
sudo rm -f "$LED_UDEV_RULE" /etc/modules-load.d/leds-valve.conf
|
||||||
sudo udevadm control --reload
|
sudo udevadm control --reload
|
||||||
best_effort sudo modprobe -r leds-valve
|
sudo modprobe -r leds-valve 2>/dev/null
|
||||||
best_effort sudo pacman -Rns --noconfirm leds-valve-dkms-git
|
sudo pacman -Rns --noconfirm leds-valve-dkms-git 2>/dev/null
|
||||||
krevert machine
|
krevert machine
|
||||||
reload_powerdevil
|
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
|
led_dkms_override_remove
|
||||||
sudo rm -f "$HEADERS_UNIT" "$HEADERS_SCRIPT"
|
sudo rm -f "$HEADERS_UNIT" "$HEADERS_SCRIPT"
|
||||||
sudo rm -rf "$(dirname "$HEADERS_SCRIPT")" "$(dirname "$HEADERS_SCRIPT_OLD")"
|
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.
|
# package-owned); per-user settings go through the undo journal.
|
||||||
# Sourced by steamify.sh; not meant to be run on its own.
|
# Sourced by steamify.sh; not meant to be run on its own.
|
||||||
|
|
||||||
|
VALVE_MIRROR="https://steamdeck-packages.steamos.cloud/archlinux-mirror"
|
||||||
NESTED_DIR=/usr/local/share/applications/steam/holo-nested-desktop
|
NESTED_DIR=/usr/local/share/applications/steam/holo-nested-desktop
|
||||||
EXTRAS_FILES=(
|
EXTRAS_FILES=(
|
||||||
/usr/local/bin/holo-add-to-steam
|
/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() {
|
||||||
# fetch_valve_presets <dir>: download and extract the newest
|
# fetch_valve_presets <dir>: download and extract the newest
|
||||||
# steamdeck-kde-presets (from the newest SteamOS release repo, jupiter-3.9,
|
# steamdeck-kde-presets into <dir>. The newest SteamOS release repo
|
||||||
# jupiter-3.10, ...) into <dir>.
|
# (jupiter-3.9, jupiter-3.10, ...) is found on the mirror; its pacman
|
||||||
local dir="$1" pkg
|
# database gives the exact file name and SHA-256, which is verified.
|
||||||
pkg="$(valve_fetch_pkg jupiter steamdeck-kde-presets "$dir")" || return 1
|
local dir="$1" repo desc file_name sha256
|
||||||
tar -I unzstd -xf "$pkg" -C "$dir" usr/bin usr/share ||
|
repo="$(curl -fsL "$VALVE_MIRROR/" | grep -oE 'jupiter-[0-9]+\.[0-9]+/' | tr -d / | sort -V | tail -n 1)"
|
||||||
{ err "Extracting $(basename "$pkg") failed."; return 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() {
|
extras_enable() {
|
||||||
@@ -40,7 +54,7 @@ extras_enable() {
|
|||||||
done
|
done
|
||||||
|
|
||||||
local tmp_dir src
|
local tmp_dir src
|
||||||
make_tmpdir tmp_dir
|
tmp_dir="$(mktemp -d)"
|
||||||
if ! fetch_valve_presets "$tmp_dir"; then
|
if ! fetch_valve_presets "$tmp_dir"; then
|
||||||
rm -rf "$tmp_dir"
|
rm -rf "$tmp_dir"
|
||||||
return 1
|
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" ]]
|
[[ "$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage)" == "$VAPOR_LOOKANDFEEL" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
theme_install_package() {
|
theme_enable() {
|
||||||
pacman -Q cachyos-vapor >/dev/null 2>&1 && return 0
|
if ! pacman -Q cachyos-vapor >/dev/null 2>&1; then
|
||||||
info "Installing cachyos-vapor..."
|
info "Installing cachyos-vapor..."
|
||||||
sudo pacman -S --needed --noconfirm cachyos-vapor || { err "Installing cachyos-vapor failed."; return 1; }
|
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.
|
# Only remove it again on disable if we were the ones installing it.
|
||||||
state_set theme installed_pkg 1
|
state_set theme installed_pkg 1
|
||||||
}
|
fi
|
||||||
|
|
||||||
theme_check_offline() {
|
|
||||||
# Without a session (the ISO's installer), for a user who has never
|
# 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
|
# logged in: the layout still CachyOS's default from /etc/skel is
|
||||||
# removed (after the backup below), and Plasma builds its first layout
|
# removed (after the backup below), and Plasma builds its first layout
|
||||||
# from the look-and-feel package, as --resetLayout does below.
|
# from the look-and-feel package, as --resetLayout does below.
|
||||||
local layout=~/.config/plasma-org.kde.plasma.desktop-appletsrc
|
local offline=false layout=~/.config/plasma-org.kde.plasma.desktop-appletsrc
|
||||||
if [[ -e "$layout" ]] && ! cmp -s "$layout" "/etc/skel/.config/${layout##*/}"; then
|
if [[ -z "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ]]; then
|
||||||
err "Run this from the Plasma desktop: applying the Vapor layout needs a running Plasma session."
|
if [[ -e "$layout" ]] && ! cmp -s "$layout" "/etc/skel/.config/${layout##*/}"; then
|
||||||
return 1
|
err "Run this from the Plasma desktop: applying the Vapor layout needs a running Plasma session."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
offline=true
|
||||||
fi
|
fi
|
||||||
}
|
|
||||||
|
|
||||||
theme_backup_originals() {
|
|
||||||
# Only the first time, so a re-apply doesn't overwrite the originals.
|
# Only the first time, so a re-apply doesn't overwrite the originals.
|
||||||
local backup="$STATE_DIR/theme-layout" f
|
local backup="$STATE_DIR/theme-layout" f
|
||||||
if [[ ! -d "$backup" ]]; then
|
if [[ ! -d "$backup" ]]; then
|
||||||
@@ -85,9 +84,7 @@ theme_backup_originals() {
|
|||||||
[[ -f "$f" ]] && kset theme "$f" Settings gtk-cursor-theme-name \
|
[[ -f "$f" ]] && kset theme "$f" Settings gtk-cursor-theme-name \
|
||||||
"$(kreadconfig6 --file "$f" --group Settings --key gtk-cursor-theme-name)"
|
"$(kreadconfig6 --file "$f" --group Settings --key gtk-cursor-theme-name)"
|
||||||
done
|
done
|
||||||
}
|
|
||||||
|
|
||||||
theme_set_look() {
|
|
||||||
kset theme kdeglobals KDE LookAndFeelPackage "$VAPOR_LOOKANDFEEL"
|
kset theme kdeglobals KDE LookAndFeelPackage "$VAPOR_LOOKANDFEEL"
|
||||||
# Vapor is a dark theme: register it as the dark one, or Plasma's Dark
|
# 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.
|
# 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.
|
# scheme that is already named in the config, without writing its colors.
|
||||||
kset theme kdeglobals General ColorScheme --delete
|
kset theme kdeglobals General ColorScheme --delete
|
||||||
plasma-apply-colorscheme Vapor >/dev/null 2>&1 || true
|
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
|
if [[ "$offline" == true ]]; then
|
||||||
rm -f ~/.config/plasma-org.kde.plasma.desktop-appletsrc ~/.config/plasmashellrc
|
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.
|
# SteamOS panel, launcher icon and wallpaper. It needs plasmashell up.
|
||||||
lookandfeeltool -a "$VAPOR_LOOKANDFEEL" --resetLayout >/dev/null 2>&1 ||
|
lookandfeeltool -a "$VAPOR_LOOKANDFEEL" --resetLayout >/dev/null 2>&1 ||
|
||||||
{ err "lookandfeeltool failed."; return 1; }
|
{ err "lookandfeeltool failed."; return 1; }
|
||||||
# The new layout has a new launcher, without single user's settings, and the default taskbar pins.
|
# The new layout has a new launcher, without single user's settings.
|
||||||
sleep 3
|
if single_status; then
|
||||||
stop_plasmashell_for_edit
|
sleep 3
|
||||||
taskbar_drop_discover
|
stop_plasmashell_for_edit
|
||||||
single_status && single_launcher
|
single_launcher
|
||||||
restart_plasmashell_if_stopped
|
restart_plasmashell_if_stopped
|
||||||
|
fi
|
||||||
extras_enable || warn "SteamOS desktop extras not installed (see above); the theme itself is on."
|
extras_enable || warn "SteamOS desktop extras not installed (see above); the theme itself is on."
|
||||||
ok "Vapor theme active."
|
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() {
|
theme_disable() {
|
||||||
extras_disable
|
extras_disable
|
||||||
info "Restoring the previous desktop look and layout..."
|
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() {
|
launcher_enable() {
|
||||||
info "Adding the Steamify shortcut (the app on the desktop and in the launcher, Steamify Terminal in the launcher)..."
|
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/).
|
install_executable "$WIZARD_LAUNCHER" 755 << EOF
|
||||||
patch_file steamify-start.sh | fill URL="$WIZARD_URL" | install_executable "$WIZARD_LAUNCHER" 755
|
#!/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
|
# 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.
|
# 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.
|
# The icon comes with the release; without it, Steam's own icon.
|
||||||
local icon=steam tmp
|
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-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=...`) |
|
| `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 |
|
| `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):
|
def pending(new):
|
||||||
# Newer than the last Steamify that ran here, and not skipped.
|
# Newer than the last Steamify that ran here, and not skipped.
|
||||||
seen, found = version(state("seen") or "0"), version(new)
|
seen = version(state("seen") or "0")
|
||||||
# A tag that isn't a plain version (a pre-release name) is never offered.
|
return seen is not None and version(new) > seen and new != state("skipped")
|
||||||
return seen is not None and found is not None and found > seen and new != state("skipped")
|
|
||||||
|
|
||||||
|
|
||||||
def desktop():
|
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 |
|
| `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` |
|
| `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) |
|
| `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 |
|
| `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) |
|
| `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
|
set -uo pipefail
|
||||||
|
|
||||||
# Release version, see CHANGELOG.md.
|
# Release version, see CHANGELOG.md.
|
||||||
VERSION=2.11.1
|
VERSION=2.11.0
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
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
|
# shellcheck source=/dev/null
|
||||||
source "$SCRIPT_DIR/lib/$lib.sh"
|
source "$SCRIPT_DIR/lib/$lib.sh"
|
||||||
done
|
done
|
||||||
|
|
||||||
require_root_helper
|
require_root_helper
|
||||||
|
|
||||||
# What this run does, from the first argument:
|
BACKEND=false
|
||||||
# --backend ... the graphical app's machine-readable mode
|
[[ "${1:-}" == --backend ]] && BACKEND=true
|
||||||
# --defaults [--options <id>,...] [--boot gamescope|desktop]: apply what the
|
# --defaults [--options <id>,...] [--boot gamescope|desktop]: apply what the
|
||||||
# menu would preselect (or exactly the listed items), without the menu or
|
# menu would preselect (or exactly the listed items), without the menu or any
|
||||||
# any prompt (the Steam Machine ISO's first login runs this; sudo must not
|
# prompt (the Steam Machine ISO's first login runs this; sudo must not ask).
|
||||||
# ask)
|
DEFAULTS=false
|
||||||
# --defaults --list what --defaults can set up here, as JSON
|
[[ "${1:-}" == --defaults ]] && DEFAULTS=true
|
||||||
# (an installer page is built from it); nothing else on stdout
|
# --defaults --list: what --defaults can set up here, as JSON (an installer
|
||||||
# --boot gamescope|desktop only change where an installed conversion
|
# page is built from it); nothing else on stdout.
|
||||||
# starts, everything else stays as it is (for scripts; no menu)
|
LIST=false
|
||||||
# --first-login the installer's one-time follow-up
|
[[ "$DEFAULTS" == true && "${2:-}" == --list ]] && LIST=true
|
||||||
# (none) the menu
|
# --boot gamescope|desktop: only change where an installed conversion
|
||||||
MODE=menu
|
# starts, everything else stays as it is (for scripts; no menu).
|
||||||
case "${1:-}" in
|
BOOT_ONLY=false
|
||||||
--backend) MODE=backend ;;
|
[[ "${1:-}" == --boot ]] && BOOT_ONLY=true
|
||||||
--defaults) MODE=defaults; [[ "${2:-}" == --list ]] && MODE=list ;;
|
|
||||||
--boot) MODE=boot ;;
|
|
||||||
--first-login) MODE=first-login ;;
|
|
||||||
esac
|
|
||||||
BACKEND=false; [[ "$MODE" == backend ]] && BACKEND=true
|
|
||||||
|
|
||||||
if [[ "$MODE" != backend && "$MODE" != list ]]; then
|
if [[ "$BACKEND" == false && "$LIST" == false ]]; then
|
||||||
echo -e "${c_bold}Steamify CachyOS${c_reset} v$VERSION"
|
echo -e "${c_bold}Steamify CachyOS${c_reset} v$VERSION"
|
||||||
echo "Turn the SteamOS-style parts on or off. The menu shows what is on now;"
|
echo "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."
|
echo "anything you turn off is put back the way it was."
|
||||||
@@ -64,14 +59,6 @@ TARGET_USER="$(id -un)"
|
|||||||
migrate_layout
|
migrate_layout
|
||||||
notify_seen
|
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_needed() { [[ -n "${BIOS_NEEDS_RESTART:-}" || "$RESTART_FOR_LOGIN" == true ]]; }
|
||||||
|
|
||||||
restart_now() {
|
restart_now() {
|
||||||
@@ -131,62 +118,63 @@ quit_prompt() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
report_failed() {
|
# The graphical app (steamify-ui) drives the same components through
|
||||||
# Exit status for the scripted modes: 1 when a component had problems.
|
# lib/backend.sh instead of the menu.
|
||||||
[[ ${#FAILED[@]} -eq 0 ]] || { warn "These had problems (see above): ${FAILED[*]}"; return 1; }
|
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
|
RESTART_FOR_LOGIN=false
|
||||||
REAPPLY=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
|
detect_components
|
||||||
defaults_options "${@:2}" || return 1
|
defaults_options "${@:2}" || exit 1
|
||||||
plan_changes
|
plan_changes
|
||||||
apply_changes
|
apply_changes
|
||||||
# Run by the installer: the rest waits for the first desktop login.
|
# Run by the installer: the rest waits for the first desktop login.
|
||||||
user_session || first_login_schedule
|
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."
|
ok "Done; the changes take effect after a restart."
|
||||||
}
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
run_boot_only() {
|
if [[ "$BOOT_ONLY" == true ]]; then
|
||||||
local mode="${2:-}" c
|
|
||||||
RESTART_FOR_LOGIN=false
|
RESTART_FOR_LOGIN=false
|
||||||
REAPPLY=false
|
REAPPLY=false
|
||||||
case "$mode" in
|
case "${2:-}" in
|
||||||
gamescope|desktop) ;;
|
gamescope|desktop) ;;
|
||||||
*) err "--boot takes gamescope or desktop"; return 1 ;;
|
*) err "--boot takes gamescope or desktop"; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
detect_components
|
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
|
# What is on stays on, and nothing else changes: not the updates or
|
||||||
# removals a normal run would pick.
|
# removals a normal run would pick.
|
||||||
for c in "${COMPONENTS[@]}"; do WANTED[$c]=${CURRENT[$c]:-0}; done
|
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
|
plan_changes
|
||||||
if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then
|
if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then
|
||||||
ok "Already starting in $mode."
|
ok "Already starting in $2."
|
||||||
return 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
apply_changes
|
apply_changes
|
||||||
report_failed || return 1
|
[[ ${#FAILED[@]} -eq 0 ]] || { warn "These had problems (see above): ${FAILED[*]}"; exit 1; }
|
||||||
ok "Starts in $mode from the next boot on."
|
ok "Starts in $2 from the next boot on."
|
||||||
}
|
exit 0
|
||||||
|
fi
|
||||||
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
|
|
||||||
|
|
||||||
# Menu loop: after each run the menu comes back with the new state, until
|
# Menu loop: after each run the menu comes back with the new state, until
|
||||||
# the user quits; the restart question comes then, once, for everything.
|
# the user quits; the restart question comes then, once, for everything.
|
||||||
@@ -217,7 +205,7 @@ while true; do
|
|||||||
if [[ "$c" == boot ]]; then echo " - boot into: desktop (from the next boot)"
|
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 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 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)"
|
elif feature_new "$c"; then echo " - turn on: ${LABEL[$c]} (new in this version)"
|
||||||
else echo " - turn on: ${LABEL[$c]}"; fi
|
else echo " - turn on: ${LABEL[$c]}"; fi
|
||||||
done
|
done
|
||||||
@@ -242,11 +230,11 @@ while true; do
|
|||||||
for c in "${COMPONENTS[@]}"; do
|
for c in "${COMPONENTS[@]}"; do
|
||||||
component_available "$c" && ! is_action "$c" || continue
|
component_available "$c" && ! is_action "$c" || continue
|
||||||
if [[ "$c" == boot ]]; then
|
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
|
elif [[ -n "${PARENT[$c]:-}" ]]; then
|
||||||
[[ "${CURRENT[${PARENT[$c]}]}" == 1 ]] || continue
|
[[ "${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
|
if [[ "${CURRENT[$c]}" == 1 ]]; 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
|
elif [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " off ${LABEL[$c]}"; fi
|
||||||
done
|
done
|
||||||
if [[ ${#FAILED[@]} -gt 0 ]]; then
|
if [[ ${#FAILED[@]} -gt 0 ]]; then
|
||||||
warn "These had problems (see above): ${FAILED[*]}"
|
warn "These had problems (see above): ${FAILED[*]}"
|
||||||
|
|||||||
@@ -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
|
||||||
+4
-11
@@ -38,9 +38,7 @@ Item {
|
|||||||
|
|
||||||
readonly property var status: backend.status || ({})
|
readonly property var status: backend.status || ({})
|
||||||
readonly property var items: status.items || []
|
readonly property var items: status.items || []
|
||||||
// Hidden: Big Picture excludes a silent start.
|
readonly property var rows: items.filter(function (i) { return !i.parent || want[i.parent]; })
|
||||||
function hidden(i) { return i.id === "nvsilent" && want.bigpicture; }
|
|
||||||
readonly property var rows: items.filter(function (i) { return (!i.parent || want[i.parent]) && !hidden(i); })
|
|
||||||
readonly property var bios: status.bios || null
|
readonly property var bios: status.bios || null
|
||||||
|
|
||||||
signal passwordRequested()
|
signal passwordRequested()
|
||||||
@@ -78,14 +76,10 @@ Item {
|
|||||||
w[id] = !w[id];
|
w[id] = !w[id];
|
||||||
// The same rules as the terminal menu (toggle_component).
|
// The same rules as the terminal menu (toggle_component).
|
||||||
var hasGaming = items.some(function (i) { return i.id === "gaming"; });
|
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;
|
||||||
if (id === "single" && w.single && hasGaming) w.gaming = true;
|
if (id === "single" && w.single && hasGaming) w.gaming = true;
|
||||||
if (id === "nvidia") w.bigpicture = w.nvidia;
|
if (id === "nvidia") w.bigpicture = w.nvidia;
|
||||||
if (id === "bigpicture" && w.bigpicture) { w.nvidia = true; w.nvsilent = false; }
|
if (id === "bigpicture" && w.bigpicture) w.nvidia = true;
|
||||||
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 === "machine") w.poweroff = w.machine;
|
||||||
if (id === "poweroff" && w.poweroff) w.machine = true;
|
if (id === "poweroff" && w.poweroff) w.machine = true;
|
||||||
if (id === "machine" && !w.machine) w.kpin = false;
|
if (id === "machine" && !w.machine) w.kpin = false;
|
||||||
@@ -99,7 +93,6 @@ Item {
|
|||||||
for (var i = 0; i < items.length; i++) {
|
for (var i = 0; i < items.length; i++) {
|
||||||
var it = items[i];
|
var it = items[i];
|
||||||
if (it.kind !== "toggle") continue;
|
if (it.kind !== "toggle") continue;
|
||||||
if (hidden(it)) { if (it.on) p.push({ id: it.id, action: "off" }); continue; }
|
|
||||||
if (it.parent && !want[it.parent]) { if (it.on) p.push({ id: it.id, action: "off" }); continue; }
|
if (it.parent && !want[it.parent]) { if (it.on) p.push({ id: it.id, action: "off" }); continue; }
|
||||||
if (want[it.id] && !it.on) p.push({ id: it.id, action: "on", isNew: !!it["new"] });
|
if (want[it.id] && !it.on) p.push({ id: it.id, action: "on", isNew: !!it["new"] });
|
||||||
else if (!want[it.id] && it.on) p.push({ id: it.id, action: "off" });
|
else if (!want[it.id] && it.on) p.push({ id: it.id, action: "off" });
|
||||||
@@ -116,7 +109,7 @@ Item {
|
|||||||
function wantedIds() {
|
function wantedIds() {
|
||||||
var ids = [];
|
var ids = [];
|
||||||
for (var i = 0; i < items.length; i++)
|
for (var i = 0; i < items.length; i++)
|
||||||
if (items[i].kind === "toggle" && want[items[i].id] && !hidden(items[i]) && (!items[i].parent || want[items[i].parent])) ids.push(items[i].id);
|
if (items[i].kind === "toggle" && want[items[i].id] && (!items[i].parent || want[items[i].parent])) ids.push(items[i].id);
|
||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-7
@@ -6,22 +6,16 @@ QtObject {
|
|||||||
readonly property var items: ({
|
readonly property var items: ({
|
||||||
gaming: { label: "SteamOS conversion", hint: "Boot into gaming mode, Steam on the desktop",
|
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.",
|
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",
|
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.",
|
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"] },
|
changes: ["Desktop: the Plasma session is set before login", "Gaming: the SteamOS default"] },
|
||||||
silent: { label: "Start Steam silently in desktop mode", hint: "In the tray at login on the desktop, no window",
|
|
||||||
body: "Steam starts by itself when you log in to the Plasma desktop, in the tray without a window. Gaming mode starts its own Steam.",
|
|
||||||
changes: ["Steam is started with -silent (a systemd user service, desktop only)", "Off: the service is removed again"] },
|
|
||||||
nvidia: { label: "Gaming on NVIDIA", hint: "Steam on the desktop, started at login",
|
nvidia: { label: "Gaming on NVIDIA", hint: "Steam on the desktop, started at login",
|
||||||
body: "gamescope's own gaming mode shows a corrupted picture on NVIDIA graphics cards (an open NVIDIA driver bug), so it isn't offered here. This puts Steam on the Plasma desktop instead and starts it when you log in.",
|
body: "gamescope's own gaming mode shows a corrupted picture on NVIDIA graphics cards (an open NVIDIA driver bug), so it isn't offered here. This puts Steam on the Plasma desktop instead and starts it when you log in.",
|
||||||
changes: ["Steam is installed when it's missing", "Steam starts at login (a systemd user service)", "Off: the service is removed again; Steam is only removed if Steamify installed it"] },
|
changes: ["Steam is installed when it's missing", "Steam starts at login (a systemd user service)", "Off: the service is removed again; Steam is only removed if Steamify installed it"] },
|
||||||
bigpicture: { label: "Steam starts in Big Picture", hint: "The controller-friendly Steam, or the normal window",
|
bigpicture: { label: "Steam starts in Big Picture", hint: "The controller-friendly Steam, or the normal window",
|
||||||
body: "Steam opens in Big Picture at login, which works well with a controller on the TV. Untick it for Steam's normal window.",
|
body: "Steam opens in Big Picture at login, which works well with a controller on the TV. Untick it for Steam's normal window.",
|
||||||
changes: ["Steam is started with -gamepadui", "Plasma starts with an empty session, so old windows don't cover Big Picture", "Off: Steam starts in its normal window"] },
|
changes: ["Steam is started with -gamepadui", "Plasma starts with an empty session, so old windows don't cover Big Picture", "Off: Steam starts in its normal window"] },
|
||||||
nvsilent: { label: "Start Steam silently", hint: "In the tray at login, no window",
|
|
||||||
body: "Steam starts in the tray at login without a window. Not together with Big Picture: ticking one unticks the other.",
|
|
||||||
changes: ["Steam is started with -silent", "Off: Steam starts in its normal window"] },
|
|
||||||
theme: { label: "SteamOS theme", hint: "Vapor look for the desktop, Add to Steam",
|
theme: { label: "SteamOS theme", hint: "Vapor look for the desktop, Add to Steam",
|
||||||
body: "The Vapor look of SteamOS for the desktop, with its panel, launcher icon and wallpaper.",
|
body: "The Vapor look of SteamOS for the desktop, with its panel, launcher icon and wallpaper.",
|
||||||
changes: ["cachyos-vapor global theme", "Add to Steam in right-click menus", "Nested Desktop, Steam keyboard window rule"] },
|
changes: ["cachyos-vapor global theme", "Add to Steam in right-click menus", "Nested Desktop, Steam keyboard window rule"] },
|
||||||
|
|||||||
Reference in new issue
Block a user